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