reth_transaction_pool/pool/
txpool.rs

1//! The internal transaction pool implementation.
2
3use crate::{
4    config::{LocalTransactionConfig, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER},
5    error::{
6        Eip4844PoolTransactionError, Eip7702PoolTransactionError, InvalidPoolTransactionError,
7        PoolError, PoolErrorKind,
8    },
9    identifier::{SenderId, TransactionId},
10    metrics::{AllTransactionsMetrics, TxPoolMetrics},
11    pool::{
12        best::BestTransactions,
13        blob::BlobTransactions,
14        parked::{BasefeeOrd, ParkedPool, QueuedOrd},
15        pending::PendingPool,
16        state::{SubPool, TxState},
17        update::{Destination, PoolUpdate, UpdateOutcome},
18        AddedPendingTransaction, AddedTransaction, OnNewCanonicalStateOutcome,
19    },
20    traits::{BestTransactionsAttributes, BlockInfo, PoolSize},
21    PoolConfig, PoolResult, PoolTransaction, PoolUpdateKind, PriceBumpConfig, TransactionOrdering,
22    ValidPoolTransaction, U256,
23};
24use alloy_consensus::constants::{
25    EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID, KECCAK_EMPTY,
26    LEGACY_TX_TYPE_ID,
27};
28use alloy_eips::{
29    eip1559::{ETHEREUM_BLOCK_GAS_LIMIT_30M, MIN_PROTOCOL_BASE_FEE},
30    eip4844::BLOB_TX_MIN_BLOB_GASPRICE,
31    Typed2718,
32};
33use alloy_primitives::{Address, TxHash, B256};
34use rustc_hash::FxHashMap;
35use smallvec::SmallVec;
36use std::{
37    cmp::Ordering,
38    collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet},
39    fmt,
40    ops::Bound::{Excluded, Unbounded},
41    sync::Arc,
42};
43use tracing::trace;
44
45#[cfg_attr(doc, aquamarine::aquamarine)]
46// TODO: Inlined diagram due to a bug in aquamarine library, should become an include when it's
47// fixed. See https://github.com/mersinvald/aquamarine/issues/50
48// include_mmd!("docs/mermaid/txpool.mmd")
49/// A pool that manages transactions.
50///
51/// This pool maintains the state of all transactions and stores them accordingly.
52///
53/// ```mermaid
54/// graph TB
55///   subgraph TxPool
56///     direction TB
57///     pool[(All Transactions)]
58///     subgraph Subpools
59///         direction TB
60///         B3[(Queued)]
61///         B1[(Pending)]
62///         B2[(Basefee)]
63///         B4[(Blob)]
64///     end
65///   end
66///   discard([discard])
67///   production([Block Production])
68///   new([New Block])
69///   A[Incoming Tx] --> B[Validation] -->|ins
70///   pool --> |if ready + blobfee too low| B4
71///   pool --> |if ready| B1
72///   pool --> |if ready + basfee too low| B2
73///   pool --> |nonce gap or lack of funds| B3
74///   pool --> |update| pool
75///   B1 --> |best| production
76///   B2 --> |worst| discard
77///   B3 --> |worst| discard
78///   B4 --> |worst| discard
79///   B1 --> |increased blob fee| B4
80///   B4 --> |decreased blob fee| B1
81///   B1 --> |increased base fee| B2
82///   B2 --> |decreased base fee| B1
83///   B3 --> |promote| B1
84///   B3 --> |promote| B2
85///   new --> |apply state changes| pool
86/// ```
87pub struct TxPool<T: TransactionOrdering> {
88    /// Contains the currently known information about the senders.
89    sender_info: FxHashMap<SenderId, SenderInfo>,
90    /// pending subpool
91    ///
92    /// Holds transactions that are ready to be executed on the current state.
93    pending_pool: PendingPool<T>,
94    /// Pool settings to enforce limits etc.
95    config: PoolConfig,
96    /// queued subpool
97    ///
98    /// Holds all parked transactions that depend on external changes from the sender:
99    ///
100    ///    - blocked by missing ancestor transaction (has nonce gaps)
101    ///    - sender lacks funds to pay for this transaction.
102    queued_pool: ParkedPool<QueuedOrd<T::Transaction>>,
103    /// base fee subpool
104    ///
105    /// Holds all parked transactions that currently violate the dynamic fee requirement but could
106    /// be moved to pending if the base fee changes in their favor (decreases) in future blocks.
107    basefee_pool: ParkedPool<BasefeeOrd<T::Transaction>>,
108    /// Blob transactions in the pool that are __not pending__.
109    ///
110    /// This means they either do not satisfy the dynamic fee requirement or the blob fee
111    /// requirement. These transactions can be moved to pending if the base fee or blob fee changes
112    /// in their favor (decreases) in future blocks. The transaction may need both the base fee and
113    /// blob fee to decrease to become executable.
114    blob_pool: BlobTransactions<T::Transaction>,
115    /// All transactions in the pool.
116    all_transactions: AllTransactions<T::Transaction>,
117    /// Transaction pool metrics
118    metrics: TxPoolMetrics,
119    /// The last update kind that was applied to the pool.
120    latest_update_kind: Option<PoolUpdateKind>,
121}
122
123// === impl TxPool ===
124
125impl<T: TransactionOrdering> TxPool<T> {
126    /// Create a new graph pool instance.
127    pub fn new(ordering: T, config: PoolConfig) -> Self {
128        Self {
129            sender_info: Default::default(),
130            pending_pool: PendingPool::with_buffer(
131                ordering,
132                config.max_new_pending_txs_notifications,
133            ),
134            queued_pool: Default::default(),
135            basefee_pool: Default::default(),
136            blob_pool: Default::default(),
137            all_transactions: AllTransactions::new(&config),
138            config,
139            metrics: Default::default(),
140            latest_update_kind: None,
141        }
142    }
143
144    /// Retrieves the highest nonce for a specific sender from the transaction pool.
145    pub fn get_highest_nonce_by_sender(&self, sender: SenderId) -> Option<u64> {
146        self.all().txs_iter(sender).last().map(|(_, tx)| tx.transaction.nonce())
147    }
148
149    /// Retrieves the highest transaction (wrapped in an `Arc`) for a specific sender from the
150    /// transaction pool.
151    pub fn get_highest_transaction_by_sender(
152        &self,
153        sender: SenderId,
154    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
155        self.all().txs_iter(sender).last().map(|(_, tx)| Arc::clone(&tx.transaction))
156    }
157
158    /// Returns the transaction with the highest nonce that is executable given the on chain nonce.
159    ///
160    /// If the pool already tracks a higher nonce for the given sender, then this nonce is used
161    /// instead.
162    ///
163    /// Note: The next pending pooled transaction must have the on chain nonce.
164    pub(crate) fn get_highest_consecutive_transaction_by_sender(
165        &self,
166        mut on_chain: TransactionId,
167    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
168        let mut last_consecutive_tx = None;
169
170        // ensure this operates on the most recent
171        if let Some(current) = self.sender_info.get(&on_chain.sender) {
172            on_chain.nonce = on_chain.nonce.max(current.state_nonce);
173        }
174
175        let mut next_expected_nonce = on_chain.nonce;
176        for (id, tx) in self.all().descendant_txs_inclusive(&on_chain) {
177            if next_expected_nonce != id.nonce {
178                break
179            }
180            next_expected_nonce = id.next_nonce();
181            last_consecutive_tx = Some(tx);
182        }
183
184        last_consecutive_tx.map(|tx| Arc::clone(&tx.transaction))
185    }
186
187    /// Returns access to the [`AllTransactions`] container.
188    pub(crate) const fn all(&self) -> &AllTransactions<T::Transaction> {
189        &self.all_transactions
190    }
191
192    /// Returns all senders in the pool
193    pub(crate) fn unique_senders(&self) -> HashSet<Address> {
194        self.all_transactions.txs.values().map(|tx| tx.transaction.sender()).collect()
195    }
196
197    /// Returns stats about the size of pool.
198    pub fn size(&self) -> PoolSize {
199        PoolSize {
200            pending: self.pending_pool.len(),
201            pending_size: self.pending_pool.size(),
202            basefee: self.basefee_pool.len(),
203            basefee_size: self.basefee_pool.size(),
204            queued: self.queued_pool.len(),
205            queued_size: self.queued_pool.size(),
206            blob: self.blob_pool.len(),
207            blob_size: self.blob_pool.size(),
208            total: self.all_transactions.len(),
209        }
210    }
211
212    /// Returns the currently tracked block values
213    pub const fn block_info(&self) -> BlockInfo {
214        BlockInfo {
215            block_gas_limit: self.all_transactions.block_gas_limit,
216            last_seen_block_hash: self.all_transactions.last_seen_block_hash,
217            last_seen_block_number: self.all_transactions.last_seen_block_number,
218            pending_basefee: self.all_transactions.pending_fees.base_fee,
219            pending_blob_fee: Some(self.all_transactions.pending_fees.blob_fee),
220        }
221    }
222
223    /// Updates the tracked blob fee
224    fn update_blob_fee(&mut self, mut pending_blob_fee: u128, base_fee_update: Ordering) {
225        std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut pending_blob_fee);
226        match (self.all_transactions.pending_fees.blob_fee.cmp(&pending_blob_fee), base_fee_update)
227        {
228            (Ordering::Equal, Ordering::Equal | Ordering::Greater) => {
229                // fee unchanged, nothing to update
230            }
231            (Ordering::Greater, Ordering::Equal | Ordering::Greater) => {
232                // increased blob fee: recheck pending pool and remove all that are no longer valid
233                let removed =
234                    self.pending_pool.update_blob_fee(self.all_transactions.pending_fees.blob_fee);
235                for tx in removed {
236                    let to = {
237                        let tx =
238                            self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
239
240                        // the blob fee is too high now, unset the blob fee cap block flag
241                        tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
242                        tx.subpool = tx.state.into();
243                        tx.subpool
244                    };
245                    self.add_transaction_to_subpool(to, tx);
246                }
247            }
248            (Ordering::Less, _) | (_, Ordering::Less) => {
249                // decreased blob/base fee: recheck blob pool and promote all that are now valid
250                let removed =
251                    self.blob_pool.enforce_pending_fees(&self.all_transactions.pending_fees);
252                for tx in removed {
253                    let to = {
254                        let tx =
255                            self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
256                        tx.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
257                        tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
258                        tx.subpool = tx.state.into();
259                        tx.subpool
260                    };
261                    self.add_transaction_to_subpool(to, tx);
262                }
263            }
264        }
265    }
266
267    /// Updates the tracked basefee
268    ///
269    /// Depending on the change in direction of the basefee, this will promote or demote
270    /// transactions from the basefee pool.
271    fn update_basefee(&mut self, mut pending_basefee: u64) -> Ordering {
272        std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut pending_basefee);
273        match self.all_transactions.pending_fees.base_fee.cmp(&pending_basefee) {
274            Ordering::Equal => {
275                // fee unchanged, nothing to update
276                Ordering::Equal
277            }
278            Ordering::Greater => {
279                // increased base fee: recheck pending pool and remove all that are no longer valid
280                let removed =
281                    self.pending_pool.update_base_fee(self.all_transactions.pending_fees.base_fee);
282                for tx in removed {
283                    let to = {
284                        let tx =
285                            self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
286                        tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
287                        tx.subpool = tx.state.into();
288                        tx.subpool
289                    };
290                    self.add_transaction_to_subpool(to, tx);
291                }
292
293                Ordering::Greater
294            }
295            Ordering::Less => {
296                // decreased base fee: recheck basefee pool and promote all that are now valid
297                let removed =
298                    self.basefee_pool.enforce_basefee(self.all_transactions.pending_fees.base_fee);
299                for tx in removed {
300                    let to = {
301                        let tx =
302                            self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
303                        tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
304                        tx.subpool = tx.state.into();
305                        tx.subpool
306                    };
307                    self.add_transaction_to_subpool(to, tx);
308                }
309
310                Ordering::Less
311            }
312        }
313    }
314
315    /// Sets the current block info for the pool.
316    ///
317    /// This will also apply updates to the pool based on the new base fee
318    pub fn set_block_info(&mut self, info: BlockInfo) {
319        let BlockInfo {
320            block_gas_limit,
321            last_seen_block_hash,
322            last_seen_block_number,
323            pending_basefee,
324            pending_blob_fee,
325        } = info;
326        self.all_transactions.last_seen_block_hash = last_seen_block_hash;
327        self.all_transactions.last_seen_block_number = last_seen_block_number;
328        let basefee_ordering = self.update_basefee(pending_basefee);
329
330        self.all_transactions.block_gas_limit = block_gas_limit;
331
332        if let Some(blob_fee) = pending_blob_fee {
333            self.update_blob_fee(blob_fee, basefee_ordering)
334        }
335    }
336
337    /// Returns an iterator that yields transactions that are ready to be included in the block with
338    /// the tracked fees.
339    pub(crate) fn best_transactions(&self) -> BestTransactions<T> {
340        self.pending_pool.best()
341    }
342
343    /// Returns an iterator that yields transactions that are ready to be included in the block with
344    /// the given base fee and optional blob fee.
345    ///
346    /// If the provided attributes differ from the currently tracked fees, this will also include
347    /// transactions that are unlocked by the new fees, or exclude transactions that are no longer
348    /// valid with the new fees.
349    pub(crate) fn best_transactions_with_attributes(
350        &self,
351        best_transactions_attributes: BestTransactionsAttributes,
352    ) -> Box<dyn crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T::Transaction>>>>
353    {
354        // First we need to check if the given base fee is different than what's currently being
355        // tracked
356        match best_transactions_attributes.basefee.cmp(&self.all_transactions.pending_fees.base_fee)
357        {
358            Ordering::Equal => {
359                // for EIP-4844 transactions we also need to check if the blob fee is now lower than
360                // what's currently being tracked, if so we need to include transactions from the
361                // blob pool that are valid with the lower blob fee
362                let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
363                match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
364                    Ordering::Less => {
365                        // it's possible that this swing unlocked more blob transactions
366                        let unlocked =
367                            self.blob_pool.satisfy_attributes(best_transactions_attributes);
368                        Box::new(self.pending_pool.best_with_unlocked_and_attributes(
369                            unlocked,
370                            best_transactions_attributes.basefee,
371                            new_blob_fee,
372                        ))
373                    }
374                    Ordering::Equal => Box::new(self.pending_pool.best()),
375                    Ordering::Greater => {
376                        // no additional transactions unlocked
377                        Box::new(self.pending_pool.best_with_basefee_and_blobfee(
378                            best_transactions_attributes.basefee,
379                            best_transactions_attributes.blob_fee.unwrap_or_default(),
380                        ))
381                    }
382                }
383            }
384            Ordering::Greater => {
385                // base fee increased, we need to check how the blob fee moved
386                let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
387                match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
388                    Ordering::Less => {
389                        // it's possible that this swing unlocked more blob transactions
390                        let unlocked =
391                            self.blob_pool.satisfy_attributes(best_transactions_attributes);
392                        Box::new(self.pending_pool.best_with_unlocked_and_attributes(
393                            unlocked,
394                            best_transactions_attributes.basefee,
395                            new_blob_fee,
396                        ))
397                    }
398                    Ordering::Equal | Ordering::Greater => {
399                        // no additional transactions unlocked
400                        Box::new(self.pending_pool.best_with_basefee_and_blobfee(
401                            best_transactions_attributes.basefee,
402                            new_blob_fee,
403                        ))
404                    }
405                }
406            }
407            Ordering::Less => {
408                // base fee decreased, we need to move transactions from the basefee + blob pool to
409                // the pending pool that might be unlocked by the lower base fee
410                let mut unlocked = self
411                    .basefee_pool
412                    .satisfy_base_fee_transactions(best_transactions_attributes.basefee);
413
414                // also include blob pool transactions that are now unlocked
415                unlocked.extend(self.blob_pool.satisfy_attributes(best_transactions_attributes));
416
417                Box::new(self.pending_pool.best_with_unlocked_and_attributes(
418                    unlocked,
419                    best_transactions_attributes.basefee,
420                    best_transactions_attributes.blob_fee.unwrap_or_default(),
421                ))
422            }
423        }
424    }
425
426    /// Returns all transactions from the pending sub-pool
427    pub(crate) fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
428        self.pending_pool.all().collect()
429    }
430    /// Returns an iterator over all transactions from the pending sub-pool
431    pub(crate) fn pending_transactions_iter(
432        &self,
433    ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
434        self.pending_pool.all()
435    }
436
437    /// Returns all pending transactions filtered by predicate
438    pub(crate) fn pending_transactions_with_predicate(
439        &self,
440        mut predicate: impl FnMut(&ValidPoolTransaction<T::Transaction>) -> bool,
441    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
442        self.pending_transactions_iter().filter(|tx| predicate(tx)).collect()
443    }
444
445    /// Returns all pending transactions for the specified sender
446    pub(crate) fn pending_txs_by_sender(
447        &self,
448        sender: SenderId,
449    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
450        self.pending_transactions_iter().filter(|tx| tx.sender_id() == sender).collect()
451    }
452
453    /// Returns all transactions from parked pools
454    pub(crate) fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
455        self.basefee_pool.all().chain(self.queued_pool.all()).collect()
456    }
457
458    /// Returns an iterator over all transactions from parked pools
459    pub(crate) fn queued_transactions_iter(
460        &self,
461    ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
462        self.basefee_pool.all().chain(self.queued_pool.all())
463    }
464
465    /// Returns queued and pending transactions for the specified sender
466    pub fn queued_and_pending_txs_by_sender(
467        &self,
468        sender: SenderId,
469    ) -> (SmallVec<[TransactionId; TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER]>, Vec<TransactionId>) {
470        (self.queued_pool.get_txs_by_sender(sender), self.pending_pool.get_txs_by_sender(sender))
471    }
472
473    /// Returns all queued transactions for the specified sender
474    pub(crate) fn queued_txs_by_sender(
475        &self,
476        sender: SenderId,
477    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
478        self.queued_transactions_iter().filter(|tx| tx.sender_id() == sender).collect()
479    }
480
481    /// Returns `true` if the transaction with the given hash is already included in this pool.
482    pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
483        self.all_transactions.contains(tx_hash)
484    }
485
486    /// Returns `true` if the transaction with the given id is already included in the given subpool
487    #[cfg(test)]
488    pub(crate) fn subpool_contains(&self, subpool: SubPool, id: &TransactionId) -> bool {
489        match subpool {
490            SubPool::Queued => self.queued_pool.contains(id),
491            SubPool::Pending => self.pending_pool.contains(id),
492            SubPool::BaseFee => self.basefee_pool.contains(id),
493            SubPool::Blob => self.blob_pool.contains(id),
494        }
495    }
496
497    /// Returns `true` if the pool is over its configured limits.
498    #[inline]
499    pub(crate) fn is_exceeded(&self) -> bool {
500        self.config.is_exceeded(self.size())
501    }
502
503    /// Returns the transaction for the given hash.
504    pub(crate) fn get(
505        &self,
506        tx_hash: &TxHash,
507    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
508        self.all_transactions.by_hash.get(tx_hash).cloned()
509    }
510
511    /// Returns transactions for the multiple given hashes, if they exist.
512    pub(crate) fn get_all(
513        &self,
514        txs: Vec<TxHash>,
515    ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
516        txs.into_iter().filter_map(|tx| self.get(&tx))
517    }
518
519    /// Returns all transactions sent from the given sender.
520    pub(crate) fn get_transactions_by_sender(
521        &self,
522        sender: SenderId,
523    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
524        self.all_transactions.txs_iter(sender).map(|(_, tx)| Arc::clone(&tx.transaction)).collect()
525    }
526
527    /// Updates the transactions for the changed senders.
528    pub(crate) fn update_accounts(
529        &mut self,
530        changed_senders: FxHashMap<SenderId, SenderInfo>,
531    ) -> UpdateOutcome<T::Transaction> {
532        // Apply the state changes to the total set of transactions which triggers sub-pool updates.
533        let updates = self.all_transactions.update(&changed_senders);
534
535        // track changed accounts
536        self.sender_info.extend(changed_senders);
537
538        // Process the sub-pool updates
539        let update = self.process_updates(updates);
540        // update the metrics after the update
541        self.update_size_metrics();
542        update
543    }
544
545    /// Updates the entire pool after a new block was mined.
546    ///
547    /// This removes all mined transactions, updates according to the new base fee and rechecks
548    /// sender allowance.
549    pub(crate) fn on_canonical_state_change(
550        &mut self,
551        block_info: BlockInfo,
552        mined_transactions: Vec<TxHash>,
553        changed_senders: FxHashMap<SenderId, SenderInfo>,
554        update_kind: PoolUpdateKind,
555    ) -> OnNewCanonicalStateOutcome<T::Transaction> {
556        // update block info
557        let block_hash = block_info.last_seen_block_hash;
558        self.all_transactions.set_block_info(block_info);
559
560        // Remove all transaction that were included in the block
561        let mut removed_txs_count = 0;
562        for tx_hash in &mined_transactions {
563            if self.prune_transaction_by_hash(tx_hash).is_some() {
564                removed_txs_count += 1;
565            }
566        }
567
568        // Update removed transactions metric
569        self.metrics.removed_transactions.increment(removed_txs_count);
570
571        let UpdateOutcome { promoted, discarded } = self.update_accounts(changed_senders);
572
573        self.update_transaction_type_metrics();
574        self.metrics.performed_state_updates.increment(1);
575
576        // Update the latest update kind
577        self.latest_update_kind = Some(update_kind);
578
579        OnNewCanonicalStateOutcome { block_hash, mined: mined_transactions, promoted, discarded }
580    }
581
582    /// Update sub-pools size metrics.
583    pub(crate) fn update_size_metrics(&self) {
584        let stats = self.size();
585        self.metrics.pending_pool_transactions.set(stats.pending as f64);
586        self.metrics.pending_pool_size_bytes.set(stats.pending_size as f64);
587        self.metrics.basefee_pool_transactions.set(stats.basefee as f64);
588        self.metrics.basefee_pool_size_bytes.set(stats.basefee_size as f64);
589        self.metrics.queued_pool_transactions.set(stats.queued as f64);
590        self.metrics.queued_pool_size_bytes.set(stats.queued_size as f64);
591        self.metrics.blob_pool_transactions.set(stats.blob as f64);
592        self.metrics.blob_pool_size_bytes.set(stats.blob_size as f64);
593        self.metrics.total_transactions.set(stats.total as f64);
594    }
595
596    /// Updates transaction type metrics for the entire pool.
597    pub(crate) fn update_transaction_type_metrics(&self) {
598        let mut legacy_count = 0;
599        let mut eip2930_count = 0;
600        let mut eip1559_count = 0;
601        let mut eip4844_count = 0;
602        let mut eip7702_count = 0;
603
604        for tx in self.all_transactions.transactions_iter() {
605            match tx.transaction.ty() {
606                LEGACY_TX_TYPE_ID => legacy_count += 1,
607                EIP2930_TX_TYPE_ID => eip2930_count += 1,
608                EIP1559_TX_TYPE_ID => eip1559_count += 1,
609                EIP4844_TX_TYPE_ID => eip4844_count += 1,
610                EIP7702_TX_TYPE_ID => eip7702_count += 1,
611                _ => {} // Ignore other types
612            }
613        }
614
615        self.metrics.total_legacy_transactions.set(legacy_count as f64);
616        self.metrics.total_eip2930_transactions.set(eip2930_count as f64);
617        self.metrics.total_eip1559_transactions.set(eip1559_count as f64);
618        self.metrics.total_eip4844_transactions.set(eip4844_count as f64);
619        self.metrics.total_eip7702_transactions.set(eip7702_count as f64);
620    }
621
622    /// Adds the transaction into the pool.
623    ///
624    /// This pool consists of four sub-pools: `Queued`, `Pending`, `BaseFee`, and `Blob`.
625    ///
626    /// The `Queued` pool contains transactions with gaps in its dependency tree: It requires
627    /// additional transactions that are note yet present in the pool. And transactions that the
628    /// sender can not afford with the current balance.
629    ///
630    /// The `Pending` pool contains all transactions that have no nonce gaps, and can be afforded by
631    /// the sender. It only contains transactions that are ready to be included in the pending
632    /// block. The pending pool contains all transactions that could be listed currently, but not
633    /// necessarily independently. However, this pool never contains transactions with nonce gaps. A
634    /// transaction is considered `ready` when it has the lowest nonce of all transactions from the
635    /// same sender. Which is equals to the chain nonce of the sender in the pending pool.
636    ///
637    /// The `BaseFee` pool contains transactions that currently can't satisfy the dynamic fee
638    /// requirement. With EIP-1559, transactions can become executable or not without any changes to
639    /// the sender's balance or nonce and instead their `feeCap` determines whether the
640    /// transaction is _currently_ (on the current state) ready or needs to be parked until the
641    /// `feeCap` satisfies the block's `baseFee`.
642    ///
643    /// The `Blob` pool contains _blob_ transactions that currently can't satisfy the dynamic fee
644    /// requirement, or blob fee requirement. Transactions become executable only if the
645    /// transaction `feeCap` is greater than the block's `baseFee` and the `maxBlobFee` is greater
646    /// than the block's `blobFee`.
647    pub(crate) fn add_transaction(
648        &mut self,
649        tx: ValidPoolTransaction<T::Transaction>,
650        on_chain_balance: U256,
651        on_chain_nonce: u64,
652        on_chain_code_hash: Option<B256>,
653    ) -> PoolResult<AddedTransaction<T::Transaction>> {
654        if self.contains(tx.hash()) {
655            return Err(PoolError::new(*tx.hash(), PoolErrorKind::AlreadyImported))
656        }
657
658        self.validate_auth(&tx, on_chain_nonce, on_chain_code_hash)?;
659
660        // Update sender info with balance and nonce
661        self.sender_info
662            .entry(tx.sender_id())
663            .or_default()
664            .update(on_chain_nonce, on_chain_balance);
665
666        match self.all_transactions.insert_tx(tx, on_chain_balance, on_chain_nonce) {
667            Ok(InsertOk { transaction, move_to, replaced_tx, updates, .. }) => {
668                // replace the new tx and remove the replaced in the subpool(s)
669                self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
670                // Update inserted transactions metric
671                self.metrics.inserted_transactions.increment(1);
672                let UpdateOutcome { promoted, discarded } = self.process_updates(updates);
673
674                let replaced = replaced_tx.map(|(tx, _)| tx);
675
676                // This transaction was moved to the pending pool.
677                let res = if move_to.is_pending() {
678                    AddedTransaction::Pending(AddedPendingTransaction {
679                        transaction,
680                        promoted,
681                        discarded,
682                        replaced,
683                    })
684                } else {
685                    AddedTransaction::Parked { transaction, subpool: move_to, replaced }
686                };
687
688                // Update size metrics after adding and potentially moving transactions.
689                self.update_size_metrics();
690
691                Ok(res)
692            }
693            Err(err) => {
694                // Update invalid transactions metric
695                self.metrics.invalid_transactions.increment(1);
696                match err {
697                    InsertErr::Underpriced { existing: _, transaction } => Err(PoolError::new(
698                        *transaction.hash(),
699                        PoolErrorKind::ReplacementUnderpriced,
700                    )),
701                    InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap } => {
702                        Err(PoolError::new(
703                            *transaction.hash(),
704                            PoolErrorKind::FeeCapBelowMinimumProtocolFeeCap(fee_cap),
705                        ))
706                    }
707                    InsertErr::ExceededSenderTransactionsCapacity { transaction } => {
708                        Err(PoolError::new(
709                            *transaction.hash(),
710                            PoolErrorKind::SpammerExceededCapacity(transaction.sender()),
711                        ))
712                    }
713                    InsertErr::TxGasLimitMoreThanAvailableBlockGas {
714                        transaction,
715                        block_gas_limit,
716                        tx_gas_limit,
717                    } => Err(PoolError::new(
718                        *transaction.hash(),
719                        PoolErrorKind::InvalidTransaction(
720                            InvalidPoolTransactionError::ExceedsGasLimit(
721                                tx_gas_limit,
722                                block_gas_limit,
723                            ),
724                        ),
725                    )),
726                    InsertErr::BlobTxHasNonceGap { transaction } => Err(PoolError::new(
727                        *transaction.hash(),
728                        PoolErrorKind::InvalidTransaction(
729                            Eip4844PoolTransactionError::Eip4844NonceGap.into(),
730                        ),
731                    )),
732                    InsertErr::Overdraft { transaction } => Err(PoolError::new(
733                        *transaction.hash(),
734                        PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Overdraft {
735                            cost: *transaction.cost(),
736                            balance: on_chain_balance,
737                        }),
738                    )),
739                    InsertErr::TxTypeConflict { transaction } => Err(PoolError::new(
740                        *transaction.hash(),
741                        PoolErrorKind::ExistingConflictingTransactionType(
742                            transaction.sender(),
743                            transaction.tx_type(),
744                        ),
745                    )),
746                }
747            }
748        }
749    }
750
751    /// Determines if the tx sender is delegated or has a  pending delegation, and if so, ensures
752    /// they have at most one in-flight **executable** transaction, e.g. disallow stacked and
753    /// nonce-gapped transactions from the account.
754    fn check_delegation_limit(
755        &self,
756        transaction: &ValidPoolTransaction<T::Transaction>,
757        on_chain_nonce: u64,
758        on_chain_code_hash: Option<B256>,
759    ) -> Result<(), PoolError> {
760        // Short circuit if the sender has neither delegation nor pending delegation.
761        if (on_chain_code_hash.is_none() || on_chain_code_hash == Some(KECCAK_EMPTY)) &&
762            !self.all_transactions.auths.contains_key(&transaction.sender_id())
763        {
764            return Ok(())
765        }
766
767        let mut txs_by_sender =
768            self.pending_pool.iter_txs_by_sender(transaction.sender_id()).peekable();
769
770        if txs_by_sender.peek().is_none() {
771            // Transaction with gapped nonce is not supported for delegated accounts
772            if transaction.nonce() > on_chain_nonce {
773                return Err(PoolError::new(
774                    *transaction.hash(),
775                    PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
776                        Eip7702PoolTransactionError::OutOfOrderTxFromDelegated,
777                    )),
778                ))
779            }
780            return Ok(())
781        }
782
783        if txs_by_sender.any(|id| id == &transaction.transaction_id) {
784            // Transaction replacement is supported
785            return Ok(())
786        }
787
788        Err(PoolError::new(
789            *transaction.hash(),
790            PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
791                Eip7702PoolTransactionError::InflightTxLimitReached,
792            )),
793        ))
794    }
795
796    /// This verifies that the transaction complies with code authorization
797    /// restrictions brought by EIP-7702 transaction type:
798    /// 1. Any account with a deployed delegation or an in-flight authorization to deploy a
799    ///    delegation will only be allowed a single transaction slot instead of the standard limit.
800    ///    This is due to the possibility of the account being sweeped by an unrelated account.
801    /// 2. In case the pool is tracking a pending / queued transaction from a specific account, it
802    ///    will reject new transactions with delegations from that account with standard in-flight
803    ///    transactions.
804    fn validate_auth(
805        &self,
806        transaction: &ValidPoolTransaction<T::Transaction>,
807        on_chain_nonce: u64,
808        on_chain_code_hash: Option<B256>,
809    ) -> Result<(), PoolError> {
810        // Allow at most one in-flight tx for delegated accounts or those with a
811        // pending authorization.
812        self.check_delegation_limit(transaction, on_chain_nonce, on_chain_code_hash)?;
813
814        if let Some(authority_list) = &transaction.authority_ids {
815            for sender_id in authority_list {
816                if self.all_transactions.txs_iter(*sender_id).next().is_some() {
817                    return Err(PoolError::new(
818                        *transaction.hash(),
819                        PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
820                            Eip7702PoolTransactionError::AuthorityReserved,
821                        )),
822                    ))
823                }
824            }
825        }
826
827        Ok(())
828    }
829
830    /// Maintenance task to apply a series of updates.
831    ///
832    /// This will move/discard the given transaction according to the `PoolUpdate`
833    fn process_updates(&mut self, updates: Vec<PoolUpdate>) -> UpdateOutcome<T::Transaction> {
834        let mut outcome = UpdateOutcome::default();
835        for PoolUpdate { id, hash, current, destination } in updates {
836            match destination {
837                Destination::Discard => {
838                    // remove the transaction from the pool and subpool
839                    if let Some(tx) = self.prune_transaction_by_hash(&hash) {
840                        outcome.discarded.push(tx);
841                    }
842                    self.metrics.removed_transactions.increment(1);
843                }
844                Destination::Pool(move_to) => {
845                    debug_assert_ne!(&move_to, &current, "destination must be different");
846                    let moved = self.move_transaction(current, move_to, &id);
847                    if matches!(move_to, SubPool::Pending) {
848                        if let Some(tx) = moved {
849                            trace!(target: "txpool", hash=%tx.transaction.hash(), "Promoted transaction to pending");
850                            outcome.promoted.push(tx);
851                        }
852                    }
853                }
854            }
855        }
856        outcome
857    }
858
859    /// Moves a transaction from one sub pool to another.
860    ///
861    /// This will remove the given transaction from one sub-pool and insert it into the other
862    /// sub-pool.
863    fn move_transaction(
864        &mut self,
865        from: SubPool,
866        to: SubPool,
867        id: &TransactionId,
868    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
869        let tx = self.remove_from_subpool(from, id)?;
870        self.add_transaction_to_subpool(to, tx.clone());
871        Some(tx)
872    }
873
874    /// Removes and returns all matching transactions from the pool.
875    ///
876    /// Note: this does not advance any descendants of the removed transactions and does not apply
877    /// any additional updates.
878    pub(crate) fn remove_transactions(
879        &mut self,
880        hashes: Vec<TxHash>,
881    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
882        let txs =
883            hashes.into_iter().filter_map(|hash| self.remove_transaction_by_hash(&hash)).collect();
884        self.update_size_metrics();
885        txs
886    }
887
888    /// Removes and returns all matching transactions and their descendants from the pool.
889    pub(crate) fn remove_transactions_and_descendants(
890        &mut self,
891        hashes: Vec<TxHash>,
892    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
893        let mut removed = Vec::new();
894        for hash in hashes {
895            if let Some(tx) = self.remove_transaction_by_hash(&hash) {
896                removed.push(tx.clone());
897                self.remove_descendants(tx.id(), &mut removed);
898            }
899        }
900        self.update_size_metrics();
901        removed
902    }
903
904    /// Removes all transactions from the given sender.
905    pub(crate) fn remove_transactions_by_sender(
906        &mut self,
907        sender_id: SenderId,
908    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
909        let mut removed = Vec::new();
910        let txs = self.get_transactions_by_sender(sender_id);
911        for tx in txs {
912            if let Some(tx) = self.remove_transaction(tx.id()) {
913                removed.push(tx);
914            }
915        }
916        self.update_size_metrics();
917        removed
918    }
919
920    /// Remove the transaction from the __entire__ pool.
921    ///
922    /// This includes the total set of transaction and the subpool it currently resides in.
923    fn remove_transaction(
924        &mut self,
925        id: &TransactionId,
926    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
927        let (tx, pool) = self.all_transactions.remove_transaction(id)?;
928        self.remove_from_subpool(pool, tx.id())
929    }
930
931    /// Remove the transaction from the entire pool via its hash. This includes the total set of
932    /// transactions and the subpool it currently resides in.
933    ///
934    /// This treats the descendants as if this transaction is discarded and removing the transaction
935    /// reduces a nonce gap.
936    fn remove_transaction_by_hash(
937        &mut self,
938        tx_hash: &B256,
939    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
940        let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
941
942        // After a tx is removed, its descendants must become parked due to the nonce gap
943        let updates = self.all_transactions.park_descendant_transactions(tx.id());
944        self.process_updates(updates);
945        self.remove_from_subpool(pool, tx.id())
946    }
947
948    /// This removes the transaction from the pool and advances any descendant state inside the
949    /// subpool.
950    ///
951    /// This is intended to be used when a transaction is included in a block,
952    /// [`Self::on_canonical_state_change`]. So its descendants will not change from pending to
953    /// parked, just like what we do in `remove_transaction_by_hash`.
954    fn prune_transaction_by_hash(
955        &mut self,
956        tx_hash: &B256,
957    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
958        let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
959        self.remove_from_subpool(pool, tx.id())
960    }
961
962    /// Removes the transaction from the given pool.
963    ///
964    /// Caution: this only removes the tx from the sub-pool and not from the pool itself
965    fn remove_from_subpool(
966        &mut self,
967        pool: SubPool,
968        tx: &TransactionId,
969    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
970        let tx = match pool {
971            SubPool::Queued => self.queued_pool.remove_transaction(tx),
972            SubPool::Pending => self.pending_pool.remove_transaction(tx),
973            SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
974            SubPool::Blob => self.blob_pool.remove_transaction(tx),
975        };
976
977        if let Some(ref tx) = tx {
978            // We trace here instead of in subpool structs directly, because the `ParkedPool` type
979            // is generic and it would not be possible to distinguish whether a transaction is
980            // being removed from the `BaseFee` pool, or the `Queued` pool.
981            trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Removed transaction from a subpool");
982        }
983
984        tx
985    }
986
987    /// Removes _only_ the descendants of the given transaction from the __entire__ pool.
988    ///
989    /// All removed transactions are added to the `removed` vec.
990    fn remove_descendants(
991        &mut self,
992        tx: &TransactionId,
993        removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
994    ) {
995        let mut id = *tx;
996
997        // this will essentially pop _all_ descendant transactions one by one
998        loop {
999            let descendant =
1000                self.all_transactions.descendant_txs_exclusive(&id).map(|(id, _)| *id).next();
1001            if let Some(descendant) = descendant {
1002                if let Some(tx) = self.remove_transaction(&descendant) {
1003                    removed.push(tx)
1004                }
1005                id = descendant;
1006            } else {
1007                return
1008            }
1009        }
1010    }
1011
1012    /// Inserts the transaction into the given sub-pool.
1013    fn add_transaction_to_subpool(
1014        &mut self,
1015        pool: SubPool,
1016        tx: Arc<ValidPoolTransaction<T::Transaction>>,
1017    ) {
1018        // We trace here instead of in structs directly, because the `ParkedPool` type is
1019        // generic and it would not be possible to distinguish whether a transaction is being
1020        // added to the `BaseFee` pool, or the `Queued` pool.
1021        match pool {
1022            SubPool::Queued => self.queued_pool.add_transaction(tx),
1023            SubPool::Pending => {
1024                self.pending_pool.add_transaction(tx, self.all_transactions.pending_fees.base_fee);
1025            }
1026            SubPool::BaseFee => {
1027                self.basefee_pool.add_transaction(tx);
1028            }
1029            SubPool::Blob => {
1030                self.blob_pool.add_transaction(tx);
1031            }
1032        }
1033    }
1034
1035    /// Inserts the transaction into the given sub-pool.
1036    /// Optionally, removes the replacement transaction.
1037    fn add_new_transaction(
1038        &mut self,
1039        transaction: Arc<ValidPoolTransaction<T::Transaction>>,
1040        replaced: Option<(Arc<ValidPoolTransaction<T::Transaction>>, SubPool)>,
1041        pool: SubPool,
1042    ) {
1043        if let Some((replaced, replaced_pool)) = replaced {
1044            // Remove the replaced transaction
1045            self.remove_from_subpool(replaced_pool, replaced.id());
1046        }
1047
1048        self.add_transaction_to_subpool(pool, transaction)
1049    }
1050
1051    /// Ensures that the transactions in the sub-pools are within the given bounds.
1052    ///
1053    /// If the current size exceeds the given bounds, the worst transactions are evicted from the
1054    /// pool and returned.
1055    ///
1056    /// This returns all transactions that were removed from the entire pool.
1057    pub(crate) fn discard_worst(&mut self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1058        let mut removed = Vec::new();
1059
1060        // Helper macro that discards the worst transactions for the pools
1061        macro_rules! discard_worst {
1062            ($this:ident, $removed:ident, [$($limit:ident => ($pool:ident, $metric:ident)),* $(,)*]) => {
1063                $ (
1064                while $this.$pool.exceeds(&$this.config.$limit)
1065                    {
1066                        trace!(
1067                            target: "txpool",
1068                            "discarding transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1069                            stringify!($pool),
1070                            $this.config.$limit,
1071                            $this.$pool.size(),
1072                            $this.$pool.len(),
1073                        );
1074
1075                        // 1. first remove the worst transaction from the subpool
1076                        let removed_from_subpool = $this.$pool.truncate_pool($this.config.$limit.clone());
1077
1078                        trace!(
1079                            target: "txpool",
1080                            "removed {} transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1081                            removed_from_subpool.len(),
1082                            stringify!($pool),
1083                            $this.config.$limit,
1084                            $this.$pool.size(),
1085                            $this.$pool.len()
1086                        );
1087                        $this.metrics.$metric.increment(removed_from_subpool.len() as u64);
1088
1089                        // 2. remove all transactions from the total set
1090                        for tx in removed_from_subpool {
1091                            $this.all_transactions.remove_transaction(tx.id());
1092
1093                            let id = *tx.id();
1094
1095                            // keep track of removed transaction
1096                            removed.push(tx);
1097
1098                            // 3. remove all its descendants from the entire pool
1099                            $this.remove_descendants(&id, &mut $removed);
1100                        }
1101                    }
1102
1103                )*
1104            };
1105        }
1106
1107        discard_worst!(
1108            self, removed, [
1109                pending_limit => (pending_pool, pending_transactions_evicted),
1110                basefee_limit => (basefee_pool, basefee_transactions_evicted),
1111                blob_limit    => (blob_pool, blob_transactions_evicted),
1112                queued_limit  => (queued_pool, queued_transactions_evicted),
1113            ]
1114        );
1115
1116        removed
1117    }
1118
1119    /// Number of transactions in the entire pool
1120    pub(crate) fn len(&self) -> usize {
1121        self.all_transactions.len()
1122    }
1123
1124    /// Whether the pool is empty
1125    pub(crate) fn is_empty(&self) -> bool {
1126        self.all_transactions.is_empty()
1127    }
1128
1129    /// Asserts all invariants of the  pool's:
1130    ///
1131    ///  - All maps are bijections (`by_id`, `by_hash`)
1132    ///  - Total size is equal to the sum of all sub-pools
1133    ///
1134    /// # Panics
1135    /// if any invariant is violated
1136    #[cfg(any(test, feature = "test-utils"))]
1137    pub fn assert_invariants(&self) {
1138        let size = self.size();
1139        let actual = size.basefee + size.pending + size.queued + size.blob;
1140        assert_eq!(
1141            size.total, actual,
1142            "total size must be equal to the sum of all sub-pools, basefee:{}, pending:{}, queued:{}, blob:{}",
1143            size.basefee, size.pending, size.queued, size.blob
1144        );
1145        self.all_transactions.assert_invariants();
1146        self.pending_pool.assert_invariants();
1147        self.basefee_pool.assert_invariants();
1148        self.queued_pool.assert_invariants();
1149        self.blob_pool.assert_invariants();
1150    }
1151}
1152
1153#[cfg(any(test, feature = "test-utils"))]
1154impl TxPool<crate::test_utils::MockOrdering> {
1155    /// Creates a mock instance for testing.
1156    pub fn mock() -> Self {
1157        Self::new(crate::test_utils::MockOrdering::default(), PoolConfig::default())
1158    }
1159}
1160
1161#[cfg(test)]
1162impl<T: TransactionOrdering> Drop for TxPool<T> {
1163    fn drop(&mut self) {
1164        self.assert_invariants();
1165    }
1166}
1167
1168// Additional test impls
1169#[cfg(any(test, feature = "test-utils"))]
1170impl<T: TransactionOrdering> TxPool<T> {
1171    pub(crate) const fn pending(&self) -> &PendingPool<T> {
1172        &self.pending_pool
1173    }
1174
1175    pub(crate) const fn base_fee(&self) -> &ParkedPool<BasefeeOrd<T::Transaction>> {
1176        &self.basefee_pool
1177    }
1178
1179    pub(crate) const fn queued(&self) -> &ParkedPool<QueuedOrd<T::Transaction>> {
1180        &self.queued_pool
1181    }
1182}
1183
1184impl<T: TransactionOrdering> fmt::Debug for TxPool<T> {
1185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1186        f.debug_struct("TxPool").field("config", &self.config).finish_non_exhaustive()
1187    }
1188}
1189
1190/// Container for _all_ transaction in the pool.
1191///
1192/// This is the sole entrypoint that's guarding all sub-pools, all sub-pool actions are always
1193/// derived from this set. Updates returned from this type must be applied to the sub-pools.
1194pub(crate) struct AllTransactions<T: PoolTransaction> {
1195    /// Minimum base fee required by the protocol.
1196    ///
1197    /// Transactions with a lower base fee will never be included by the chain
1198    minimal_protocol_basefee: u64,
1199    /// The max gas limit of the block
1200    block_gas_limit: u64,
1201    /// Max number of executable transaction slots guaranteed per account
1202    max_account_slots: usize,
1203    /// _All_ transactions identified by their hash.
1204    by_hash: HashMap<TxHash, Arc<ValidPoolTransaction<T>>>,
1205    /// _All_ transaction in the pool sorted by their sender and nonce pair.
1206    txs: BTreeMap<TransactionId, PoolInternalTransaction<T>>,
1207    /// Tracks the number of transactions by sender that are currently in the pool.
1208    tx_counter: FxHashMap<SenderId, usize>,
1209    /// The current block number the pool keeps track of.
1210    last_seen_block_number: u64,
1211    /// The current block hash the pool keeps track of.
1212    last_seen_block_hash: B256,
1213    /// Expected blob and base fee for the pending block.
1214    pending_fees: PendingFees,
1215    /// Configured price bump settings for replacements
1216    price_bumps: PriceBumpConfig,
1217    /// How to handle [`TransactionOrigin::Local`](crate::TransactionOrigin) transactions.
1218    local_transactions_config: LocalTransactionConfig,
1219    /// All accounts with a pooled authorization
1220    auths: FxHashMap<SenderId, HashSet<TxHash>>,
1221    /// All Transactions metrics
1222    metrics: AllTransactionsMetrics,
1223}
1224
1225impl<T: PoolTransaction> AllTransactions<T> {
1226    /// Create a new instance
1227    fn new(config: &PoolConfig) -> Self {
1228        Self {
1229            max_account_slots: config.max_account_slots,
1230            price_bumps: config.price_bumps,
1231            local_transactions_config: config.local_transactions_config.clone(),
1232            minimal_protocol_basefee: config.minimal_protocol_basefee,
1233            block_gas_limit: config.gas_limit,
1234            ..Default::default()
1235        }
1236    }
1237
1238    /// Returns an iterator over all _unique_ hashes in the pool
1239    #[expect(dead_code)]
1240    pub(crate) fn hashes_iter(&self) -> impl Iterator<Item = TxHash> + '_ {
1241        self.by_hash.keys().copied()
1242    }
1243
1244    /// Returns an iterator over all transactions in the pool
1245    pub(crate) fn transactions_iter(
1246        &self,
1247    ) -> impl Iterator<Item = &Arc<ValidPoolTransaction<T>>> + '_ {
1248        self.by_hash.values()
1249    }
1250
1251    /// Returns if the transaction for the given hash is already included in this pool
1252    pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
1253        self.by_hash.contains_key(tx_hash)
1254    }
1255
1256    /// Returns the internal transaction with additional metadata
1257    pub(crate) fn get(&self, id: &TransactionId) -> Option<&PoolInternalTransaction<T>> {
1258        self.txs.get(id)
1259    }
1260
1261    /// Increments the transaction counter for the sender
1262    pub(crate) fn tx_inc(&mut self, sender: SenderId) {
1263        let count = self.tx_counter.entry(sender).or_default();
1264        *count += 1;
1265        self.metrics.all_transactions_by_all_senders.increment(1.0);
1266    }
1267
1268    /// Decrements the transaction counter for the sender
1269    pub(crate) fn tx_decr(&mut self, sender: SenderId) {
1270        if let hash_map::Entry::Occupied(mut entry) = self.tx_counter.entry(sender) {
1271            let count = entry.get_mut();
1272            if *count == 1 {
1273                entry.remove();
1274                self.metrics.all_transactions_by_all_senders.decrement(1.0);
1275                return
1276            }
1277            *count -= 1;
1278            self.metrics.all_transactions_by_all_senders.decrement(1.0);
1279        }
1280    }
1281
1282    /// Updates the block specific info
1283    fn set_block_info(&mut self, block_info: BlockInfo) {
1284        let BlockInfo {
1285            block_gas_limit,
1286            last_seen_block_hash,
1287            last_seen_block_number,
1288            pending_basefee,
1289            pending_blob_fee,
1290        } = block_info;
1291        self.last_seen_block_number = last_seen_block_number;
1292        self.last_seen_block_hash = last_seen_block_hash;
1293
1294        self.pending_fees.base_fee = pending_basefee;
1295        self.metrics.base_fee.set(pending_basefee as f64);
1296
1297        self.block_gas_limit = block_gas_limit;
1298
1299        if let Some(pending_blob_fee) = pending_blob_fee {
1300            self.pending_fees.blob_fee = pending_blob_fee;
1301            self.metrics.blob_base_fee.set(pending_blob_fee as f64);
1302        }
1303    }
1304
1305    /// Updates the size metrics
1306    pub(crate) fn update_size_metrics(&self) {
1307        self.metrics.all_transactions_by_hash.set(self.by_hash.len() as f64);
1308        self.metrics.all_transactions_by_id.set(self.txs.len() as f64);
1309    }
1310
1311    /// Rechecks all transactions in the pool against the changes.
1312    ///
1313    /// Possible changes are:
1314    ///
1315    /// For all transactions:
1316    ///   - decreased basefee: promotes from `basefee` to `pending` sub-pool.
1317    ///   - increased basefee: demotes from `pending` to `basefee` sub-pool.
1318    ///
1319    /// Individually:
1320    ///   - decreased sender allowance: demote from (`basefee`|`pending`) to `queued`.
1321    ///   - increased sender allowance: promote from `queued` to
1322    ///       - `pending` if basefee condition is met.
1323    ///       - `basefee` if basefee condition is _not_ met.
1324    ///
1325    /// Additionally, this will also update the `cumulative_gas_used` for transactions of a sender
1326    /// that got transaction included in the block.
1327    pub(crate) fn update(
1328        &mut self,
1329        changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1330    ) -> Vec<PoolUpdate> {
1331        // pre-allocate a few updates
1332        let mut updates = Vec::with_capacity(64);
1333
1334        let mut iter = self.txs.iter_mut().peekable();
1335
1336        // Loop over all individual senders and update all affected transactions.
1337        // One sender may have up to `max_account_slots` transactions here, which means, worst case
1338        // `max_accounts_slots` need to be updated, for example if the first transaction is blocked
1339        // due to too low base fee.
1340        // However, we don't have to necessarily check every transaction of a sender. If no updates
1341        // are possible (nonce gap) then we can skip to the next sender.
1342
1343        // The `unique_sender` loop will process the first transaction of all senders, update its
1344        // state and internally update all consecutive transactions
1345        'transactions: while let Some((id, tx)) = iter.next() {
1346            macro_rules! next_sender {
1347                ($iter:ident) => {
1348                    'this: while let Some((peek, _)) = iter.peek() {
1349                        if peek.sender != id.sender {
1350                            break 'this
1351                        }
1352                        iter.next();
1353                    }
1354                };
1355            }
1356            // tracks the balance if the sender was changed in the block
1357            let mut changed_balance = None;
1358
1359            // check if this is a changed account
1360            if let Some(info) = changed_accounts.get(&id.sender) {
1361                // discard all transactions with a nonce lower than the current state nonce
1362                if id.nonce < info.state_nonce {
1363                    updates.push(PoolUpdate {
1364                        id: *tx.transaction.id(),
1365                        hash: *tx.transaction.hash(),
1366                        current: tx.subpool,
1367                        destination: Destination::Discard,
1368                    });
1369                    continue 'transactions
1370                }
1371
1372                let ancestor = TransactionId::ancestor(id.nonce, info.state_nonce, id.sender);
1373                // If there's no ancestor then this is the next transaction.
1374                if ancestor.is_none() {
1375                    tx.state.insert(TxState::NO_NONCE_GAPS);
1376                    tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1377                    tx.cumulative_cost = U256::ZERO;
1378                    if tx.transaction.cost() > &info.balance {
1379                        // sender lacks sufficient funds to pay for this transaction
1380                        tx.state.remove(TxState::ENOUGH_BALANCE);
1381                    } else {
1382                        tx.state.insert(TxState::ENOUGH_BALANCE);
1383                    }
1384                }
1385
1386                changed_balance = Some(&info.balance);
1387            }
1388
1389            // If there's a nonce gap, we can shortcircuit, because there's nothing to update yet.
1390            if tx.state.has_nonce_gap() {
1391                next_sender!(iter);
1392                continue 'transactions
1393            }
1394
1395            // Since this is the first transaction of the sender, it has no parked ancestors
1396            tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1397
1398            // Update the first transaction of this sender.
1399            Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1400            // Track if the transaction's sub-pool changed.
1401            Self::record_subpool_update(&mut updates, tx);
1402
1403            // Track blocking transactions.
1404            let mut has_parked_ancestor = !tx.state.is_pending();
1405
1406            let mut cumulative_cost = tx.next_cumulative_cost();
1407
1408            // the next expected nonce after this transaction: nonce + 1
1409            let mut next_nonce_in_line = tx.transaction.nonce().saturating_add(1);
1410
1411            // Update all consecutive transaction of this sender
1412            while let Some((peek, tx)) = iter.peek_mut() {
1413                if peek.sender != id.sender {
1414                    // Found the next sender we need to check
1415                    continue 'transactions
1416                }
1417
1418                if tx.transaction.nonce() == next_nonce_in_line {
1419                    // no longer nonce gapped
1420                    tx.state.insert(TxState::NO_NONCE_GAPS);
1421                } else {
1422                    // can short circuit if there's still a nonce gap
1423                    next_sender!(iter);
1424                    continue 'transactions
1425                }
1426
1427                // update for next iteration of this sender's loop
1428                next_nonce_in_line = next_nonce_in_line.saturating_add(1);
1429
1430                // update cumulative cost
1431                tx.cumulative_cost = cumulative_cost;
1432                // Update for next transaction
1433                cumulative_cost = tx.next_cumulative_cost();
1434
1435                // If the account changed in the block, check the balance.
1436                if let Some(changed_balance) = changed_balance {
1437                    if &cumulative_cost > changed_balance {
1438                        // sender lacks sufficient funds to pay for this transaction
1439                        tx.state.remove(TxState::ENOUGH_BALANCE);
1440                    } else {
1441                        tx.state.insert(TxState::ENOUGH_BALANCE);
1442                    }
1443                }
1444
1445                // Update ancestor condition.
1446                if has_parked_ancestor {
1447                    tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1448                } else {
1449                    tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1450                }
1451                has_parked_ancestor = !tx.state.is_pending();
1452
1453                // Update and record sub-pool changes.
1454                Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1455                Self::record_subpool_update(&mut updates, tx);
1456
1457                // Advance iterator
1458                iter.next();
1459            }
1460        }
1461
1462        updates
1463    }
1464
1465    /// This will update the transaction's `subpool` based on its state.
1466    ///
1467    /// If the sub-pool derived from the state differs from the current pool, it will record a
1468    /// `PoolUpdate` for this transaction to move it to the new sub-pool.
1469    fn record_subpool_update(updates: &mut Vec<PoolUpdate>, tx: &mut PoolInternalTransaction<T>) {
1470        let current_pool = tx.subpool;
1471        tx.subpool = tx.state.into();
1472        if current_pool != tx.subpool {
1473            updates.push(PoolUpdate {
1474                id: *tx.transaction.id(),
1475                hash: *tx.transaction.hash(),
1476                current: current_pool,
1477                destination: tx.subpool.into(),
1478            })
1479        }
1480    }
1481
1482    /// Rechecks the transaction's dynamic fee condition.
1483    fn update_tx_base_fee(pending_block_base_fee: u64, tx: &mut PoolInternalTransaction<T>) {
1484        // Recheck dynamic fee condition.
1485        match tx.transaction.max_fee_per_gas().cmp(&(pending_block_base_fee as u128)) {
1486            Ordering::Greater | Ordering::Equal => {
1487                tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1488            }
1489            Ordering::Less => {
1490                tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
1491            }
1492        }
1493    }
1494
1495    /// Returns an iterator over all transactions for the given sender, starting with the lowest
1496    /// nonce
1497    pub(crate) fn txs_iter(
1498        &self,
1499        sender: SenderId,
1500    ) -> impl Iterator<Item = (&TransactionId, &PoolInternalTransaction<T>)> + '_ {
1501        self.txs
1502            .range((sender.start_bound(), Unbounded))
1503            .take_while(move |(other, _)| sender == other.sender)
1504    }
1505
1506    /// Returns a mutable iterator over all transactions for the given sender, starting with the
1507    /// lowest nonce
1508    #[cfg(test)]
1509    #[expect(dead_code)]
1510    pub(crate) fn txs_iter_mut(
1511        &mut self,
1512        sender: SenderId,
1513    ) -> impl Iterator<Item = (&TransactionId, &mut PoolInternalTransaction<T>)> + '_ {
1514        self.txs
1515            .range_mut((sender.start_bound(), Unbounded))
1516            .take_while(move |(other, _)| sender == other.sender)
1517    }
1518
1519    /// Returns all transactions that _follow_ after the given id and have the same sender.
1520    ///
1521    /// NOTE: The range is _exclusive_
1522    pub(crate) fn descendant_txs_exclusive<'a, 'b: 'a>(
1523        &'a self,
1524        id: &'b TransactionId,
1525    ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1526        self.txs.range((Excluded(id), Unbounded)).take_while(|(other, _)| id.sender == other.sender)
1527    }
1528
1529    /// Returns all transactions that _follow_ after the given id but have the same sender.
1530    ///
1531    /// NOTE: The range is _inclusive_: if the transaction that belongs to `id` it will be the
1532    /// first value.
1533    pub(crate) fn descendant_txs_inclusive<'a, 'b: 'a>(
1534        &'a self,
1535        id: &'b TransactionId,
1536    ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1537        self.txs.range(id..).take_while(|(other, _)| id.sender == other.sender)
1538    }
1539
1540    /// Returns all mutable transactions that _follow_ after the given id but have the same sender.
1541    ///
1542    /// NOTE: The range is _inclusive_: if the transaction that belongs to `id` it field be the
1543    /// first value.
1544    pub(crate) fn descendant_txs_mut<'a, 'b: 'a>(
1545        &'a mut self,
1546        id: &'b TransactionId,
1547    ) -> impl Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)> + 'a {
1548        self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender)
1549    }
1550
1551    /// Removes a transaction from the set using its hash.
1552    pub(crate) fn remove_transaction_by_hash(
1553        &mut self,
1554        tx_hash: &B256,
1555    ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1556        let tx = self.by_hash.remove(tx_hash)?;
1557        let internal = self.txs.remove(&tx.transaction_id)?;
1558        self.remove_auths(&internal);
1559        // decrement the counter for the sender.
1560        self.tx_decr(tx.sender_id());
1561        self.update_size_metrics();
1562        Some((tx, internal.subpool))
1563    }
1564
1565    /// If a tx is removed (_not_ mined), all descendants are set to parked due to the nonce gap
1566    pub(crate) fn park_descendant_transactions(
1567        &mut self,
1568        tx_id: &TransactionId,
1569    ) -> Vec<PoolUpdate> {
1570        let mut updates = Vec::new();
1571
1572        for (id, tx) in self.descendant_txs_mut(tx_id) {
1573            let current_pool = tx.subpool;
1574
1575            tx.state.remove(TxState::NO_NONCE_GAPS);
1576
1577            // update the pool based on the state.
1578            tx.subpool = tx.state.into();
1579
1580            // check if anything changed.
1581            if current_pool != tx.subpool {
1582                updates.push(PoolUpdate {
1583                    id: *id,
1584                    hash: *tx.transaction.hash(),
1585                    current: current_pool,
1586                    destination: tx.subpool.into(),
1587                })
1588            }
1589        }
1590
1591        updates
1592    }
1593
1594    /// Removes a transaction from the set.
1595    ///
1596    /// This will _not_ trigger additional updates, because descendants without nonce gaps are
1597    /// already in the pending pool, and this transaction will be the first transaction of the
1598    /// sender in this pool.
1599    pub(crate) fn remove_transaction(
1600        &mut self,
1601        id: &TransactionId,
1602    ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1603        let internal = self.txs.remove(id)?;
1604
1605        // decrement the counter for the sender.
1606        self.tx_decr(internal.transaction.sender_id());
1607
1608        let result =
1609            self.by_hash.remove(internal.transaction.hash()).map(|tx| (tx, internal.subpool));
1610
1611        self.remove_auths(&internal);
1612
1613        self.update_size_metrics();
1614
1615        result
1616    }
1617
1618    /// Removes any pending auths for the given transaction.
1619    ///
1620    /// This is a noop for non EIP-7702 transactions.
1621    fn remove_auths(&mut self, tx: &PoolInternalTransaction<T>) {
1622        let Some(auths) = &tx.transaction.authority_ids else { return };
1623
1624        let tx_hash = tx.transaction.hash();
1625        for auth in auths {
1626            if let Some(list) = self.auths.get_mut(auth) {
1627                list.remove(tx_hash);
1628                if list.is_empty() {
1629                    self.auths.remove(auth);
1630                }
1631            }
1632        }
1633    }
1634
1635    /// Checks if the given transaction's type conflicts with an existing transaction.
1636    ///
1637    /// See also [`ValidPoolTransaction::tx_type_conflicts_with`].
1638    ///
1639    /// Caution: This assumes that mutually exclusive invariant is always true for the same sender.
1640    #[inline]
1641    fn contains_conflicting_transaction(&self, tx: &ValidPoolTransaction<T>) -> bool {
1642        self.txs_iter(tx.transaction_id.sender)
1643            .next()
1644            .is_some_and(|(_, existing)| tx.tx_type_conflicts_with(&existing.transaction))
1645    }
1646
1647    /// Additional checks for a new transaction.
1648    ///
1649    /// This will enforce all additional rules in the context of this pool, such as:
1650    ///   - Spam protection: reject new non-local transaction from a sender that exhausted its slot
1651    ///     capacity.
1652    ///   - Gas limit: reject transactions if they exceed a block's maximum gas.
1653    ///   - Ensures transaction types are not conflicting for the sender: blob vs normal
1654    ///     transactions are mutually exclusive for the same sender.
1655    fn ensure_valid(
1656        &self,
1657        transaction: ValidPoolTransaction<T>,
1658        on_chain_nonce: u64,
1659    ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1660        if !self.local_transactions_config.is_local(transaction.origin, transaction.sender_ref()) {
1661            let current_txs =
1662                self.tx_counter.get(&transaction.sender_id()).copied().unwrap_or_default();
1663
1664            // Reject transactions if sender's capacity is exceeded.
1665            // If transaction's nonce matches on-chain nonce always let it through
1666            if current_txs >= self.max_account_slots && transaction.nonce() > on_chain_nonce {
1667                return Err(InsertErr::ExceededSenderTransactionsCapacity {
1668                    transaction: Arc::new(transaction),
1669                })
1670            }
1671        }
1672        if transaction.gas_limit() > self.block_gas_limit {
1673            return Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas {
1674                block_gas_limit: self.block_gas_limit,
1675                tx_gas_limit: transaction.gas_limit(),
1676                transaction: Arc::new(transaction),
1677            })
1678        }
1679
1680        if self.contains_conflicting_transaction(&transaction) {
1681            // blob vs non blob transactions are mutually exclusive for the same sender
1682            return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) })
1683        }
1684
1685        Ok(transaction)
1686    }
1687
1688    /// Enforces additional constraints for blob transactions before attempting to insert:
1689    ///    - new blob transactions must not have any nonce gaps
1690    ///    - blob transactions cannot go into overdraft
1691    ///    - replacement blob transaction with a higher fee must not shift an already propagated
1692    ///      descending blob transaction into overdraft
1693    fn ensure_valid_blob_transaction(
1694        &self,
1695        new_blob_tx: ValidPoolTransaction<T>,
1696        on_chain_balance: U256,
1697        ancestor: Option<TransactionId>,
1698    ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1699        if let Some(ancestor) = ancestor {
1700            let Some(ancestor_tx) = self.txs.get(&ancestor) else {
1701                // ancestor tx is missing, so we can't insert the new blob
1702                self.metrics.blob_transactions_nonce_gaps.increment(1);
1703                return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1704            };
1705            if ancestor_tx.state.has_nonce_gap() {
1706                // the ancestor transaction already has a nonce gap, so we can't insert the new
1707                // blob
1708                self.metrics.blob_transactions_nonce_gaps.increment(1);
1709                return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1710            }
1711
1712            // the max cost executing this transaction requires
1713            let mut cumulative_cost = ancestor_tx.next_cumulative_cost() + new_blob_tx.cost();
1714
1715            // check if the new blob would go into overdraft
1716            if cumulative_cost > on_chain_balance {
1717                // the transaction would go into overdraft
1718                return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1719            }
1720
1721            // ensure that a replacement would not shift already propagated blob transactions into
1722            // overdraft
1723            let id = new_blob_tx.transaction_id;
1724            let mut descendants = self.descendant_txs_inclusive(&id).peekable();
1725            if let Some((maybe_replacement, _)) = descendants.peek() {
1726                if **maybe_replacement == new_blob_tx.transaction_id {
1727                    // replacement transaction
1728                    descendants.next();
1729
1730                    // check if any of descendant blob transactions should be shifted into overdraft
1731                    for (_, tx) in descendants {
1732                        cumulative_cost += tx.transaction.cost();
1733                        if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance {
1734                            // the transaction would shift
1735                            return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1736                        }
1737                    }
1738                }
1739            }
1740        } else if new_blob_tx.cost() > &on_chain_balance {
1741            // the transaction would go into overdraft
1742            return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1743        }
1744
1745        Ok(new_blob_tx)
1746    }
1747
1748    /// Inserts a new _valid_ transaction into the pool.
1749    ///
1750    /// If the transaction already exists, it will be replaced if not underpriced.
1751    /// Returns info to which sub-pool the transaction should be moved.
1752    /// Also returns a set of pool updates triggered by this insert, that need to be handled by the
1753    /// caller.
1754    ///
1755    /// These can include:
1756    ///      - closing nonce gaps of descendant transactions
1757    ///      - enough balance updates
1758    ///
1759    /// Note: For EIP-4844 blob transactions additional constraints are enforced:
1760    ///      - new blob transactions must not have any nonce gaps
1761    ///      - blob transactions cannot go into overdraft
1762    ///
1763    /// ## Transaction type Exclusivity
1764    ///
1765    /// The pool enforces exclusivity of eip-4844 blob vs non-blob transactions on a per sender
1766    /// basis:
1767    ///  - If the pool already includes a blob transaction from the `transaction`'s sender, then the
1768    ///    `transaction` must also be a blob transaction
1769    ///  - If the pool already includes a non-blob transaction from the `transaction`'s sender, then
1770    ///    the `transaction` must _not_ be a blob transaction.
1771    ///
1772    /// In other words, the presence of blob transactions exclude non-blob transactions and vice
1773    /// versa.
1774    ///
1775    /// ## Replacements
1776    ///
1777    /// The replacement candidate must satisfy given price bump constraints: replacement candidate
1778    /// must not be underpriced
1779    pub(crate) fn insert_tx(
1780        &mut self,
1781        transaction: ValidPoolTransaction<T>,
1782        on_chain_balance: U256,
1783        on_chain_nonce: u64,
1784    ) -> InsertResult<T> {
1785        assert!(on_chain_nonce <= transaction.nonce(), "Invalid transaction");
1786
1787        let mut transaction = self.ensure_valid(transaction, on_chain_nonce)?;
1788
1789        let inserted_tx_id = *transaction.id();
1790        let mut state = TxState::default();
1791        let mut cumulative_cost = U256::ZERO;
1792        let mut updates = Vec::new();
1793
1794        // Current tx does not exceed block gas limit after ensure_valid check
1795        state.insert(TxState::NOT_TOO_MUCH_GAS);
1796
1797        // identifier of the ancestor transaction, will be None if the transaction is the next tx of
1798        // the sender
1799        let ancestor = TransactionId::ancestor(
1800            transaction.transaction.nonce(),
1801            on_chain_nonce,
1802            inserted_tx_id.sender,
1803        );
1804
1805        // before attempting to insert a blob transaction, we need to ensure that additional
1806        // constraints are met that only apply to blob transactions
1807        if transaction.is_eip4844() {
1808            state.insert(TxState::BLOB_TRANSACTION);
1809
1810            transaction =
1811                self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
1812            let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();
1813            if blob_fee_cap >= self.pending_fees.blob_fee {
1814                state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1815            }
1816        } else {
1817            // Non-EIP4844 transaction always satisfy the blob fee cap condition
1818            state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1819        }
1820
1821        let transaction = Arc::new(transaction);
1822
1823        // If there's no ancestor tx then this is the next transaction.
1824        if ancestor.is_none() {
1825            state.insert(TxState::NO_NONCE_GAPS);
1826            state.insert(TxState::NO_PARKED_ANCESTORS);
1827        }
1828
1829        // Check dynamic fee
1830        let fee_cap = transaction.max_fee_per_gas();
1831
1832        if fee_cap < self.minimal_protocol_basefee as u128 {
1833            return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap })
1834        }
1835        if fee_cap >= self.pending_fees.base_fee as u128 {
1836            state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1837        }
1838
1839        // placeholder for the replaced transaction, if any
1840        let mut replaced_tx = None;
1841
1842        let pool_tx = PoolInternalTransaction {
1843            transaction: Arc::clone(&transaction),
1844            subpool: state.into(),
1845            state,
1846            cumulative_cost,
1847        };
1848
1849        // try to insert the transaction
1850        match self.txs.entry(*transaction.id()) {
1851            Entry::Vacant(entry) => {
1852                // Insert the transaction in both maps
1853                self.by_hash.insert(*pool_tx.transaction.hash(), pool_tx.transaction.clone());
1854                entry.insert(pool_tx);
1855            }
1856            Entry::Occupied(mut entry) => {
1857                // Transaction with the same nonce already exists: replacement candidate
1858                let existing_transaction = entry.get().transaction.as_ref();
1859                let maybe_replacement = transaction.as_ref();
1860
1861                // Ensure the new transaction is not underpriced
1862                if existing_transaction.is_underpriced(maybe_replacement, &self.price_bumps) {
1863                    return Err(InsertErr::Underpriced {
1864                        transaction: pool_tx.transaction,
1865                        existing: *entry.get().transaction.hash(),
1866                    })
1867                }
1868                let new_hash = *pool_tx.transaction.hash();
1869                let new_transaction = pool_tx.transaction.clone();
1870                let replaced = entry.insert(pool_tx);
1871                self.by_hash.remove(replaced.transaction.hash());
1872                self.by_hash.insert(new_hash, new_transaction);
1873
1874                self.remove_auths(&replaced);
1875
1876                // also remove the hash
1877                replaced_tx = Some((replaced.transaction, replaced.subpool));
1878            }
1879        }
1880
1881        if let Some(auths) = &transaction.authority_ids {
1882            let tx_hash = transaction.hash();
1883            for auth in auths {
1884                self.auths.entry(*auth).or_default().insert(*tx_hash);
1885            }
1886        }
1887
1888        // The next transaction of this sender
1889        let on_chain_id = TransactionId::new(transaction.sender_id(), on_chain_nonce);
1890        {
1891            // Tracks the next nonce we expect if the transactions are gapless
1892            let mut next_nonce = on_chain_id.nonce;
1893
1894            // We need to find out if the next transaction of the sender is considered pending
1895            // The direct descendant has _no_ parked ancestors because the `on_chain_nonce` is
1896            // pending, so we can set this to `false`
1897            let mut has_parked_ancestor = false;
1898
1899            // Traverse all future transactions of the sender starting with the on chain nonce, and
1900            // update existing transactions: `[on_chain_nonce,..]`
1901            for (id, tx) in self.descendant_txs_mut(&on_chain_id) {
1902                let current_pool = tx.subpool;
1903
1904                // If there's a nonce gap, we can shortcircuit
1905                if next_nonce != id.nonce {
1906                    break
1907                }
1908
1909                // close the nonce gap
1910                tx.state.insert(TxState::NO_NONCE_GAPS);
1911
1912                // set cumulative cost
1913                tx.cumulative_cost = cumulative_cost;
1914
1915                // Update for next transaction
1916                cumulative_cost = tx.next_cumulative_cost();
1917
1918                if cumulative_cost > on_chain_balance {
1919                    // sender lacks sufficient funds to pay for this transaction
1920                    tx.state.remove(TxState::ENOUGH_BALANCE);
1921                } else {
1922                    tx.state.insert(TxState::ENOUGH_BALANCE);
1923                }
1924
1925                // Update ancestor condition.
1926                if has_parked_ancestor {
1927                    tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1928                } else {
1929                    tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1930                }
1931                has_parked_ancestor = !tx.state.is_pending();
1932
1933                // update the pool based on the state
1934                tx.subpool = tx.state.into();
1935
1936                if inserted_tx_id.eq(id) {
1937                    // if it is the new transaction, track its updated state
1938                    state = tx.state;
1939                } else {
1940                    // check if anything changed
1941                    if current_pool != tx.subpool {
1942                        updates.push(PoolUpdate {
1943                            id: *id,
1944                            hash: *tx.transaction.hash(),
1945                            current: current_pool,
1946                            destination: tx.subpool.into(),
1947                        })
1948                    }
1949                }
1950
1951                // increment for next iteration
1952                next_nonce = id.next_nonce();
1953            }
1954        }
1955
1956        // If this wasn't a replacement transaction we need to update the counter.
1957        if replaced_tx.is_none() {
1958            self.tx_inc(inserted_tx_id.sender);
1959        }
1960
1961        self.update_size_metrics();
1962
1963        let res = Ok(InsertOk { transaction, move_to: state.into(), state, replaced_tx, updates });
1964        res
1965    }
1966
1967    /// Number of transactions in the entire pool
1968    pub(crate) fn len(&self) -> usize {
1969        self.txs.len()
1970    }
1971
1972    /// Whether the pool is empty
1973    pub(crate) fn is_empty(&self) -> bool {
1974        self.txs.is_empty()
1975    }
1976
1977    /// Asserts that the bijection between `by_hash` and `txs` is valid.
1978    #[cfg(any(test, feature = "test-utils"))]
1979    pub(crate) fn assert_invariants(&self) {
1980        assert_eq!(self.by_hash.len(), self.txs.len(), "by_hash.len() != txs.len()");
1981        assert!(self.auths.len() <= self.txs.len(), "auths > txs.len()");
1982    }
1983}
1984
1985#[cfg(test)]
1986impl<T: PoolTransaction> AllTransactions<T> {
1987    /// This function retrieves the number of transactions stored in the pool for a specific sender.
1988    ///
1989    /// If there are no transactions for the given sender, it returns zero by default.
1990    pub(crate) fn tx_count(&self, sender: SenderId) -> usize {
1991        self.tx_counter.get(&sender).copied().unwrap_or_default()
1992    }
1993}
1994
1995impl<T: PoolTransaction> Default for AllTransactions<T> {
1996    fn default() -> Self {
1997        Self {
1998            max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
1999            minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
2000            block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
2001            by_hash: Default::default(),
2002            txs: Default::default(),
2003            tx_counter: Default::default(),
2004            last_seen_block_number: Default::default(),
2005            last_seen_block_hash: Default::default(),
2006            pending_fees: Default::default(),
2007            price_bumps: Default::default(),
2008            local_transactions_config: Default::default(),
2009            auths: Default::default(),
2010            metrics: Default::default(),
2011        }
2012    }
2013}
2014
2015/// Represents updated fees for the pending block.
2016#[derive(Debug, Clone)]
2017pub(crate) struct PendingFees {
2018    /// The pending base fee
2019    pub(crate) base_fee: u64,
2020    /// The pending blob fee
2021    pub(crate) blob_fee: u128,
2022}
2023
2024impl Default for PendingFees {
2025    fn default() -> Self {
2026        Self { base_fee: Default::default(), blob_fee: BLOB_TX_MIN_BLOB_GASPRICE }
2027    }
2028}
2029
2030/// Result type for inserting a transaction
2031pub(crate) type InsertResult<T> = Result<InsertOk<T>, InsertErr<T>>;
2032
2033/// Err variant of `InsertResult`
2034#[derive(Debug)]
2035pub(crate) enum InsertErr<T: PoolTransaction> {
2036    /// Attempted to replace existing transaction, but was underpriced
2037    Underpriced {
2038        transaction: Arc<ValidPoolTransaction<T>>,
2039        #[expect(dead_code)]
2040        existing: TxHash,
2041    },
2042    /// Attempted to insert a blob transaction with a nonce gap
2043    BlobTxHasNonceGap { transaction: Arc<ValidPoolTransaction<T>> },
2044    /// Attempted to insert a transaction that would overdraft the sender's balance at the time of
2045    /// insertion.
2046    Overdraft { transaction: Arc<ValidPoolTransaction<T>> },
2047    /// The transactions feeCap is lower than the chain's minimum fee requirement.
2048    ///
2049    /// See also [`MIN_PROTOCOL_BASE_FEE`]
2050    FeeCapBelowMinimumProtocolFeeCap { transaction: Arc<ValidPoolTransaction<T>>, fee_cap: u128 },
2051    /// Sender currently exceeds the configured limit for max account slots.
2052    ///
2053    /// The sender can be considered a spammer at this point.
2054    ExceededSenderTransactionsCapacity { transaction: Arc<ValidPoolTransaction<T>> },
2055    /// Transaction gas limit exceeds block's gas limit
2056    TxGasLimitMoreThanAvailableBlockGas {
2057        transaction: Arc<ValidPoolTransaction<T>>,
2058        block_gas_limit: u64,
2059        tx_gas_limit: u64,
2060    },
2061    /// Thrown if the mutual exclusivity constraint (blob vs normal transaction) is violated.
2062    TxTypeConflict { transaction: Arc<ValidPoolTransaction<T>> },
2063}
2064
2065/// Transaction was successfully inserted into the pool
2066#[derive(Debug)]
2067pub(crate) struct InsertOk<T: PoolTransaction> {
2068    /// Ref to the inserted transaction.
2069    transaction: Arc<ValidPoolTransaction<T>>,
2070    /// Where to move the transaction to.
2071    move_to: SubPool,
2072    /// Current state of the inserted tx.
2073    #[cfg_attr(not(test), expect(dead_code))]
2074    state: TxState,
2075    /// The transaction that was replaced by this.
2076    replaced_tx: Option<(Arc<ValidPoolTransaction<T>>, SubPool)>,
2077    /// Additional updates to transactions affected by this change.
2078    updates: Vec<PoolUpdate>,
2079}
2080
2081/// The internal transaction typed used by `AllTransactions` which also additional info used for
2082/// determining the current state of the transaction.
2083#[derive(Debug)]
2084pub(crate) struct PoolInternalTransaction<T: PoolTransaction> {
2085    /// The actual transaction object.
2086    pub(crate) transaction: Arc<ValidPoolTransaction<T>>,
2087    /// The `SubPool` that currently contains this transaction.
2088    pub(crate) subpool: SubPool,
2089    /// Keeps track of the current state of the transaction and therefore in which subpool it
2090    /// should reside
2091    pub(crate) state: TxState,
2092    /// The total cost all transactions before this transaction.
2093    ///
2094    /// This is the combined `cost` of all transactions from the same sender that currently
2095    /// come before this transaction.
2096    pub(crate) cumulative_cost: U256,
2097}
2098
2099// === impl PoolInternalTransaction ===
2100
2101impl<T: PoolTransaction> PoolInternalTransaction<T> {
2102    fn next_cumulative_cost(&self) -> U256 {
2103        self.cumulative_cost + self.transaction.cost()
2104    }
2105}
2106
2107/// Stores relevant context about a sender.
2108#[derive(Debug, Clone, Default)]
2109pub(crate) struct SenderInfo {
2110    /// current nonce of the sender.
2111    pub(crate) state_nonce: u64,
2112    /// Balance of the sender at the current point.
2113    pub(crate) balance: U256,
2114}
2115
2116// === impl SenderInfo ===
2117
2118impl SenderInfo {
2119    /// Updates the info with the new values.
2120    const fn update(&mut self, state_nonce: u64, balance: U256) {
2121        *self = Self { state_nonce, balance };
2122    }
2123}
2124
2125#[cfg(test)]
2126mod tests {
2127    use super::*;
2128    use crate::{
2129        test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
2130        traits::TransactionOrigin,
2131        SubPoolLimit,
2132    };
2133    use alloy_consensus::{Transaction, TxType};
2134    use alloy_primitives::address;
2135
2136    #[test]
2137    fn test_insert_blob() {
2138        let on_chain_balance = U256::MAX;
2139        let on_chain_nonce = 0;
2140        let mut f = MockTransactionFactory::default();
2141        let mut pool = AllTransactions::default();
2142        let tx = MockTransaction::eip4844().inc_price().inc_limit();
2143        let valid_tx = f.validated(tx);
2144        let InsertOk { updates, replaced_tx, move_to, state, .. } =
2145            pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2146        assert!(updates.is_empty());
2147        assert!(replaced_tx.is_none());
2148        assert!(state.contains(TxState::NO_NONCE_GAPS));
2149        assert!(state.contains(TxState::ENOUGH_BALANCE));
2150        assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2151        assert_eq!(move_to, SubPool::Pending);
2152
2153        let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2154        assert_eq!(inserted.subpool, SubPool::Pending);
2155    }
2156
2157    #[test]
2158    fn test_insert_blob_not_enough_blob_fee() {
2159        let on_chain_balance = U256::MAX;
2160        let on_chain_nonce = 0;
2161        let mut f = MockTransactionFactory::default();
2162        let mut pool = AllTransactions {
2163            pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2164            ..Default::default()
2165        };
2166        let tx = MockTransaction::eip4844().inc_price().inc_limit();
2167        pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2168        let valid_tx = f.validated(tx);
2169        let InsertOk { state, .. } =
2170            pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2171        assert!(state.contains(TxState::NO_NONCE_GAPS));
2172        assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2173
2174        let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2175    }
2176
2177    #[test]
2178    fn test_valid_tx_with_decreasing_blob_fee() {
2179        let on_chain_balance = U256::MAX;
2180        let on_chain_nonce = 0;
2181        let mut f = MockTransactionFactory::default();
2182        let mut pool = AllTransactions {
2183            pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2184            ..Default::default()
2185        };
2186        let tx = MockTransaction::eip4844().inc_price().inc_limit();
2187
2188        pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2189        let valid_tx = f.validated(tx.clone());
2190        let InsertOk { state, .. } =
2191            pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2192        assert!(state.contains(TxState::NO_NONCE_GAPS));
2193        assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2194
2195        let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2196        pool.remove_transaction(&valid_tx.transaction_id);
2197
2198        pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
2199        let InsertOk { state, .. } =
2200            pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2201        assert!(state.contains(TxState::NO_NONCE_GAPS));
2202        assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2203    }
2204
2205    #[test]
2206    fn test_demote_valid_tx_with_increasing_blob_fee() {
2207        let on_chain_balance = U256::MAX;
2208        let on_chain_nonce = 0;
2209        let mut f = MockTransactionFactory::default();
2210        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2211        let tx = MockTransaction::eip4844().inc_price().inc_limit();
2212
2213        // set block info so the tx is initially underpriced w.r.t. blob fee
2214        let mut block_info = pool.block_info();
2215        block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2216        pool.set_block_info(block_info);
2217
2218        let validated = f.validated(tx.clone());
2219        let id = *validated.id();
2220        pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2221
2222        // assert pool lengths
2223        assert!(pool.blob_pool.is_empty());
2224        assert_eq!(pool.pending_pool.len(), 1);
2225
2226        // check tx state and derived subpool
2227        let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2228        assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2229        assert_eq!(internal_tx.subpool, SubPool::Pending);
2230
2231        // set block info so the pools are updated
2232        block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2233        pool.set_block_info(block_info);
2234
2235        // check that the tx is promoted
2236        let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2237        assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2238        assert_eq!(internal_tx.subpool, SubPool::Blob);
2239
2240        // make sure the blob transaction was promoted into the pending pool
2241        assert_eq!(pool.blob_pool.len(), 1);
2242        assert!(pool.pending_pool.is_empty());
2243    }
2244
2245    #[test]
2246    fn test_promote_valid_tx_with_decreasing_blob_fee() {
2247        let on_chain_balance = U256::MAX;
2248        let on_chain_nonce = 0;
2249        let mut f = MockTransactionFactory::default();
2250        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2251        let tx = MockTransaction::eip4844().inc_price().inc_limit();
2252
2253        // set block info so the tx is initially underpriced w.r.t. blob fee
2254        let mut block_info = pool.block_info();
2255        block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2256        pool.set_block_info(block_info);
2257
2258        let validated = f.validated(tx.clone());
2259        let id = *validated.id();
2260        pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2261
2262        // assert pool lengths
2263        assert!(pool.pending_pool.is_empty());
2264        assert_eq!(pool.blob_pool.len(), 1);
2265
2266        // check tx state and derived subpool
2267        let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2268        assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2269        assert_eq!(internal_tx.subpool, SubPool::Blob);
2270
2271        // set block info so the pools are updated
2272        block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2273        pool.set_block_info(block_info);
2274
2275        // check that the tx is promoted
2276        let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2277        assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2278        assert_eq!(internal_tx.subpool, SubPool::Pending);
2279
2280        // make sure the blob transaction was promoted into the pending pool
2281        assert_eq!(pool.pending_pool.len(), 1);
2282        assert!(pool.blob_pool.is_empty());
2283    }
2284
2285    /// A struct representing a txpool promotion test instance
2286    #[derive(Debug, PartialEq, Eq, Clone, Hash)]
2287    struct PromotionTest {
2288        /// The basefee at the start of the test
2289        basefee: u64,
2290        /// The blobfee at the start of the test
2291        blobfee: u128,
2292        /// The subpool at the start of the test
2293        subpool: SubPool,
2294        /// The basefee update
2295        basefee_update: u64,
2296        /// The blobfee update
2297        blobfee_update: u128,
2298        /// The subpool after the update
2299        new_subpool: SubPool,
2300    }
2301
2302    impl PromotionTest {
2303        /// Returns the test case for the opposite update
2304        const fn opposite(&self) -> Self {
2305            Self {
2306                basefee: self.basefee_update,
2307                blobfee: self.blobfee_update,
2308                subpool: self.new_subpool,
2309                blobfee_update: self.blobfee,
2310                basefee_update: self.basefee,
2311                new_subpool: self.subpool,
2312            }
2313        }
2314
2315        fn assert_subpool_lengths<T: TransactionOrdering>(
2316            &self,
2317            pool: &TxPool<T>,
2318            failure_message: String,
2319            check_subpool: SubPool,
2320        ) {
2321            match check_subpool {
2322                SubPool::Blob => {
2323                    assert_eq!(pool.blob_pool.len(), 1, "{failure_message}");
2324                    assert!(pool.pending_pool.is_empty(), "{failure_message}");
2325                    assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2326                    assert!(pool.queued_pool.is_empty(), "{failure_message}");
2327                }
2328                SubPool::Pending => {
2329                    assert!(pool.blob_pool.is_empty(), "{failure_message}");
2330                    assert_eq!(pool.pending_pool.len(), 1, "{failure_message}");
2331                    assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2332                    assert!(pool.queued_pool.is_empty(), "{failure_message}");
2333                }
2334                SubPool::BaseFee => {
2335                    assert!(pool.blob_pool.is_empty(), "{failure_message}");
2336                    assert!(pool.pending_pool.is_empty(), "{failure_message}");
2337                    assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}");
2338                    assert!(pool.queued_pool.is_empty(), "{failure_message}");
2339                }
2340                SubPool::Queued => {
2341                    assert!(pool.blob_pool.is_empty(), "{failure_message}");
2342                    assert!(pool.pending_pool.is_empty(), "{failure_message}");
2343                    assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2344                    assert_eq!(pool.queued_pool.len(), 1, "{failure_message}");
2345                }
2346            }
2347        }
2348
2349        /// Runs an assertion on the provided pool, ensuring that the transaction is in the correct
2350        /// subpool based on the starting condition of the test, assuming the pool contains only a
2351        /// single transaction.
2352        fn assert_single_tx_starting_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2353            self.assert_subpool_lengths(
2354                pool,
2355                format!("pool length check failed at start of test: {self:?}"),
2356                self.subpool,
2357            );
2358        }
2359
2360        /// Runs an assertion on the provided pool, ensuring that the transaction is in the correct
2361        /// subpool based on the ending condition of the test, assuming the pool contains only a
2362        /// single transaction.
2363        fn assert_single_tx_ending_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2364            self.assert_subpool_lengths(
2365                pool,
2366                format!("pool length check failed at end of test: {self:?}"),
2367                self.new_subpool,
2368            );
2369        }
2370    }
2371
2372    #[test]
2373    fn test_promote_blob_tx_with_both_pending_fee_updates() {
2374        // this exhaustively tests all possible promotion scenarios for a single transaction moving
2375        // between the blob and pending pool
2376        let on_chain_balance = U256::MAX;
2377        let on_chain_nonce = 0;
2378        let mut f = MockTransactionFactory::default();
2379        let tx = MockTransaction::eip4844().inc_price().inc_limit();
2380
2381        let max_fee_per_blob_gas = tx.max_fee_per_blob_gas().unwrap();
2382        let max_fee_per_gas = tx.max_fee_per_gas() as u64;
2383
2384        // These are all _promotion_ tests or idempotent tests.
2385        let mut expected_promotions = vec![
2386            PromotionTest {
2387                blobfee: max_fee_per_blob_gas + 1,
2388                basefee: max_fee_per_gas + 1,
2389                subpool: SubPool::Blob,
2390                blobfee_update: max_fee_per_blob_gas + 1,
2391                basefee_update: max_fee_per_gas + 1,
2392                new_subpool: SubPool::Blob,
2393            },
2394            PromotionTest {
2395                blobfee: max_fee_per_blob_gas + 1,
2396                basefee: max_fee_per_gas + 1,
2397                subpool: SubPool::Blob,
2398                blobfee_update: max_fee_per_blob_gas,
2399                basefee_update: max_fee_per_gas + 1,
2400                new_subpool: SubPool::Blob,
2401            },
2402            PromotionTest {
2403                blobfee: max_fee_per_blob_gas + 1,
2404                basefee: max_fee_per_gas + 1,
2405                subpool: SubPool::Blob,
2406                blobfee_update: max_fee_per_blob_gas + 1,
2407                basefee_update: max_fee_per_gas,
2408                new_subpool: SubPool::Blob,
2409            },
2410            PromotionTest {
2411                blobfee: max_fee_per_blob_gas + 1,
2412                basefee: max_fee_per_gas + 1,
2413                subpool: SubPool::Blob,
2414                blobfee_update: max_fee_per_blob_gas,
2415                basefee_update: max_fee_per_gas,
2416                new_subpool: SubPool::Pending,
2417            },
2418            PromotionTest {
2419                blobfee: max_fee_per_blob_gas,
2420                basefee: max_fee_per_gas + 1,
2421                subpool: SubPool::Blob,
2422                blobfee_update: max_fee_per_blob_gas,
2423                basefee_update: max_fee_per_gas,
2424                new_subpool: SubPool::Pending,
2425            },
2426            PromotionTest {
2427                blobfee: max_fee_per_blob_gas + 1,
2428                basefee: max_fee_per_gas,
2429                subpool: SubPool::Blob,
2430                blobfee_update: max_fee_per_blob_gas,
2431                basefee_update: max_fee_per_gas,
2432                new_subpool: SubPool::Pending,
2433            },
2434            PromotionTest {
2435                blobfee: max_fee_per_blob_gas,
2436                basefee: max_fee_per_gas,
2437                subpool: SubPool::Pending,
2438                blobfee_update: max_fee_per_blob_gas,
2439                basefee_update: max_fee_per_gas,
2440                new_subpool: SubPool::Pending,
2441            },
2442        ];
2443
2444        // extend the test cases with reversed updates - this will add all _demotion_ tests
2445        let reversed = expected_promotions.iter().map(|test| test.opposite()).collect::<Vec<_>>();
2446        expected_promotions.extend(reversed);
2447
2448        // dedup the test cases
2449        let expected_promotions = expected_promotions.into_iter().collect::<HashSet<_>>();
2450
2451        for promotion_test in &expected_promotions {
2452            let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2453
2454            // set block info so the tx is initially underpriced w.r.t. blob fee
2455            let mut block_info = pool.block_info();
2456
2457            block_info.pending_blob_fee = Some(promotion_test.blobfee);
2458            block_info.pending_basefee = promotion_test.basefee;
2459            pool.set_block_info(block_info);
2460
2461            let validated = f.validated(tx.clone());
2462            let id = *validated.id();
2463            pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2464
2465            // assert pool lengths
2466            promotion_test.assert_single_tx_starting_subpool(&pool);
2467
2468            // check tx state and derived subpool, it should not move into the blob pool
2469            let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2470            assert_eq!(
2471                internal_tx.subpool, promotion_test.subpool,
2472                "Subpools do not match at start of test: {promotion_test:?}"
2473            );
2474
2475            // set block info with new base fee
2476            block_info.pending_basefee = promotion_test.basefee_update;
2477            block_info.pending_blob_fee = Some(promotion_test.blobfee_update);
2478            pool.set_block_info(block_info);
2479
2480            // check tx state and derived subpool, it should not move into the blob pool
2481            let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2482            assert_eq!(
2483                internal_tx.subpool, promotion_test.new_subpool,
2484                "Subpools do not match at end of test: {promotion_test:?}"
2485            );
2486
2487            // assert new pool lengths
2488            promotion_test.assert_single_tx_ending_subpool(&pool);
2489        }
2490    }
2491
2492    #[test]
2493    fn test_insert_pending() {
2494        let on_chain_balance = U256::MAX;
2495        let on_chain_nonce = 0;
2496        let mut f = MockTransactionFactory::default();
2497        let mut pool = AllTransactions::default();
2498        let tx = MockTransaction::eip1559().inc_price().inc_limit();
2499        let valid_tx = f.validated(tx);
2500        let InsertOk { updates, replaced_tx, move_to, state, .. } =
2501            pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2502        assert!(updates.is_empty());
2503        assert!(replaced_tx.is_none());
2504        assert!(state.contains(TxState::NO_NONCE_GAPS));
2505        assert!(state.contains(TxState::ENOUGH_BALANCE));
2506        assert_eq!(move_to, SubPool::Pending);
2507
2508        let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2509        assert_eq!(inserted.subpool, SubPool::Pending);
2510    }
2511
2512    #[test]
2513    fn test_simple_insert() {
2514        let on_chain_balance = U256::ZERO;
2515        let on_chain_nonce = 0;
2516        let mut f = MockTransactionFactory::default();
2517        let mut pool = AllTransactions::default();
2518        let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2519        tx.set_priority_fee(100);
2520        tx.set_max_fee(100);
2521        let valid_tx = f.validated(tx.clone());
2522        let InsertOk { updates, replaced_tx, move_to, state, .. } =
2523            pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2524        assert!(updates.is_empty());
2525        assert!(replaced_tx.is_none());
2526        assert!(state.contains(TxState::NO_NONCE_GAPS));
2527        assert!(!state.contains(TxState::ENOUGH_BALANCE));
2528        assert_eq!(move_to, SubPool::Queued);
2529
2530        assert_eq!(pool.len(), 1);
2531        assert!(pool.contains(valid_tx.hash()));
2532        let expected_state = TxState::ENOUGH_FEE_CAP_BLOCK | TxState::NO_NONCE_GAPS;
2533        let inserted = pool.get(valid_tx.id()).unwrap();
2534        assert!(inserted.state.intersects(expected_state));
2535
2536        // insert the same tx again
2537        let res = pool.insert_tx(valid_tx, on_chain_balance, on_chain_nonce);
2538        res.unwrap_err();
2539        assert_eq!(pool.len(), 1);
2540
2541        let valid_tx = f.validated(tx.next());
2542        let InsertOk { updates, replaced_tx, move_to, state, .. } =
2543            pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2544
2545        assert!(updates.is_empty());
2546        assert!(replaced_tx.is_none());
2547        assert!(state.contains(TxState::NO_NONCE_GAPS));
2548        assert!(!state.contains(TxState::ENOUGH_BALANCE));
2549        assert_eq!(move_to, SubPool::Queued);
2550
2551        assert!(pool.contains(valid_tx.hash()));
2552        assert_eq!(pool.len(), 2);
2553        let inserted = pool.get(valid_tx.id()).unwrap();
2554        assert!(inserted.state.intersects(expected_state));
2555    }
2556
2557    #[test]
2558    fn insert_already_imported() {
2559        let on_chain_balance = U256::ZERO;
2560        let on_chain_nonce = 0;
2561        let mut f = MockTransactionFactory::default();
2562        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2563        let tx = MockTransaction::eip1559().inc_price().inc_limit();
2564        let tx = f.validated(tx);
2565        pool.add_transaction(tx.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
2566        match pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap_err().kind {
2567            PoolErrorKind::AlreadyImported => {}
2568            _ => unreachable!(),
2569        }
2570    }
2571
2572    #[test]
2573    fn insert_replace() {
2574        let on_chain_balance = U256::ZERO;
2575        let on_chain_nonce = 0;
2576        let mut f = MockTransactionFactory::default();
2577        let mut pool = AllTransactions::default();
2578        let tx = MockTransaction::eip1559().inc_price().inc_limit();
2579        let first = f.validated(tx.clone());
2580        let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
2581        let replacement = f.validated(tx.rng_hash().inc_price());
2582        let InsertOk { updates, replaced_tx, .. } =
2583            pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
2584        assert!(updates.is_empty());
2585        let replaced = replaced_tx.unwrap();
2586        assert_eq!(replaced.0.hash(), first.hash());
2587
2588        // ensure replaced tx is fully removed
2589        assert!(!pool.contains(first.hash()));
2590        assert!(pool.contains(replacement.hash()));
2591        assert_eq!(pool.len(), 1);
2592    }
2593
2594    #[test]
2595    fn insert_replace_txpool() {
2596        let on_chain_balance = U256::ZERO;
2597        let on_chain_nonce = 0;
2598        let mut f = MockTransactionFactory::default();
2599        let mut pool = TxPool::mock();
2600
2601        let tx = MockTransaction::eip1559().inc_price().inc_limit();
2602        let first = f.validated(tx.clone());
2603        let first_added =
2604            pool.add_transaction(first, on_chain_balance, on_chain_nonce, None).unwrap();
2605        let replacement = f.validated(tx.rng_hash().inc_price());
2606        let replacement_added = pool
2607            .add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce, None)
2608            .unwrap();
2609
2610        // // ensure replaced tx removed
2611        assert!(!pool.contains(first_added.hash()));
2612        // but the replacement is still there
2613        assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));
2614
2615        assert!(pool.contains(replacement.hash()));
2616        let size = pool.size();
2617        assert_eq!(size.total, 1);
2618        size.assert_invariants();
2619    }
2620
2621    #[test]
2622    fn insert_replace_underpriced() {
2623        let on_chain_balance = U256::ZERO;
2624        let on_chain_nonce = 0;
2625        let mut f = MockTransactionFactory::default();
2626        let mut pool = AllTransactions::default();
2627        let tx = MockTransaction::eip1559().inc_price().inc_limit();
2628        let first = f.validated(tx.clone());
2629        let _res = pool.insert_tx(first, on_chain_balance, on_chain_nonce);
2630        let mut replacement = f.validated(tx.rng_hash());
2631        replacement.transaction = replacement.transaction.decr_price();
2632        let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
2633        assert!(matches!(err, InsertErr::Underpriced { .. }));
2634    }
2635
2636    #[test]
2637    fn insert_replace_underpriced_not_enough_bump() {
2638        let on_chain_balance = U256::ZERO;
2639        let on_chain_nonce = 0;
2640        let mut f = MockTransactionFactory::default();
2641        let mut pool = AllTransactions::default();
2642        let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2643        tx.set_priority_fee(100);
2644        tx.set_max_fee(100);
2645        let first = f.validated(tx.clone());
2646        let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
2647        let mut replacement = f.validated(tx.rng_hash().inc_price());
2648
2649        // a price bump of 9% is not enough for a default min price bump of 10%
2650        replacement.transaction.set_priority_fee(109);
2651        replacement.transaction.set_max_fee(109);
2652        let err =
2653            pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
2654        assert!(matches!(err, InsertErr::Underpriced { .. }));
2655        // ensure first tx is not removed
2656        assert!(pool.contains(first.hash()));
2657        assert_eq!(pool.len(), 1);
2658
2659        // should also fail if the bump in max fee is not enough
2660        replacement.transaction.set_priority_fee(110);
2661        replacement.transaction.set_max_fee(109);
2662        let err =
2663            pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
2664        assert!(matches!(err, InsertErr::Underpriced { .. }));
2665        assert!(pool.contains(first.hash()));
2666        assert_eq!(pool.len(), 1);
2667
2668        // should also fail if the bump in priority fee is not enough
2669        replacement.transaction.set_priority_fee(109);
2670        replacement.transaction.set_max_fee(110);
2671        let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
2672        assert!(matches!(err, InsertErr::Underpriced { .. }));
2673        assert!(pool.contains(first.hash()));
2674        assert_eq!(pool.len(), 1);
2675    }
2676
2677    #[test]
2678    fn insert_conflicting_type_normal_to_blob() {
2679        let on_chain_balance = U256::from(10_000);
2680        let on_chain_nonce = 0;
2681        let mut f = MockTransactionFactory::default();
2682        let mut pool = AllTransactions::default();
2683        let tx = MockTransaction::eip1559().inc_price().inc_limit();
2684        let first = f.validated(tx.clone());
2685        pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
2686        let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
2687        let blob = f.validated(tx);
2688        let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
2689        assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
2690    }
2691
2692    #[test]
2693    fn insert_conflicting_type_blob_to_normal() {
2694        let on_chain_balance = U256::from(10_000);
2695        let on_chain_nonce = 0;
2696        let mut f = MockTransactionFactory::default();
2697        let mut pool = AllTransactions::default();
2698        let tx = MockTransaction::eip4844().inc_price().inc_limit();
2699        let first = f.validated(tx.clone());
2700        pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
2701        let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
2702        let tx = f.validated(tx);
2703        let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
2704        assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
2705    }
2706
2707    // insert nonce then nonce - 1
2708    #[test]
2709    fn insert_previous() {
2710        let on_chain_balance = U256::ZERO;
2711        let on_chain_nonce = 0;
2712        let mut f = MockTransactionFactory::default();
2713        let mut pool = AllTransactions::default();
2714        let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
2715        let first = f.validated(tx.clone());
2716        let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
2717
2718        let first_in_pool = pool.get(first.id()).unwrap();
2719
2720        // has nonce gap
2721        assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2722
2723        let prev = f.validated(tx.prev());
2724        let InsertOk { updates, replaced_tx, state, move_to, .. } =
2725            pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
2726
2727        // no updates since still in queued pool
2728        assert!(updates.is_empty());
2729        assert!(replaced_tx.is_none());
2730        assert!(state.contains(TxState::NO_NONCE_GAPS));
2731        assert_eq!(move_to, SubPool::Queued);
2732
2733        let first_in_pool = pool.get(first.id()).unwrap();
2734        // has non nonce gap
2735        assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2736    }
2737
2738    // insert nonce then nonce - 1
2739    #[test]
2740    fn insert_with_updates() {
2741        let on_chain_balance = U256::from(10_000);
2742        let on_chain_nonce = 0;
2743        let mut f = MockTransactionFactory::default();
2744        let mut pool = AllTransactions::default();
2745        let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
2746        let first = f.validated(tx.clone());
2747        let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
2748
2749        let first_in_pool = pool.get(first.id()).unwrap();
2750        // has nonce gap
2751        assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2752        assert_eq!(SubPool::Queued, first_in_pool.subpool);
2753
2754        let prev = f.validated(tx.prev());
2755        let InsertOk { updates, replaced_tx, state, move_to, .. } =
2756            pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
2757
2758        // updated previous tx
2759        assert_eq!(updates.len(), 1);
2760        assert!(replaced_tx.is_none());
2761        assert!(state.contains(TxState::NO_NONCE_GAPS));
2762        assert_eq!(move_to, SubPool::Pending);
2763
2764        let first_in_pool = pool.get(first.id()).unwrap();
2765        // has non nonce gap
2766        assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2767        assert_eq!(SubPool::Pending, first_in_pool.subpool);
2768    }
2769
2770    #[test]
2771    fn insert_previous_blocking() {
2772        let on_chain_balance = U256::from(1_000);
2773        let on_chain_nonce = 0;
2774        let mut f = MockTransactionFactory::default();
2775        let mut pool = AllTransactions::default();
2776        pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
2777        let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
2778        let first = f.validated(tx.clone());
2779
2780        let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
2781
2782        let first_in_pool = pool.get(first.id()).unwrap();
2783
2784        assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
2785        // has nonce gap
2786        assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2787
2788        let prev = f.validated(tx.prev());
2789        let InsertOk { updates, replaced_tx, state, move_to, .. } =
2790            pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
2791
2792        assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
2793        // no updates since still in queued pool
2794        assert!(updates.is_empty());
2795        assert!(replaced_tx.is_none());
2796        assert!(state.contains(TxState::NO_NONCE_GAPS));
2797        assert_eq!(move_to, SubPool::BaseFee);
2798
2799        let first_in_pool = pool.get(first.id()).unwrap();
2800        // has non nonce gap
2801        assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2802    }
2803
2804    #[test]
2805    fn rejects_spammer() {
2806        let on_chain_balance = U256::from(1_000);
2807        let on_chain_nonce = 0;
2808        let mut f = MockTransactionFactory::default();
2809        let mut pool = AllTransactions::default();
2810
2811        let mut tx = MockTransaction::eip1559();
2812        let unblocked_tx = tx.clone();
2813        for _ in 0..pool.max_account_slots {
2814            tx = tx.next();
2815            pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
2816        }
2817
2818        assert_eq!(
2819            pool.max_account_slots,
2820            pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
2821        );
2822
2823        let err =
2824            pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
2825        assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
2826
2827        assert!(pool
2828            .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
2829            .is_ok());
2830    }
2831
2832    #[test]
2833    fn allow_local_spamming() {
2834        let on_chain_balance = U256::from(1_000);
2835        let on_chain_nonce = 0;
2836        let mut f = MockTransactionFactory::default();
2837        let mut pool = AllTransactions::default();
2838
2839        let mut tx = MockTransaction::eip1559();
2840        for _ in 0..pool.max_account_slots {
2841            tx = tx.next();
2842            pool.insert_tx(
2843                f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
2844                on_chain_balance,
2845                on_chain_nonce,
2846            )
2847            .unwrap();
2848        }
2849
2850        assert_eq!(
2851            pool.max_account_slots,
2852            pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
2853        );
2854
2855        pool.insert_tx(
2856            f.validated_with_origin(TransactionOrigin::Local, tx.next()),
2857            on_chain_balance,
2858            on_chain_nonce,
2859        )
2860        .unwrap();
2861    }
2862
2863    #[test]
2864    fn reject_tx_over_gas_limit() {
2865        let on_chain_balance = U256::from(1_000);
2866        let on_chain_nonce = 0;
2867        let mut f = MockTransactionFactory::default();
2868        let mut pool = AllTransactions::default();
2869
2870        let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
2871
2872        assert!(matches!(
2873            pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
2874            Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
2875        ));
2876    }
2877
2878    #[test]
2879    fn test_tx_equal_gas_limit() {
2880        let on_chain_balance = U256::from(1_000);
2881        let on_chain_nonce = 0;
2882        let mut f = MockTransactionFactory::default();
2883        let mut pool = AllTransactions::default();
2884
2885        let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
2886
2887        let InsertOk { state, .. } =
2888            pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
2889        assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
2890    }
2891
2892    #[test]
2893    fn update_basefee_subpools() {
2894        let mut f = MockTransactionFactory::default();
2895        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2896
2897        let tx = MockTransaction::eip1559().inc_price_by(10);
2898        let validated = f.validated(tx.clone());
2899        let id = *validated.id();
2900        pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
2901
2902        assert_eq!(pool.pending_pool.len(), 1);
2903
2904        pool.update_basefee((tx.max_fee_per_gas() + 1) as u64);
2905
2906        assert!(pool.pending_pool.is_empty());
2907        assert_eq!(pool.basefee_pool.len(), 1);
2908
2909        assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
2910    }
2911
2912    #[test]
2913    fn update_basefee_subpools_setting_block_info() {
2914        let mut f = MockTransactionFactory::default();
2915        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2916
2917        let tx = MockTransaction::eip1559().inc_price_by(10);
2918        let validated = f.validated(tx.clone());
2919        let id = *validated.id();
2920        pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
2921
2922        assert_eq!(pool.pending_pool.len(), 1);
2923
2924        // use set_block_info for the basefee update
2925        let mut block_info = pool.block_info();
2926        block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
2927        pool.set_block_info(block_info);
2928
2929        assert!(pool.pending_pool.is_empty());
2930        assert_eq!(pool.basefee_pool.len(), 1);
2931
2932        assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
2933    }
2934
2935    #[test]
2936    fn get_highest_transaction_by_sender_and_nonce() {
2937        // Set up a mock transaction factory and a new transaction pool.
2938        let mut f = MockTransactionFactory::default();
2939        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2940
2941        // Create a mock transaction and add it to the pool.
2942        let tx = MockTransaction::eip1559();
2943        pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0, None).unwrap();
2944
2945        // Create another mock transaction with an incremented price.
2946        let tx1 = tx.inc_price().next();
2947
2948        // Validate the second mock transaction and add it to the pool.
2949        let tx1_validated = f.validated(tx1.clone());
2950        pool.add_transaction(tx1_validated, U256::from(1_000), 0, None).unwrap();
2951
2952        // Ensure that the calculated next nonce for the sender matches the expected value.
2953        assert_eq!(
2954            pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
2955            Some(1)
2956        );
2957
2958        // Retrieve the highest transaction by sender.
2959        let highest_tx = pool
2960            .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
2961            .expect("Failed to retrieve highest transaction");
2962
2963        // Validate that the retrieved highest transaction matches the expected transaction.
2964        assert_eq!(highest_tx.as_ref().transaction, tx1);
2965    }
2966
2967    #[test]
2968    fn get_highest_consecutive_transaction_by_sender() {
2969        // Set up a mock transaction factory and a new transaction pool.
2970        let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
2971        let mut f = MockTransactionFactory::default();
2972
2973        // Create transactions with nonces 0, 1, 2, 4, 5.
2974        let sender = Address::random();
2975        let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
2976        for nonce in txs {
2977            let mut mock_tx = MockTransaction::eip1559();
2978            mock_tx.set_sender(sender);
2979            mock_tx.set_nonce(nonce);
2980
2981            let validated_tx = f.validated(mock_tx);
2982            pool.add_transaction(validated_tx, U256::from(1000), 0, None).unwrap();
2983        }
2984
2985        // Get last consecutive transaction
2986        let sender_id = f.ids.sender_id(&sender).unwrap();
2987        let next_tx =
2988            pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
2989        assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
2990
2991        let next_tx =
2992            pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
2993        assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
2994
2995        let next_tx =
2996            pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
2997        assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
2998
2999        // update the tracked nonce
3000        let mut info = SenderInfo::default();
3001        info.update(8, U256::ZERO);
3002        pool.sender_info.insert(sender_id, info);
3003        let next_tx =
3004            pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3005        assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
3006    }
3007
3008    #[test]
3009    fn discard_nonce_too_low() {
3010        let mut f = MockTransactionFactory::default();
3011        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3012
3013        let tx = MockTransaction::eip1559().inc_price_by(10);
3014        let validated = f.validated(tx.clone());
3015        let id = *validated.id();
3016        pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3017
3018        let next = tx.next();
3019        let validated = f.validated(next.clone());
3020        pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3021
3022        assert_eq!(pool.pending_pool.len(), 2);
3023
3024        let mut changed_senders = HashMap::default();
3025        changed_senders.insert(
3026            id.sender,
3027            SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
3028        );
3029        let outcome = pool.update_accounts(changed_senders);
3030        assert_eq!(outcome.discarded.len(), 1);
3031        assert_eq!(pool.pending_pool.len(), 1);
3032    }
3033
3034    #[test]
3035    fn discard_with_large_blob_txs() {
3036        // init tracing
3037        reth_tracing::init_test_tracing();
3038
3039        // this test adds large txs to the parked pool, then attempting to discard worst
3040        let mut f = MockTransactionFactory::default();
3041        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3042        let default_limits = pool.config.blob_limit;
3043
3044        // create a chain of transactions by sender A
3045        // make sure they are all one over half the limit
3046        let a_sender = address!("0x000000000000000000000000000000000000000a");
3047
3048        // set the base fee of the pool
3049        let mut block_info = pool.block_info();
3050        block_info.pending_blob_fee = Some(100);
3051        block_info.pending_basefee = 100;
3052
3053        // update
3054        pool.set_block_info(block_info);
3055
3056        // 2 txs, that should put the pool over the size limit but not max txs
3057        let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
3058            .into_iter()
3059            .map(|mut tx| {
3060                tx.set_size(default_limits.max_size / 2 + 1);
3061                tx.set_max_fee((block_info.pending_basefee - 1).into());
3062                tx
3063            })
3064            .collect::<Vec<_>>();
3065
3066        // add all the transactions to the parked pool
3067        for tx in a_txs {
3068            pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3069        }
3070
3071        // truncate the pool, it should remove at least one transaction
3072        let removed = pool.discard_worst();
3073        assert_eq!(removed.len(), 1);
3074    }
3075
3076    #[test]
3077    fn discard_with_parked_large_txs() {
3078        // init tracing
3079        reth_tracing::init_test_tracing();
3080
3081        // this test adds large txs to the parked pool, then attempting to discard worst
3082        let mut f = MockTransactionFactory::default();
3083        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3084        let default_limits = pool.config.queued_limit;
3085
3086        // create a chain of transactions by sender A
3087        // make sure they are all one over half the limit
3088        let a_sender = address!("0x000000000000000000000000000000000000000a");
3089
3090        // set the base fee of the pool
3091        let pool_base_fee = 100;
3092        pool.update_basefee(pool_base_fee);
3093
3094        // 2 txs, that should put the pool over the size limit but not max txs
3095        let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
3096            .into_iter()
3097            .map(|mut tx| {
3098                tx.set_size(default_limits.max_size / 2 + 1);
3099                tx.set_max_fee((pool_base_fee - 1).into());
3100                tx
3101            })
3102            .collect::<Vec<_>>();
3103
3104        // add all the transactions to the parked pool
3105        for tx in a_txs {
3106            pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3107        }
3108
3109        // truncate the pool, it should remove at least one transaction
3110        let removed = pool.discard_worst();
3111        assert_eq!(removed.len(), 1);
3112    }
3113
3114    #[test]
3115    fn discard_at_capacity() {
3116        let mut f = MockTransactionFactory::default();
3117        let queued_limit = SubPoolLimit::new(1000, usize::MAX);
3118        let mut pool =
3119            TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
3120
3121        // insert a bunch of transactions into the queued pool
3122        for _ in 0..queued_limit.max_txs {
3123            let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3124            let validated = f.validated(tx.clone());
3125            let _id = *validated.id();
3126            pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3127        }
3128
3129        let size = pool.size();
3130        assert_eq!(size.queued, queued_limit.max_txs);
3131
3132        for _ in 0..queued_limit.max_txs {
3133            let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3134            let validated = f.validated(tx.clone());
3135            let _id = *validated.id();
3136            pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3137
3138            pool.discard_worst();
3139            pool.assert_invariants();
3140            assert!(pool.size().queued <= queued_limit.max_txs);
3141        }
3142    }
3143
3144    #[test]
3145    fn discard_blobs_at_capacity() {
3146        let mut f = MockTransactionFactory::default();
3147        let blob_limit = SubPoolLimit::new(1000, usize::MAX);
3148        let mut pool =
3149            TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
3150        pool.all_transactions.pending_fees.blob_fee = 10000;
3151        // insert a bunch of transactions into the queued pool
3152        for _ in 0..blob_limit.max_txs {
3153            let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3154            let validated = f.validated(tx.clone());
3155            let _id = *validated.id();
3156            pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3157        }
3158
3159        let size = pool.size();
3160        assert_eq!(size.blob, blob_limit.max_txs);
3161
3162        for _ in 0..blob_limit.max_txs {
3163            let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3164            let validated = f.validated(tx.clone());
3165            let _id = *validated.id();
3166            pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3167
3168            pool.discard_worst();
3169            pool.assert_invariants();
3170            assert!(pool.size().blob <= blob_limit.max_txs);
3171        }
3172    }
3173
3174    #[test]
3175    fn account_updates_sender_balance() {
3176        let mut on_chain_balance = U256::from(100);
3177        let on_chain_nonce = 0;
3178        let mut f = MockTransactionFactory::default();
3179        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3180
3181        let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3182        let tx_1 = tx_0.next();
3183        let tx_2 = tx_1.next();
3184
3185        // Create 3 transactions
3186        let v0 = f.validated(tx_0);
3187        let v1 = f.validated(tx_1);
3188        let v2 = f.validated(tx_2);
3189
3190        let _res =
3191            pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3192        let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3193        let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3194
3195        // The sender does not have enough balance to put all txs into pending.
3196        assert_eq!(1, pool.pending_transactions().len());
3197        assert_eq!(2, pool.queued_transactions().len());
3198
3199        // Simulate new block arrival - and chain balance increase.
3200        let mut updated_accounts = HashMap::default();
3201        on_chain_balance = U256::from(300);
3202        updated_accounts.insert(
3203            v0.sender_id(),
3204            SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3205        );
3206        pool.update_accounts(updated_accounts.clone());
3207
3208        assert_eq!(3, pool.pending_transactions().len());
3209        assert!(pool.queued_transactions().is_empty());
3210
3211        // Simulate new block arrival - and chain balance decrease.
3212        updated_accounts.entry(v0.sender_id()).and_modify(|v| v.balance = U256::from(1));
3213        pool.update_accounts(updated_accounts);
3214
3215        assert!(pool.pending_transactions().is_empty());
3216        assert_eq!(3, pool.queued_transactions().len());
3217    }
3218
3219    #[test]
3220    fn account_updates_nonce_gap() {
3221        let on_chain_balance = U256::from(10_000);
3222        let mut on_chain_nonce = 0;
3223        let mut f = MockTransactionFactory::default();
3224        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3225
3226        let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3227        let tx_1 = tx_0.next();
3228        let tx_2 = tx_1.next();
3229
3230        // Create 3 transactions
3231        let v0 = f.validated(tx_0);
3232        let v1 = f.validated(tx_1);
3233        let v2 = f.validated(tx_2);
3234
3235        // Add first 2 to the pool
3236        let _res =
3237            pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3238        let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3239
3240        assert!(pool.queued_transactions().is_empty());
3241        assert_eq!(2, pool.pending_transactions().len());
3242
3243        // Remove first (nonce 0)
3244        pool.remove_transaction_by_hash(v0.hash());
3245
3246        // Now add transaction with nonce 2
3247        let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3248
3249        // v1 and v2 should both be in the queue now.
3250        assert_eq!(2, pool.queued_transactions().len());
3251        assert!(pool.pending_transactions().is_empty());
3252
3253        // Simulate new block arrival - and chain nonce increasing.
3254        let mut updated_accounts = HashMap::default();
3255        on_chain_nonce += 1;
3256        updated_accounts.insert(
3257            v0.sender_id(),
3258            SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3259        );
3260        pool.update_accounts(updated_accounts);
3261
3262        // 'pending' now).
3263        assert!(pool.queued_transactions().is_empty());
3264        assert_eq!(2, pool.pending_transactions().len());
3265    }
3266    #[test]
3267    fn test_transaction_removal() {
3268        let on_chain_balance = U256::from(10_000);
3269        let on_chain_nonce = 0;
3270        let mut f = MockTransactionFactory::default();
3271        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3272
3273        let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3274        let tx_1 = tx_0.next();
3275
3276        // Create 2 transactions
3277        let v0 = f.validated(tx_0);
3278        let v1 = f.validated(tx_1);
3279
3280        // Add them to the pool
3281        let _res =
3282            pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3283        let _res =
3284            pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3285
3286        assert_eq!(0, pool.queued_transactions().len());
3287        assert_eq!(2, pool.pending_transactions().len());
3288
3289        // Remove first (nonce 0) - simulating that it was taken to be a part of the block.
3290        pool.remove_transaction(v0.id());
3291        // assert the second transaction is really at the top of the queue
3292        let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
3293        assert_eq!(vec![v1.nonce()], pool_txs);
3294    }
3295    #[test]
3296    fn test_remove_transactions() {
3297        let on_chain_balance = U256::from(10_000);
3298        let on_chain_nonce = 0;
3299        let mut f = MockTransactionFactory::default();
3300        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3301
3302        let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3303        let tx_1 = tx_0.next();
3304        let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3305        let tx_3 = tx_2.next();
3306
3307        // Create 4 transactions
3308        let v0 = f.validated(tx_0);
3309        let v1 = f.validated(tx_1);
3310        let v2 = f.validated(tx_2);
3311        let v3 = f.validated(tx_3);
3312
3313        // Add them to the pool
3314        let _res =
3315            pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3316        let _res =
3317            pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3318        let _res =
3319            pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3320        let _res =
3321            pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3322
3323        assert_eq!(0, pool.queued_transactions().len());
3324        assert_eq!(4, pool.pending_transactions().len());
3325
3326        pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
3327
3328        assert_eq!(2, pool.queued_transactions().len());
3329        assert!(pool.pending_transactions().is_empty());
3330        assert!(pool.contains(v1.hash()));
3331        assert!(pool.contains(v3.hash()));
3332    }
3333
3334    #[test]
3335    fn test_remove_transactions_middle_pending_hash() {
3336        let on_chain_balance = U256::from(10_000);
3337        let on_chain_nonce = 0;
3338        let mut f = MockTransactionFactory::default();
3339        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3340
3341        let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3342        let tx_1 = tx_0.next();
3343        let tx_2 = tx_1.next();
3344        let tx_3 = tx_2.next();
3345
3346        // Create 4 transactions
3347        let v0 = f.validated(tx_0);
3348        let v1 = f.validated(tx_1);
3349        let v2 = f.validated(tx_2);
3350        let v3 = f.validated(tx_3);
3351
3352        // Add them to the pool
3353        let _res = pool.add_transaction(v0, on_chain_balance, on_chain_nonce, None).unwrap();
3354        let _res =
3355            pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3356        let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3357        let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
3358
3359        assert_eq!(0, pool.queued_transactions().len());
3360        assert_eq!(4, pool.pending_transactions().len());
3361
3362        let mut removed_txs = pool.remove_transactions(vec![*v1.hash()]);
3363        assert_eq!(1, removed_txs.len());
3364
3365        assert_eq!(2, pool.queued_transactions().len());
3366        assert_eq!(1, pool.pending_transactions().len());
3367
3368        // reinsert
3369        let removed_tx = removed_txs.pop().unwrap();
3370        let v1 = f.validated(removed_tx.transaction.clone());
3371        let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3372        assert_eq!(0, pool.queued_transactions().len());
3373        assert_eq!(4, pool.pending_transactions().len());
3374    }
3375
3376    #[test]
3377    fn test_remove_transactions_and_descendants() {
3378        let on_chain_balance = U256::from(10_000);
3379        let on_chain_nonce = 0;
3380        let mut f = MockTransactionFactory::default();
3381        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3382
3383        let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3384        let tx_1 = tx_0.next();
3385        let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3386        let tx_3 = tx_2.next();
3387        let tx_4 = tx_3.next();
3388
3389        // Create 5 transactions
3390        let v0 = f.validated(tx_0);
3391        let v1 = f.validated(tx_1);
3392        let v2 = f.validated(tx_2);
3393        let v3 = f.validated(tx_3);
3394        let v4 = f.validated(tx_4);
3395
3396        // Add them to the pool
3397        let _res =
3398            pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3399        let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3400        let _res =
3401            pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3402        let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
3403        let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
3404
3405        assert_eq!(0, pool.queued_transactions().len());
3406        assert_eq!(5, pool.pending_transactions().len());
3407
3408        pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
3409
3410        assert_eq!(0, pool.queued_transactions().len());
3411        assert_eq!(0, pool.pending_transactions().len());
3412    }
3413    #[test]
3414    fn test_remove_descendants() {
3415        let on_chain_balance = U256::from(10_000);
3416        let on_chain_nonce = 0;
3417        let mut f = MockTransactionFactory::default();
3418        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3419
3420        let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3421        let tx_1 = tx_0.next();
3422        let tx_2 = tx_1.next();
3423        let tx_3 = tx_2.next();
3424
3425        // Create 4 transactions
3426        let v0 = f.validated(tx_0);
3427        let v1 = f.validated(tx_1);
3428        let v2 = f.validated(tx_2);
3429        let v3 = f.validated(tx_3);
3430
3431        // Add them to the  pool
3432        let _res =
3433            pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3434        let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3435        let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3436        let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
3437
3438        assert_eq!(0, pool.queued_transactions().len());
3439        assert_eq!(4, pool.pending_transactions().len());
3440
3441        let mut removed = Vec::new();
3442        pool.remove_transaction(v0.id());
3443        pool.remove_descendants(v0.id(), &mut removed);
3444
3445        assert_eq!(0, pool.queued_transactions().len());
3446        assert_eq!(0, pool.pending_transactions().len());
3447        assert_eq!(3, removed.len());
3448    }
3449    #[test]
3450    fn test_remove_transactions_by_sender() {
3451        let on_chain_balance = U256::from(10_000);
3452        let on_chain_nonce = 0;
3453        let mut f = MockTransactionFactory::default();
3454        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3455
3456        let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3457        let tx_1 = tx_0.next();
3458        let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3459        let tx_3 = tx_2.next();
3460        let tx_4 = tx_3.next();
3461
3462        // Create 5 transactions
3463        let v0 = f.validated(tx_0);
3464        let v1 = f.validated(tx_1);
3465        let v2 = f.validated(tx_2);
3466        let v3 = f.validated(tx_3);
3467        let v4 = f.validated(tx_4);
3468
3469        // Add them to the pool
3470        let _res =
3471            pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3472        let _res =
3473            pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3474        let _res =
3475            pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3476        let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
3477        let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
3478
3479        assert_eq!(0, pool.queued_transactions().len());
3480        assert_eq!(5, pool.pending_transactions().len());
3481
3482        pool.remove_transactions_by_sender(v2.sender_id());
3483
3484        assert_eq!(0, pool.queued_transactions().len());
3485        assert_eq!(2, pool.pending_transactions().len());
3486        assert!(pool.contains(v0.hash()));
3487        assert!(pool.contains(v1.hash()));
3488    }
3489    #[test]
3490    fn wrong_best_order_of_transactions() {
3491        let on_chain_balance = U256::from(10_000);
3492        let mut on_chain_nonce = 0;
3493        let mut f = MockTransactionFactory::default();
3494        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3495
3496        let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3497        let tx_1 = tx_0.next();
3498        let tx_2 = tx_1.next();
3499        let tx_3 = tx_2.next();
3500
3501        // Create 4 transactions
3502        let v0 = f.validated(tx_0);
3503        let v1 = f.validated(tx_1);
3504        let v2 = f.validated(tx_2);
3505        let v3 = f.validated(tx_3);
3506
3507        // Add first 2 to the pool
3508        let _res =
3509            pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3510        let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3511
3512        assert_eq!(0, pool.queued_transactions().len());
3513        assert_eq!(2, pool.pending_transactions().len());
3514
3515        // Remove first (nonce 0) - simulating that it was taken to be a part of the block.
3516        pool.remove_transaction(v0.id());
3517
3518        // Now add transaction with nonce 2
3519        let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3520
3521        // v2 is in the queue now. v1 is still in 'pending'.
3522        assert_eq!(1, pool.queued_transactions().len());
3523        assert_eq!(1, pool.pending_transactions().len());
3524
3525        // Simulate new block arrival - and chain nonce increasing.
3526        let mut updated_accounts = HashMap::default();
3527        on_chain_nonce += 1;
3528        updated_accounts.insert(
3529            v0.sender_id(),
3530            SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3531        );
3532        pool.update_accounts(updated_accounts);
3533
3534        // Transactions are not changed (IMHO - this is a bug, as transaction v2 should be in the
3535        // 'pending' now).
3536        assert_eq!(0, pool.queued_transactions().len());
3537        assert_eq!(2, pool.pending_transactions().len());
3538
3539        // Add transaction v3 - it 'unclogs' everything.
3540        let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
3541        assert_eq!(0, pool.queued_transactions().len());
3542        assert_eq!(3, pool.pending_transactions().len());
3543
3544        // It should have returned transactions in order (v1, v2, v3 - as there is nothing blocking
3545        // them).
3546        assert_eq!(
3547            pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
3548            vec![1, 2, 3]
3549        );
3550    }
3551
3552    #[test]
3553    fn test_best_with_attributes() {
3554        let on_chain_balance = U256::MAX;
3555        let on_chain_nonce = 0;
3556        let mut f = MockTransactionFactory::default();
3557        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3558
3559        let base_fee: u128 = 100;
3560        let blob_fee: u128 = 100;
3561
3562        // set base fee and blob fee.
3563        let mut block_info = pool.block_info();
3564        block_info.pending_basefee = base_fee as u64;
3565        block_info.pending_blob_fee = Some(blob_fee);
3566        pool.set_block_info(block_info);
3567
3568        // Insert transactions with varying max_fee_per_gas and max_fee_per_blob_gas.
3569        let tx1 = MockTransaction::eip4844()
3570            .with_sender(Address::with_last_byte(1))
3571            .with_max_fee(base_fee + 10)
3572            .with_blob_fee(blob_fee + 10);
3573        let tx2 = MockTransaction::eip4844()
3574            .with_sender(Address::with_last_byte(2))
3575            .with_max_fee(base_fee + 10)
3576            .with_blob_fee(blob_fee);
3577        let tx3 = MockTransaction::eip4844()
3578            .with_sender(Address::with_last_byte(3))
3579            .with_max_fee(base_fee)
3580            .with_blob_fee(blob_fee + 10);
3581        let tx4 = MockTransaction::eip4844()
3582            .with_sender(Address::with_last_byte(4))
3583            .with_max_fee(base_fee)
3584            .with_blob_fee(blob_fee);
3585        let tx5 = MockTransaction::eip4844()
3586            .with_sender(Address::with_last_byte(5))
3587            .with_max_fee(base_fee)
3588            .with_blob_fee(blob_fee - 10);
3589        let tx6 = MockTransaction::eip4844()
3590            .with_sender(Address::with_last_byte(6))
3591            .with_max_fee(base_fee - 10)
3592            .with_blob_fee(blob_fee);
3593        let tx7 = MockTransaction::eip4844()
3594            .with_sender(Address::with_last_byte(7))
3595            .with_max_fee(base_fee - 10)
3596            .with_blob_fee(blob_fee - 10);
3597
3598        for tx in vec![
3599            tx1.clone(),
3600            tx2.clone(),
3601            tx3.clone(),
3602            tx4.clone(),
3603            tx5.clone(),
3604            tx6.clone(),
3605            tx7.clone(),
3606        ] {
3607            pool.add_transaction(f.validated(tx.clone()), on_chain_balance, on_chain_nonce, None)
3608                .unwrap();
3609        }
3610
3611        let base_fee = base_fee as u64;
3612        let blob_fee = blob_fee as u64;
3613
3614        let cases = vec![
3615            // 1. Base fee increase, blob fee increase
3616            (BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee + 5)), vec![tx1.clone()]),
3617            // 2. Base fee increase, blob fee not change
3618            (
3619                BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee)),
3620                vec![tx1.clone(), tx2.clone()],
3621            ),
3622            // 3. Base fee increase, blob fee decrease
3623            (
3624                BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee - 5)),
3625                vec![tx1.clone(), tx2.clone()],
3626            ),
3627            // 4. Base fee not change, blob fee increase
3628            (
3629                BestTransactionsAttributes::new(base_fee, Some(blob_fee + 5)),
3630                vec![tx1.clone(), tx3.clone()],
3631            ),
3632            // 5. Base fee not change, blob fee not change
3633            (
3634                BestTransactionsAttributes::new(base_fee, Some(blob_fee)),
3635                vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
3636            ),
3637            // 6. Base fee not change, blob fee decrease
3638            (
3639                BestTransactionsAttributes::new(base_fee, Some(blob_fee - 10)),
3640                vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx5.clone()],
3641            ),
3642            // 7. Base fee decrease, blob fee increase
3643            (
3644                BestTransactionsAttributes::new(base_fee - 5, Some(blob_fee + 5)),
3645                vec![tx1.clone(), tx3.clone()],
3646            ),
3647            // 8. Base fee decrease, blob fee not change
3648            (
3649                BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee)),
3650                vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx6.clone()],
3651            ),
3652            // 9. Base fee decrease, blob fee decrease
3653            (
3654                BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee - 10)),
3655                vec![tx1, tx2, tx5, tx3, tx4, tx6, tx7],
3656            ),
3657        ];
3658
3659        for (idx, (attribute, expected)) in cases.into_iter().enumerate() {
3660            let mut best = pool.best_transactions_with_attributes(attribute);
3661
3662            for (tx_idx, expected_tx) in expected.into_iter().enumerate() {
3663                let tx = best.next().expect("Transaction should be returned");
3664                assert_eq!(
3665                    tx.transaction,
3666                    expected_tx,
3667                    "Failed tx {} in case {}",
3668                    tx_idx + 1,
3669                    idx + 1
3670                );
3671            }
3672
3673            // No more transactions should be returned
3674            assert!(best.next().is_none());
3675        }
3676    }
3677
3678    #[test]
3679    fn test_pending_ordering() {
3680        let mut f = MockTransactionFactory::default();
3681        let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3682
3683        let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
3684        let tx_1 = tx_0.next();
3685
3686        let v0 = f.validated(tx_0);
3687        let v1 = f.validated(tx_1);
3688
3689        // nonce gap, tx should be queued
3690        pool.add_transaction(v0.clone(), U256::MAX, 0, None).unwrap();
3691        assert_eq!(1, pool.queued_transactions().len());
3692
3693        // nonce gap is closed on-chain, both transactions should be moved to pending
3694        pool.add_transaction(v1, U256::MAX, 1, None).unwrap();
3695
3696        assert_eq!(2, pool.pending_transactions().len());
3697        assert_eq!(0, pool.queued_transactions().len());
3698
3699        assert_eq!(
3700            pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
3701            v0.nonce()
3702        );
3703    }
3704
3705    // <https://github.com/paradigmxyz/reth/issues/12286>
3706    #[test]
3707    fn one_sender_one_independent_transaction() {
3708        let mut on_chain_balance = U256::from(4_999); // only enough for 4 txs
3709        let mut on_chain_nonce = 40;
3710        let mut f = MockTransactionFactory::default();
3711        let mut pool = TxPool::mock();
3712        let mut submitted_txs = Vec::new();
3713
3714        // We use a "template" because we want all txs to have the same sender.
3715        let template =
3716            MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
3717
3718        // Add 8 txs. Because the balance is only sufficient for 4, so the last 4 will be
3719        // Queued.
3720        for tx_nonce in 40..48 {
3721            let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
3722            submitted_txs.push(*tx.id());
3723            pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap();
3724        }
3725
3726        // A block is mined with two txs (so nonce is changed from 40 to 42).
3727        // Now the balance gets so high that it's enough to execute alltxs.
3728        on_chain_balance = U256::from(999_999);
3729        on_chain_nonce = 42;
3730        pool.remove_transaction(&submitted_txs[0]);
3731        pool.remove_transaction(&submitted_txs[1]);
3732
3733        // Add 4 txs.
3734        for tx_nonce in 48..52 {
3735            pool.add_transaction(
3736                f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
3737                on_chain_balance,
3738                on_chain_nonce,
3739                None,
3740            )
3741            .unwrap();
3742        }
3743
3744        let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
3745        assert_eq!(best_txs.len(), 10); // 8 - 2 + 4 = 10
3746
3747        assert_eq!(pool.pending_pool.independent().len(), 1);
3748    }
3749}