reth_transaction_pool/pool/
state.rs1bitflags::bitflags! {
2 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
14 pub(crate) struct TxState: u8 {
15 const NO_PARKED_ANCESTORS = 0b10000000;
17 const NO_NONCE_GAPS = 0b01000000;
19 const ENOUGH_BALANCE = 0b00100000;
24 const NOT_TOO_MUCH_GAS = 0b00010000;
26 const ENOUGH_FEE_CAP_BLOCK = 0b00001000;
30 const ENOUGH_BLOB_FEE_CAP_BLOCK = 0b00000100;
34 const BLOB_TRANSACTION = 0b00000010;
38
39 const PENDING_POOL_BITS = Self::NO_PARKED_ANCESTORS.bits() | Self::NO_NONCE_GAPS.bits() | Self::ENOUGH_BALANCE.bits() | Self::NOT_TOO_MUCH_GAS.bits() | Self::ENOUGH_FEE_CAP_BLOCK.bits() | Self::ENOUGH_BLOB_FEE_CAP_BLOCK.bits();
40
41 const BASE_FEE_POOL_BITS = Self::NO_PARKED_ANCESTORS.bits() | Self::NO_NONCE_GAPS.bits() | Self::ENOUGH_BALANCE.bits() | Self::NOT_TOO_MUCH_GAS.bits();
42
43 const QUEUED_POOL_BITS = Self::NO_PARKED_ANCESTORS.bits();
44
45 const BLOB_POOL_BITS = Self::BLOB_TRANSACTION.bits();
46 }
47}
48
49impl TxState {
50 #[inline]
56 pub(crate) const fn is_pending(&self) -> bool {
57 self.bits() >= Self::PENDING_POOL_BITS.bits()
58 }
59
60 #[inline]
62 pub(crate) const fn is_blob(&self) -> bool {
63 self.contains(Self::BLOB_TRANSACTION)
64 }
65
66 #[inline]
68 pub(crate) const fn has_nonce_gap(&self) -> bool {
69 !self.intersects(Self::NO_NONCE_GAPS)
70 }
71}
72
73#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
75#[repr(u8)]
76pub enum SubPool {
77 Queued = 0,
81 BaseFee,
84 Blob,
86 Pending,
88}
89
90impl SubPool {
91 #[inline]
93 pub const fn is_pending(&self) -> bool {
94 matches!(self, Self::Pending)
95 }
96
97 #[inline]
99 pub const fn is_queued(&self) -> bool {
100 matches!(self, Self::Queued)
101 }
102
103 #[inline]
105 pub const fn is_base_fee(&self) -> bool {
106 matches!(self, Self::BaseFee)
107 }
108
109 #[inline]
111 pub const fn is_blob(&self) -> bool {
112 matches!(self, Self::Blob)
113 }
114
115 #[inline]
117 pub fn is_promoted(&self, other: Self) -> bool {
118 self > &other
119 }
120}
121
122impl From<TxState> for SubPool {
123 fn from(value: TxState) -> Self {
124 if value.is_pending() {
125 Self::Pending
126 } else if value.is_blob() {
127 Self::Blob
129 } else if value.bits() < TxState::BASE_FEE_POOL_BITS.bits() {
130 Self::Queued
131 } else {
132 Self::BaseFee
133 }
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn test_promoted() {
143 assert!(SubPool::BaseFee.is_promoted(SubPool::Queued));
144 assert!(SubPool::Pending.is_promoted(SubPool::BaseFee));
145 assert!(SubPool::Pending.is_promoted(SubPool::Queued));
146 assert!(SubPool::Pending.is_promoted(SubPool::Blob));
147 assert!(!SubPool::BaseFee.is_promoted(SubPool::Pending));
148 assert!(!SubPool::Queued.is_promoted(SubPool::BaseFee));
149 }
150
151 #[test]
152 fn test_tx_state() {
153 let mut state = TxState::default();
154 state |= TxState::NO_NONCE_GAPS;
155 assert!(state.intersects(TxState::NO_NONCE_GAPS))
156 }
157
158 #[test]
159 fn test_tx_queued() {
160 let state = TxState::default();
161 assert_eq!(SubPool::Queued, state.into());
162
163 let state = TxState::NO_PARKED_ANCESTORS |
164 TxState::NO_NONCE_GAPS |
165 TxState::NOT_TOO_MUCH_GAS |
166 TxState::ENOUGH_FEE_CAP_BLOCK;
167 assert_eq!(SubPool::Queued, state.into());
168 }
169
170 #[test]
171 fn test_tx_pending() {
172 let state = TxState::PENDING_POOL_BITS;
173 assert_eq!(SubPool::Pending, state.into());
174 assert!(state.is_pending());
175
176 let bits = 0b11111100;
177 let state = TxState::from_bits(bits).unwrap();
178 assert_eq!(SubPool::Pending, state.into());
179 assert!(state.is_pending());
180
181 let bits = 0b11111110;
182 let state = TxState::from_bits(bits).unwrap();
183 assert_eq!(SubPool::Pending, state.into());
184 assert!(state.is_pending());
185 }
186
187 #[test]
188 fn test_blob() {
189 let mut state = TxState::PENDING_POOL_BITS;
190 state.insert(TxState::BLOB_TRANSACTION);
191 assert!(state.is_pending());
192
193 state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
194 assert!(state.is_blob());
195 assert!(!state.is_pending());
196
197 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
198 state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
199 assert!(state.is_blob());
200 assert!(!state.is_pending());
201 }
202
203 #[test]
204 fn test_tx_state_no_nonce_gap() {
205 let mut state = TxState::default();
206 state |= TxState::NO_NONCE_GAPS;
207 assert!(!state.has_nonce_gap());
208 }
209
210 #[test]
211 fn test_tx_state_with_nonce_gap() {
212 let state = TxState::default();
213 assert!(state.has_nonce_gap());
214 }
215
216 #[test]
217 fn test_tx_state_enough_balance() {
218 let mut state = TxState::default();
219 state.insert(TxState::ENOUGH_BALANCE);
220 assert!(state.contains(TxState::ENOUGH_BALANCE));
221 }
222
223 #[test]
224 fn test_tx_state_not_too_much_gas() {
225 let mut state = TxState::default();
226 state.insert(TxState::NOT_TOO_MUCH_GAS);
227 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
228 }
229
230 #[test]
231 fn test_tx_state_enough_fee_cap_block() {
232 let mut state = TxState::default();
233 state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
234 assert!(state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
235 }
236
237 #[test]
238 fn test_tx_base_fee() {
239 let state = TxState::BASE_FEE_POOL_BITS;
240 assert_eq!(SubPool::BaseFee, state.into());
241 }
242
243 #[test]
244 fn test_blob_transaction_only() {
245 let state = TxState::BLOB_TRANSACTION;
246 assert_eq!(SubPool::Blob, state.into());
247 assert!(state.is_blob());
248 assert!(!state.is_pending());
249 }
250
251 #[test]
252 fn test_blob_transaction_with_base_fee_bits() {
253 let mut state = TxState::BASE_FEE_POOL_BITS;
254 state.insert(TxState::BLOB_TRANSACTION);
255 assert_eq!(SubPool::Blob, state.into());
256 assert!(state.is_blob());
257 assert!(!state.is_pending());
258 }
259}