Skip to main content

reth_transaction_pool/pool/
pending.rs

1//! Pending transactions
2
3use crate::{
4    identifier::{SenderId, TransactionId},
5    pool::{
6        best::{BestTransactions, BestTransactionsWithFees},
7        size::SizeTracker,
8    },
9    Priority, SubPoolLimit, TransactionOrdering, ValidPoolTransaction,
10};
11use imbl::OrdMap;
12use rustc_hash::{FxHashMap, FxHashSet};
13use std::{cmp::Ordering, collections::hash_map::Entry, ops::Bound::Unbounded, sync::Arc};
14use tokio::sync::broadcast;
15
16/// A pool of validated and gapless transactions that are ready to be executed on the current state
17/// and are waiting to be included in a block.
18///
19/// This pool distinguishes between `independent` transactions and pending transactions. A
20/// transaction is `independent`, if it is in the pending pool, and it has the current on chain
21/// nonce of the sender. Meaning `independent` transactions can be executed right away, other
22/// pending transactions depend on at least one `independent` transaction.
23///
24/// Once an `independent` transaction was executed it *unlocks* the next nonce, if this transaction
25/// is also pending, then this will be moved to the `independent` queue.
26#[derive(Debug, Clone)]
27pub struct PendingPool<T: TransactionOrdering> {
28    /// How to order transactions.
29    ordering: T,
30    /// Keeps track of transactions inserted in the pool.
31    ///
32    /// This way we can determine when transactions were submitted to the pool.
33    submission_id: u64,
34    /// _All_ Transactions that are currently inside the pool grouped by their identifier.
35    by_id: OrdMap<TransactionId, PendingTransaction<T>>,
36    /// The highest nonce transactions for each sender - like the `independent` set, but the
37    /// highest instead of lowest nonce.
38    highest_nonces: FxHashMap<SenderId, PendingTransaction<T>>,
39    /// Independent transactions that can be included directly and don't require other
40    /// transactions.
41    independent_transactions: FxHashMap<SenderId, PendingTransaction<T>>,
42    /// Keeps track of the size of this pool.
43    ///
44    /// See also [`reth_primitives_traits::InMemorySize::size`].
45    size_of: SizeTracker,
46    /// Used to broadcast new transactions that have been added to the `PendingPool` to existing
47    /// `static_files` of this pool.
48    new_transaction_notifier: broadcast::Sender<PendingTransaction<T>>,
49}
50
51// === impl PendingPool ===
52
53impl<T: TransactionOrdering> PendingPool<T> {
54    /// Create a new pending pool instance.
55    pub fn new(ordering: T) -> Self {
56        Self::with_buffer(ordering, 200)
57    }
58
59    /// Create a new pool instance with the given buffer capacity.
60    pub fn with_buffer(ordering: T, buffer_capacity: usize) -> Self {
61        let (new_transaction_notifier, _) = broadcast::channel(buffer_capacity);
62        Self {
63            ordering,
64            submission_id: 0,
65            by_id: Default::default(),
66            independent_transactions: Default::default(),
67            highest_nonces: Default::default(),
68            size_of: Default::default(),
69            new_transaction_notifier,
70        }
71    }
72
73    /// Clear all transactions from the pool without resetting other values.
74    /// Used for atomic reordering during basefee update.
75    ///
76    /// # Returns
77    ///
78    /// Returns all transactions by id.
79    fn clear_transactions(&mut self) -> OrdMap<TransactionId, PendingTransaction<T>> {
80        self.independent_transactions.clear();
81        self.highest_nonces.clear();
82        self.size_of.reset();
83        std::mem::take(&mut self.by_id)
84    }
85
86    /// Returns an iterator over all transactions that are _currently_ ready.
87    ///
88    /// 1. The iterator _always_ returns transactions in order: it never returns a transaction with
89    ///    an unsatisfied dependency and only returns them if dependency transaction were yielded
90    ///    previously. In other words: the nonces of transactions with the same sender will _always_
91    ///    increase by exactly 1.
92    ///
93    /// The order of transactions which satisfy (1.) is determined by their computed priority: a
94    /// transaction with a higher priority is returned before a transaction with a lower priority.
95    ///
96    /// If two transactions have the same priority score, then the transactions which spent more
97    /// time in pool (were added earlier) are returned first.
98    ///
99    /// NOTE: while this iterator returns transaction that pool considers valid at this point, they
100    /// could potentially become invalid at point of execution. Therefore, this iterator
101    /// provides a way to mark transactions that the consumer of this iterator considers invalid. In
102    /// which case the transaction's subgraph is also automatically marked invalid, See (1.).
103    /// Invalid transactions are skipped.
104    pub fn best(&self) -> BestTransactions<T> {
105        BestTransactions {
106            all: self.by_id.clone(),
107            independent: self.independent_transactions.values().cloned().collect(),
108            invalid: Default::default(),
109            new_transaction_receiver: Some(self.new_transaction_notifier.subscribe()),
110            last_priority: None,
111            skip_blobs: false,
112        }
113    }
114
115    /// Same as `best` but only returns transactions that satisfy the given basefee and blobfee.
116    pub(crate) fn best_with_basefee_and_blobfee(
117        &self,
118        base_fee: u64,
119        base_fee_per_blob_gas: u64,
120    ) -> BestTransactionsWithFees<T> {
121        BestTransactionsWithFees { best: self.best(), base_fee, base_fee_per_blob_gas }
122    }
123
124    /// Same as `best` but also includes the given unlocked transactions.
125    ///
126    /// This mimics the [`Self::add_transaction`] method, but does not insert the transactions into
127    /// pool but only into the returned iterator.
128    ///
129    /// Note: this does not insert the unlocked transactions into the pool.
130    ///
131    /// # Panics
132    ///
133    /// if the transaction is already included
134    pub(crate) fn best_with_unlocked_and_attributes(
135        &self,
136        unlocked: Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
137        base_fee: u64,
138        base_fee_per_blob_gas: u64,
139    ) -> BestTransactionsWithFees<T> {
140        let mut best = self.best();
141        for (submission_id, tx) in (self.submission_id + 1..).zip(unlocked) {
142            debug_assert!(!best.all.contains_key(tx.id()), "transaction already included");
143            let priority = self.ordering.priority(&tx.transaction, base_fee);
144            let tx_id = *tx.id();
145            let transaction = PendingTransaction { submission_id, transaction: tx, priority };
146            if best.ancestor(&tx_id).is_none() {
147                best.independent.insert(transaction.clone());
148            }
149            best.all.insert(tx_id, transaction);
150        }
151
152        BestTransactionsWithFees { best, base_fee, base_fee_per_blob_gas }
153    }
154
155    /// Returns an iterator over all transactions in the pool
156    pub(crate) fn all(
157        &self,
158    ) -> impl ExactSizeIterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
159        self.by_id.values().map(|tx| tx.transaction.clone())
160    }
161
162    /// Updates the pool with the new blob fee. Removes
163    /// from the subpool all transactions and their dependents that no longer satisfy the given
164    /// blob fee (`tx.max_blob_fee < blob_fee`).
165    ///
166    /// Note: the transactions are not returned in a particular order.
167    ///
168    /// # Returns
169    ///
170    /// Removed transactions that no longer satisfy the blob fee.
171    pub(crate) fn update_blob_fee(
172        &mut self,
173        blob_fee: u128,
174    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
175        // Create a collection for removed transactions.
176        let mut removed = Vec::new();
177
178        // Drain and iterate over all transactions.
179        let mut transactions_iter = self.clear_transactions().into_iter().peekable();
180        while let Some((id, tx)) = transactions_iter.next() {
181            if tx.transaction.is_eip4844() && tx.transaction.max_fee_per_blob_gas() < Some(blob_fee)
182            {
183                // Add this tx to the removed collection since it no longer satisfies the blob fee
184                // condition. Decrease the total pool size.
185                removed.push(Arc::clone(&tx.transaction));
186
187                // Remove all dependent transactions.
188                'this: while let Some((next_id, next_tx)) = transactions_iter.peek() {
189                    if next_id.sender != id.sender {
190                        break 'this
191                    }
192                    removed.push(Arc::clone(&next_tx.transaction));
193                    transactions_iter.next();
194                }
195            } else {
196                self.size_of += tx.transaction.size();
197                self.update_independents_and_highest_nonces(&tx);
198                self.by_id.insert(id, tx);
199            }
200        }
201
202        removed
203    }
204
205    /// Updates the pool with the new base fee. Reorders transactions by new priorities. Removes
206    /// from the subpool all transactions and their dependents that no longer satisfy the given
207    /// base fee (`tx.fee < base_fee`).
208    ///
209    /// Note: the transactions are not returned in a particular order.
210    ///
211    /// # Returns
212    ///
213    /// Removed transactions that no longer satisfy the base fee.
214    pub(crate) fn update_base_fee(
215        &mut self,
216        base_fee: u64,
217    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
218        // Create a collection for removed transactions.
219        let mut removed = Vec::new();
220
221        // Drain and iterate over all transactions.
222        let mut transactions_iter = self.clear_transactions().into_iter().peekable();
223        while let Some((id, mut tx)) = transactions_iter.next() {
224            if tx.transaction.max_fee_per_gas() < base_fee as u128 {
225                // Add this tx to the removed collection since it no longer satisfies the base fee
226                // condition. Decrease the total pool size.
227                removed.push(Arc::clone(&tx.transaction));
228
229                // Remove all dependent transactions.
230                'this: while let Some((next_id, next_tx)) = transactions_iter.peek() {
231                    if next_id.sender != id.sender {
232                        break 'this
233                    }
234                    removed.push(Arc::clone(&next_tx.transaction));
235                    transactions_iter.next();
236                }
237            } else {
238                // Re-insert the transaction with new priority.
239                tx.priority = self.ordering.priority(&tx.transaction.transaction, base_fee);
240
241                self.size_of += tx.transaction.size();
242                self.update_independents_and_highest_nonces(&tx);
243                self.by_id.insert(id, tx);
244            }
245        }
246
247        removed
248    }
249
250    /// Updates the independent transaction and highest nonces set, assuming the given transaction
251    /// is being _added_ to the pool.
252    fn update_independents_and_highest_nonces(&mut self, tx: &PendingTransaction<T>) {
253        match self.highest_nonces.entry(tx.transaction.sender_id()) {
254            Entry::Occupied(mut entry) => {
255                if entry.get().transaction.nonce() < tx.transaction.nonce() {
256                    *entry.get_mut() = tx.clone();
257                }
258            }
259            Entry::Vacant(entry) => {
260                entry.insert(tx.clone());
261            }
262        }
263        match self.independent_transactions.entry(tx.transaction.sender_id()) {
264            Entry::Occupied(mut entry) => {
265                if entry.get().transaction.nonce() > tx.transaction.nonce() {
266                    *entry.get_mut() = tx.clone();
267                }
268            }
269            Entry::Vacant(entry) => {
270                entry.insert(tx.clone());
271            }
272        }
273    }
274
275    /// Adds a new transactions to the pending queue.
276    ///
277    /// # Panics
278    ///
279    /// if the transaction is already included
280    pub fn add_transaction(
281        &mut self,
282        tx: Arc<ValidPoolTransaction<T::Transaction>>,
283        base_fee: u64,
284    ) {
285        debug_assert!(
286            !self.contains(tx.id()),
287            "transaction already included {:?}",
288            self.get(tx.id()).unwrap().transaction
289        );
290
291        // keep track of size
292        self.size_of += tx.size();
293
294        let tx_id = *tx.id();
295
296        let submission_id = self.next_id();
297        let priority = self.ordering.priority(&tx.transaction, base_fee);
298        let tx = PendingTransaction { submission_id, transaction: tx, priority };
299
300        self.update_independents_and_highest_nonces(&tx);
301
302        // send the new transaction to any existing pendingpool static file iterators
303        if self.new_transaction_notifier.receiver_count() > 0 {
304            let _ = self.new_transaction_notifier.send(tx.clone());
305        }
306
307        self.by_id.insert(tx_id, tx);
308    }
309
310    /// Removes the transaction from the pool.
311    ///
312    /// Note: If the transaction has a descendant transaction
313    /// it will advance it to the best queue.
314    pub(crate) fn remove_transaction(
315        &mut self,
316        id: &TransactionId,
317    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
318        if let Some(lowest) = self.independent_transactions.get(&id.sender) &&
319            lowest.transaction.nonce() == id.nonce
320        {
321            self.independent_transactions.remove(&id.sender);
322            // mark the next as independent if it exists
323            if let Some(unlocked) = self.get(&id.descendant()) {
324                self.independent_transactions.insert(id.sender, unlocked.clone());
325            }
326        }
327
328        let tx = self.by_id.remove(id)?;
329        self.size_of -= tx.transaction.size();
330
331        match self.highest_nonces.entry(id.sender) {
332            Entry::Occupied(mut entry) => {
333                if entry.get().transaction.nonce() == id.nonce {
334                    // we just removed the tx with the highest nonce for this sender, find the
335                    // highest remaining tx from that sender
336                    if let Some((_, new_highest)) = self
337                        .by_id
338                        .range((
339                            id.sender.start_bound(),
340                            std::ops::Bound::Included(TransactionId::new(id.sender, u64::MAX)),
341                        ))
342                        .last()
343                    {
344                        // insert the new highest nonce for this sender
345                        entry.insert(new_highest.clone());
346                    } else {
347                        entry.remove();
348                    }
349                }
350            }
351            Entry::Vacant(_) => {
352                debug_assert!(
353                    false,
354                    "removed transaction without a tracked highest nonce {:?}",
355                    id
356                );
357            }
358        }
359
360        Some(tx.transaction)
361    }
362
363    const fn next_id(&mut self) -> u64 {
364        let id = self.submission_id;
365        self.submission_id = self.submission_id.wrapping_add(1);
366        id
367    }
368
369    /// Traverses the pool, starting at the highest nonce set, removing the transactions which
370    /// would put the pool under the specified limits.
371    ///
372    /// This attempts to remove transactions by roughly the same amount for each sender. This is
373    /// done by removing the highest-nonce transactions for each sender.
374    ///
375    /// If the `remove_locals` flag is unset, transactions will be removed per-sender until a
376    /// local transaction is the highest nonce transaction for that sender. If all senders have a
377    /// local highest-nonce transaction, the pool will not be truncated further.
378    ///
379    /// Otherwise, if the `remove_locals` flag is set, transactions will be removed per-sender
380    /// until the pool is under the given limits.
381    ///
382    /// Any removed transactions will be added to the `end_removed` vector.
383    pub fn remove_to_limit(
384        &mut self,
385        limit: &SubPoolLimit,
386        remove_locals: bool,
387        end_removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
388    ) {
389        // This serves as a termination condition for the loop - it represents the number of
390        // _valid_ unique senders that might have descendants in the pool.
391        //
392        // If `remove_locals` is false, a value of zero means that there are no non-local txs in the
393        // pool that can be removed.
394        //
395        // If `remove_locals` is true, a value of zero means that there are no txs in the pool that
396        // can be removed.
397        let mut non_local_senders = self.highest_nonces.len();
398
399        // keeps track of unique senders from previous iterations, to understand how many unique
400        // senders were removed in the last iteration
401        let mut unique_senders = self.highest_nonces.len();
402
403        // keeps track of which senders we've marked as local
404        let mut local_senders = FxHashSet::default();
405
406        // keep track of transactions to remove and how many have been removed so far
407        let original_length = self.len();
408        let mut removed = Vec::new();
409        let mut total_removed = 0;
410
411        // track total `size` of transactions to remove
412        let original_size = self.size();
413        let mut total_size = 0;
414
415        loop {
416            // check how many unique senders were removed last iteration
417            let unique_removed = unique_senders - self.highest_nonces.len();
418
419            // the new number of unique senders
420            unique_senders = self.highest_nonces.len();
421            non_local_senders -= unique_removed;
422
423            // we can reuse the temp array
424            removed.clear();
425
426            // we prefer removing transactions with lower ordering
427            let mut worst_transactions = self.highest_nonces.values().collect::<Vec<_>>();
428            worst_transactions.sort_unstable();
429
430            // loop through the highest nonces set, removing transactions until we reach the limit
431            for tx in worst_transactions {
432                // return early if the pool is under limits
433                if !limit.is_exceeded(original_length - total_removed, original_size - total_size) ||
434                    non_local_senders == 0
435                {
436                    // need to remove remaining transactions before exiting
437                    for id in &removed {
438                        if let Some(tx) = self.remove_transaction(id) {
439                            end_removed.push(tx);
440                        }
441                    }
442
443                    return
444                }
445
446                if !remove_locals && tx.transaction.is_local() {
447                    let sender_id = tx.transaction.sender_id();
448                    if local_senders.insert(sender_id) {
449                        non_local_senders -= 1;
450                    }
451                    continue
452                }
453
454                total_size += tx.transaction.size();
455                total_removed += 1;
456                removed.push(*tx.transaction.id());
457            }
458
459            // remove the transactions from this iteration
460            for id in &removed {
461                if let Some(tx) = self.remove_transaction(id) {
462                    end_removed.push(tx);
463                }
464            }
465
466            // return if either the pool is under limits or there are no more _eligible_
467            // transactions to remove
468            if !self.exceeds(limit) || non_local_senders == 0 {
469                return
470            }
471        }
472    }
473
474    /// Truncates the pool to the given [`SubPoolLimit`], removing transactions until the subpool
475    /// limits are met.
476    ///
477    /// This attempts to remove transactions by roughly the same amount for each sender. For more
478    /// information on this exact process see docs for
479    /// [`remove_to_limit`](PendingPool::remove_to_limit).
480    ///
481    /// This first truncates all of the non-local transactions in the pool. If the subpool is still
482    /// not under the limit, this truncates the entire pool, including non-local transactions. The
483    /// removed transactions are returned.
484    pub fn truncate_pool(
485        &mut self,
486        limit: SubPoolLimit,
487    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
488        let mut removed = Vec::new();
489        // return early if the pool is already under the limits
490        if !self.exceeds(&limit) {
491            return removed
492        }
493
494        // first truncate only non-local transactions, returning if the pool end up under the limit
495        self.remove_to_limit(&limit, false, &mut removed);
496        if !self.exceeds(&limit) {
497            return removed
498        }
499
500        // now repeat for local transactions, since local transactions must be removed now for the
501        // pool to be under the limit
502        self.remove_to_limit(&limit, true, &mut removed);
503
504        removed
505    }
506
507    /// Returns true if the pool exceeds the given limit
508    #[inline]
509    pub(crate) fn exceeds(&self, limit: &SubPoolLimit) -> bool {
510        limit.is_exceeded(self.len(), self.size())
511    }
512
513    /// The reported size of all transactions in this pool.
514    pub(crate) fn size(&self) -> usize {
515        self.size_of.into()
516    }
517
518    /// Number of transactions in the entire pool
519    pub(crate) fn len(&self) -> usize {
520        self.by_id.len()
521    }
522
523    /// All transactions grouped by id
524    pub const fn by_id(&self) -> &OrdMap<TransactionId, PendingTransaction<T>> {
525        &self.by_id
526    }
527
528    /// Independent transactions
529    pub const fn independent_transactions(&self) -> &FxHashMap<SenderId, PendingTransaction<T>> {
530        &self.independent_transactions
531    }
532
533    /// Subscribes to new transactions
534    pub fn new_transaction_receiver(&self) -> broadcast::Receiver<PendingTransaction<T>> {
535        self.new_transaction_notifier.subscribe()
536    }
537
538    /// Whether the pool is empty
539    #[cfg(test)]
540    pub(crate) fn is_empty(&self) -> bool {
541        self.by_id.is_empty()
542    }
543
544    /// Returns `true` if the transaction with the given id is already included in this pool.
545    pub(crate) fn contains(&self, id: &TransactionId) -> bool {
546        self.by_id.contains_key(id)
547    }
548
549    /// Get transactions by sender
550    pub(crate) fn get_txs_by_sender(&self, sender: SenderId) -> Vec<TransactionId> {
551        self.iter_txs_by_sender(sender).copied().collect()
552    }
553
554    /// Returns an iterator over all transaction with the sender id
555    pub(crate) fn iter_txs_by_sender(
556        &self,
557        sender: SenderId,
558    ) -> impl Iterator<Item = &TransactionId> + '_ {
559        self.by_id
560            .range((sender.start_bound(), Unbounded))
561            .take_while(move |(other, _)| sender == other.sender)
562            .map(|(tx_id, _)| tx_id)
563    }
564
565    /// Returns all transactions for the given sender, using a `BTree` range query.
566    pub(crate) fn txs_by_sender(
567        &self,
568        sender: SenderId,
569    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
570        self.by_id
571            .range((sender.start_bound(), Unbounded))
572            .take_while(move |(other, _)| sender == other.sender)
573            .map(|(_, tx)| tx.transaction.clone())
574            .collect()
575    }
576
577    /// Retrieves a transaction with the given ID from the pool, if it exists.
578    fn get(&self, id: &TransactionId) -> Option<&PendingTransaction<T>> {
579        self.by_id.get(id)
580    }
581
582    /// Returns a reference to the independent transactions in the pool
583    #[cfg(test)]
584    pub(crate) const fn independent(&self) -> &FxHashMap<SenderId, PendingTransaction<T>> {
585        &self.independent_transactions
586    }
587
588    /// Asserts that the bijection between `by_id` and `all` is valid.
589    #[cfg(any(test, feature = "test-utils"))]
590    pub(crate) fn assert_invariants(&self) {
591        assert!(
592            self.independent_transactions.len() <= self.by_id.len(),
593            "independent_transactions.len() > by_id.len()"
594        );
595        assert!(
596            self.highest_nonces.len() <= self.by_id.len(),
597            "highest_nonces.len() > by_id.len()"
598        );
599        assert_eq!(
600            self.highest_nonces.len(),
601            self.independent_transactions.len(),
602            "highest_nonces.len() != independent_transactions.len()"
603        );
604    }
605}
606
607/// A transaction that is ready to be included in a block.
608#[derive(Debug)]
609pub struct PendingTransaction<T: TransactionOrdering> {
610    /// Identifier that tags when transaction was submitted in the pool.
611    pub submission_id: u64,
612    /// Actual transaction.
613    pub transaction: Arc<ValidPoolTransaction<T::Transaction>>,
614    /// The priority value assigned by the used `Ordering` function.
615    pub priority: Priority<T::PriorityValue>,
616}
617
618impl<T: TransactionOrdering> PendingTransaction<T> {
619    /// The next transaction of the sender: `nonce + 1`
620    pub fn unlocks(&self) -> TransactionId {
621        self.transaction.transaction_id.descendant()
622    }
623}
624
625impl<T: TransactionOrdering> Clone for PendingTransaction<T> {
626    fn clone(&self) -> Self {
627        Self {
628            submission_id: self.submission_id,
629            transaction: Arc::clone(&self.transaction),
630            priority: self.priority.clone(),
631        }
632    }
633}
634
635impl<T: TransactionOrdering> Eq for PendingTransaction<T> {}
636
637impl<T: TransactionOrdering> PartialEq<Self> for PendingTransaction<T> {
638    fn eq(&self, other: &Self) -> bool {
639        self.cmp(other) == Ordering::Equal
640    }
641}
642
643impl<T: TransactionOrdering> PartialOrd<Self> for PendingTransaction<T> {
644    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
645        Some(self.cmp(other))
646    }
647}
648
649impl<T: TransactionOrdering> Ord for PendingTransaction<T> {
650    fn cmp(&self, other: &Self) -> Ordering {
651        // This compares by `priority` and only if two tx have the exact same priority this compares
652        // the unique `submission_id`. This ensures that transactions with same priority are not
653        // equal, so they're not replaced in the set
654        self.priority
655            .cmp(&other.priority)
656            .then_with(|| other.submission_id.cmp(&self.submission_id))
657    }
658}
659
660#[cfg(test)]
661mod tests {
662    use super::*;
663    use crate::{
664        test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
665        PoolTransaction,
666    };
667    use alloy_consensus::{Transaction, TxType};
668    use alloy_primitives::address;
669    use std::collections::HashSet;
670
671    #[test]
672    fn test_enforce_basefee() {
673        let mut f = MockTransactionFactory::default();
674        let mut pool = PendingPool::new(MockOrdering::default());
675        let tx = f.validated_arc(MockTransaction::eip1559().inc_price());
676        pool.add_transaction(tx.clone(), 0);
677
678        assert!(pool.contains(tx.id()));
679        assert_eq!(pool.len(), 1);
680
681        let removed = pool.update_base_fee(0);
682        assert!(removed.is_empty());
683
684        let removed = pool.update_base_fee((tx.max_fee_per_gas() + 1) as u64);
685        assert_eq!(removed.len(), 1);
686        assert!(pool.is_empty());
687    }
688
689    #[test]
690    fn test_enforce_basefee_descendant() {
691        let mut f = MockTransactionFactory::default();
692        let mut pool = PendingPool::new(MockOrdering::default());
693        let t = MockTransaction::eip1559().inc_price_by(10);
694        let root_tx = f.validated_arc(t.clone());
695        pool.add_transaction(root_tx.clone(), 0);
696
697        let descendant_tx = f.validated_arc(t.inc_nonce().decr_price());
698        pool.add_transaction(descendant_tx.clone(), 0);
699
700        assert!(pool.contains(root_tx.id()));
701        assert!(pool.contains(descendant_tx.id()));
702        assert_eq!(pool.len(), 2);
703
704        assert_eq!(pool.independent_transactions.len(), 1);
705        assert_eq!(pool.highest_nonces.len(), 1);
706
707        let removed = pool.update_base_fee(0);
708        assert!(removed.is_empty());
709
710        // two dependent tx in the pool with decreasing fee
711
712        {
713            let mut pool2 = pool.clone();
714            let removed = pool2.update_base_fee((descendant_tx.max_fee_per_gas() + 1) as u64);
715            assert_eq!(removed.len(), 1);
716            assert_eq!(pool2.len(), 1);
717            // descendant got popped
718            assert!(pool2.contains(root_tx.id()));
719            assert!(!pool2.contains(descendant_tx.id()));
720        }
721
722        // remove root transaction via fee
723        let removed = pool.update_base_fee((root_tx.max_fee_per_gas() + 1) as u64);
724        assert_eq!(removed.len(), 2);
725        assert!(pool.is_empty());
726        pool.assert_invariants();
727    }
728
729    #[test]
730    fn evict_worst() {
731        let mut f = MockTransactionFactory::default();
732        let mut pool = PendingPool::new(MockOrdering::default());
733
734        let t = MockTransaction::eip1559();
735        pool.add_transaction(f.validated_arc(t.clone()), 0);
736
737        let t2 = MockTransaction::eip1559().inc_price_by(10);
738        pool.add_transaction(f.validated_arc(t2), 0);
739
740        // First transaction should be evicted.
741        assert_eq!(
742            pool.highest_nonces.values().min().map(|tx| *tx.transaction.hash()),
743            Some(*t.hash())
744        );
745
746        // truncate pool with max size = 1, ensure it's the same transaction
747        let removed = pool.truncate_pool(SubPoolLimit { max_txs: 1, max_size: usize::MAX });
748        assert_eq!(removed.len(), 1);
749        assert_eq!(removed[0].hash(), t.hash());
750    }
751
752    #[test]
753    fn correct_independent_descendants() {
754        // this test ensures that we set the right highest nonces set for each sender
755        let mut f = MockTransactionFactory::default();
756        let mut pool = PendingPool::new(MockOrdering::default());
757
758        let a_sender = address!("0x000000000000000000000000000000000000000a");
759        let b_sender = address!("0x000000000000000000000000000000000000000b");
760        let c_sender = address!("0x000000000000000000000000000000000000000c");
761        let d_sender = address!("0x000000000000000000000000000000000000000d");
762
763        // create a chain of transactions by sender A, B, C
764        let mut tx_set = MockTransactionSet::dependent(a_sender, 0, 4, TxType::Eip1559);
765        let a = tx_set.clone().into_vec();
766
767        let b = MockTransactionSet::dependent(b_sender, 0, 3, TxType::Eip1559).into_vec();
768        tx_set.extend(b.clone());
769
770        // C has the same number of txs as B
771        let c = MockTransactionSet::dependent(c_sender, 0, 3, TxType::Eip1559).into_vec();
772        tx_set.extend(c.clone());
773
774        let d = MockTransactionSet::dependent(d_sender, 0, 1, TxType::Eip1559).into_vec();
775        tx_set.extend(d.clone());
776
777        // add all the transactions to the pool
778        let all_txs = tx_set.into_vec();
779        for tx in all_txs {
780            pool.add_transaction(f.validated_arc(tx), 0);
781        }
782
783        pool.assert_invariants();
784
785        // the independent set is the roots of each of these tx chains, these are the highest
786        // nonces for each sender
787        let expected_highest_nonces = [d[0].clone(), c[2].clone(), b[2].clone(), a[3].clone()]
788            .iter()
789            .map(|tx| (tx.sender(), tx.nonce()))
790            .collect::<HashSet<_>>();
791        let actual_highest_nonces = pool
792            .highest_nonces
793            .values()
794            .map(|tx| (tx.transaction.sender(), tx.transaction.nonce()))
795            .collect::<HashSet<_>>();
796        assert_eq!(expected_highest_nonces, actual_highest_nonces);
797        pool.assert_invariants();
798    }
799
800    #[test]
801    fn truncate_by_sender() {
802        // This test ensures that transactions are removed from the pending pool by sender.
803        let mut f = MockTransactionFactory::default();
804        let mut pool = PendingPool::new(MockOrdering::default());
805
806        // Addresses for simulated senders A, B, C, and D.
807        let a = address!("0x000000000000000000000000000000000000000a");
808        let b = address!("0x000000000000000000000000000000000000000b");
809        let c = address!("0x000000000000000000000000000000000000000c");
810        let d = address!("0x000000000000000000000000000000000000000d");
811
812        // Create transaction chains for senders A, B, C, and D.
813        let a_txs = MockTransactionSet::sequential_transactions_by_sender(a, 4, TxType::Eip1559);
814        let b_txs = MockTransactionSet::sequential_transactions_by_sender(b, 3, TxType::Eip1559);
815        let c_txs = MockTransactionSet::sequential_transactions_by_sender(c, 3, TxType::Eip1559);
816        let d_txs = MockTransactionSet::sequential_transactions_by_sender(d, 1, TxType::Eip1559);
817
818        // Set up expected pending transactions.
819        let expected_pending = vec![
820            a_txs.transactions[0].clone(),
821            b_txs.transactions[0].clone(),
822            c_txs.transactions[0].clone(),
823            a_txs.transactions[1].clone(),
824        ]
825        .into_iter()
826        .map(|tx| (tx.sender(), tx.nonce()))
827        .collect::<HashSet<_>>();
828
829        // Set up expected removed transactions.
830        let expected_removed = vec![
831            d_txs.transactions[0].clone(),
832            c_txs.transactions[2].clone(),
833            b_txs.transactions[2].clone(),
834            a_txs.transactions[3].clone(),
835            c_txs.transactions[1].clone(),
836            b_txs.transactions[1].clone(),
837            a_txs.transactions[2].clone(),
838        ]
839        .into_iter()
840        .map(|tx| (tx.sender(), tx.nonce()))
841        .collect::<HashSet<_>>();
842
843        // Consolidate all transactions into a single vector.
844        let all_txs =
845            [a_txs.into_vec(), b_txs.into_vec(), c_txs.into_vec(), d_txs.into_vec()].concat();
846
847        // Add all the transactions to the pool.
848        for tx in all_txs {
849            pool.add_transaction(f.validated_arc(tx), 0);
850        }
851
852        // Sanity check, ensuring everything is consistent.
853        pool.assert_invariants();
854
855        // Define the maximum total transactions to be 4, removing transactions for each sender.
856        // Expected order of removal:
857        // * d1, c3, b3, a4
858        // * c2, b2, a3
859        //
860        // Remaining transactions:
861        // * a1, a2
862        // * b1
863        // * c1
864        let pool_limit = SubPoolLimit { max_txs: 4, max_size: usize::MAX };
865
866        // Truncate the pool based on the defined limit.
867        let removed = pool.truncate_pool(pool_limit);
868        pool.assert_invariants();
869        assert_eq!(removed.len(), expected_removed.len());
870
871        // Get the set of removed transactions and compare with the expected set.
872        let removed =
873            removed.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::<HashSet<_>>();
874        assert_eq!(removed, expected_removed);
875
876        // Retrieve the current pending transactions after truncation.
877        let pending = pool.all().collect::<Vec<_>>();
878        assert_eq!(pending.len(), expected_pending.len());
879
880        // Get the set of pending transactions and compare with the expected set.
881        let pending =
882            pending.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::<HashSet<_>>();
883        assert_eq!(pending, expected_pending);
884    }
885
886    // <https://github.com/paradigmxyz/reth/issues/12340>
887    #[test]
888    fn test_eligible_updates_promoted() {
889        let mut pool = PendingPool::new(MockOrdering::default());
890        let mut f = MockTransactionFactory::default();
891
892        let num_senders = 10;
893
894        let first_txs: Vec<_> = (0..num_senders) //
895            .map(|_| MockTransaction::eip1559())
896            .collect();
897        let second_txs: Vec<_> =
898            first_txs.iter().map(|tx| tx.clone().rng_hash().inc_nonce()).collect();
899
900        for tx in first_txs {
901            let valid_tx = f.validated(tx);
902            pool.add_transaction(Arc::new(valid_tx), 0);
903        }
904
905        let mut best = pool.best();
906
907        for _ in 0..num_senders {
908            if let Some(tx) = best.next() {
909                assert_eq!(tx.nonce(), 0);
910            } else {
911                panic!("cannot read one of first_txs");
912            }
913        }
914
915        for tx in second_txs {
916            let valid_tx = f.validated(tx);
917            pool.add_transaction(Arc::new(valid_tx), 0);
918        }
919
920        for _ in 0..num_senders {
921            if let Some(tx) = best.next() {
922                assert_eq!(tx.nonce(), 1);
923            } else {
924                panic!("cannot read one of second_txs");
925            }
926        }
927    }
928
929    #[test]
930    fn test_empty_pool_behavior() {
931        let mut pool = PendingPool::<MockOrdering>::new(MockOrdering::default());
932
933        // Ensure the pool is empty
934        assert!(pool.is_empty());
935        assert_eq!(pool.len(), 0);
936        assert_eq!(pool.size(), 0);
937
938        // Verify that attempting to truncate an empty pool does not panic and returns an empty vec
939        let removed = pool.truncate_pool(SubPoolLimit { max_txs: 10, max_size: 1000 });
940        assert!(removed.is_empty());
941
942        // Verify that retrieving transactions from an empty pool yields nothing
943        assert!(pool.all().next().is_none());
944    }
945
946    #[test]
947    fn test_add_remove_transaction() {
948        let mut f = MockTransactionFactory::default();
949        let mut pool = PendingPool::new(MockOrdering::default());
950
951        // Add a transaction and check if it's in the pool
952        let tx = f.validated_arc(MockTransaction::eip1559());
953        pool.add_transaction(tx.clone(), 0);
954        assert!(pool.contains(tx.id()));
955        assert_eq!(pool.len(), 1);
956
957        // Remove the transaction and ensure it's no longer in the pool
958        let removed_tx = pool.remove_transaction(tx.id()).unwrap();
959        assert_eq!(removed_tx.id(), tx.id());
960        assert!(!pool.contains(tx.id()));
961        assert_eq!(pool.len(), 0);
962    }
963
964    #[test]
965    fn test_reorder_on_basefee_update() {
966        let mut f = MockTransactionFactory::default();
967        let mut pool = PendingPool::new(MockOrdering::default());
968
969        // Add two transactions with different fees
970        let tx1 = f.validated_arc(MockTransaction::eip1559().inc_price());
971        let tx2 = f.validated_arc(MockTransaction::eip1559().inc_price_by(20));
972        pool.add_transaction(tx1.clone(), 0);
973        pool.add_transaction(tx2.clone(), 0);
974
975        // Ensure the transactions are in the correct order
976        let mut best = pool.best();
977        assert_eq!(best.next().unwrap().hash(), tx2.hash());
978        assert_eq!(best.next().unwrap().hash(), tx1.hash());
979
980        // Update the base fee to a value higher than tx1's fee, causing it to be removed
981        let removed = pool.update_base_fee((tx1.max_fee_per_gas() + 1) as u64);
982        assert_eq!(removed.len(), 1);
983        assert_eq!(removed[0].hash(), tx1.hash());
984
985        // Verify that only tx2 remains in the pool
986        assert_eq!(pool.len(), 1);
987        assert!(pool.contains(tx2.id()));
988        assert!(!pool.contains(tx1.id()));
989    }
990
991    #[test]
992    #[cfg(debug_assertions)]
993    #[should_panic(expected = "transaction already included")]
994    fn test_handle_duplicates() {
995        let mut f = MockTransactionFactory::default();
996        let mut pool = PendingPool::new(MockOrdering::default());
997
998        // Add the same transaction twice and ensure it only appears once
999        let tx = f.validated_arc(MockTransaction::eip1559());
1000        pool.add_transaction(tx.clone(), 0);
1001        assert!(pool.contains(tx.id()));
1002        assert_eq!(pool.len(), 1);
1003
1004        // Attempt to add the same transaction again, which should be ignored
1005        pool.add_transaction(tx, 0);
1006    }
1007
1008    #[test]
1009    fn test_update_blob_fee() {
1010        let mut f = MockTransactionFactory::default();
1011        let mut pool = PendingPool::new(MockOrdering::default());
1012
1013        // Add transactions with varying blob fees
1014        let tx1 = f.validated_arc(MockTransaction::eip4844().set_blob_fee(50).clone());
1015        let tx2 = f.validated_arc(MockTransaction::eip4844().set_blob_fee(150).clone());
1016        pool.add_transaction(tx1.clone(), 0);
1017        pool.add_transaction(tx2.clone(), 0);
1018
1019        // Update the blob fee to a value that causes tx1 to be removed
1020        let removed = pool.update_blob_fee(100);
1021        assert_eq!(removed.len(), 1);
1022        assert_eq!(removed[0].hash(), tx1.hash());
1023
1024        // Verify that only tx2 remains in the pool
1025        assert!(pool.contains(tx2.id()));
1026        assert!(!pool.contains(tx1.id()));
1027    }
1028
1029    #[test]
1030    fn local_senders_tracking() {
1031        let mut f = MockTransactionFactory::default();
1032        let mut pool = PendingPool::new(MockOrdering::default());
1033
1034        // Addresses for simulated senders A, B, C
1035        let a = address!("0x000000000000000000000000000000000000000a");
1036        let b = address!("0x000000000000000000000000000000000000000b");
1037        let c = address!("0x000000000000000000000000000000000000000c");
1038
1039        // sender A (local) - 11+ transactions (large enough to keep limit exceeded)
1040        // sender B (external) - 2 transactions
1041        // sender C (external) - 2 transactions
1042
1043        // Create transaction chains for senders A, B, C
1044        let a_txs = MockTransactionSet::sequential_transactions_by_sender(a, 11, TxType::Eip1559);
1045        let b_txs = MockTransactionSet::sequential_transactions_by_sender(b, 2, TxType::Eip1559);
1046        let c_txs = MockTransactionSet::sequential_transactions_by_sender(c, 2, TxType::Eip1559);
1047
1048        // create local txs for sender A
1049        for tx in a_txs.into_vec() {
1050            let final_tx = Arc::new(f.validated_with_origin(crate::TransactionOrigin::Local, tx));
1051
1052            pool.add_transaction(final_tx, 0);
1053        }
1054
1055        // create external txs for senders B and C
1056        let remaining_txs = [b_txs.into_vec(), c_txs.into_vec()].concat();
1057        for tx in remaining_txs {
1058            let final_tx = f.validated_arc(tx);
1059
1060            pool.add_transaction(final_tx, 0);
1061        }
1062
1063        // Sanity check, ensuring everything is consistent.
1064        pool.assert_invariants();
1065
1066        let pool_limit = SubPoolLimit { max_txs: 10, max_size: usize::MAX };
1067        pool.truncate_pool(pool_limit);
1068
1069        let sender_a = f.ids.sender_id(&a).unwrap();
1070        let sender_b = f.ids.sender_id(&b).unwrap();
1071        let sender_c = f.ids.sender_id(&c).unwrap();
1072
1073        assert_eq!(pool.get_txs_by_sender(sender_a).len(), 10);
1074        assert!(pool.get_txs_by_sender(sender_b).is_empty());
1075        assert!(pool.get_txs_by_sender(sender_c).is_empty());
1076    }
1077
1078    #[test]
1079    fn test_remove_non_highest_keeps_highest() {
1080        let mut f = MockTransactionFactory::default();
1081        let mut pool = PendingPool::new(MockOrdering::default());
1082        let sender = address!("0x00000000000000000000000000000000000000aa");
1083        let txs = MockTransactionSet::dependent(sender, 0, 3, TxType::Eip1559).into_vec();
1084        for tx in txs {
1085            pool.add_transaction(f.validated_arc(tx), 0);
1086        }
1087        pool.assert_invariants();
1088        let sender_id = f.ids.sender_id(&sender).unwrap();
1089        let mid_id = TransactionId::new(sender_id, 1);
1090        let _ = pool.remove_transaction(&mid_id);
1091        let highest = pool.highest_nonces.get(&sender_id).unwrap();
1092        assert_eq!(highest.transaction.nonce(), 2);
1093        pool.assert_invariants();
1094    }
1095
1096    #[test]
1097    fn test_cascade_removal_recomputes_highest() {
1098        let mut f = MockTransactionFactory::default();
1099        let mut pool = PendingPool::new(MockOrdering::default());
1100        let sender = address!("0x00000000000000000000000000000000000000bb");
1101        let txs = MockTransactionSet::dependent(sender, 0, 4, TxType::Eip1559).into_vec();
1102        for tx in txs {
1103            pool.add_transaction(f.validated_arc(tx), 0);
1104        }
1105        pool.assert_invariants();
1106        let sender_id = f.ids.sender_id(&sender).unwrap();
1107        let id3 = TransactionId::new(sender_id, 3);
1108        let _ = pool.remove_transaction(&id3);
1109        let highest = pool.highest_nonces.get(&sender_id).unwrap();
1110        assert_eq!(highest.transaction.nonce(), 2);
1111        let id2 = TransactionId::new(sender_id, 2);
1112        let _ = pool.remove_transaction(&id2);
1113        let highest = pool.highest_nonces.get(&sender_id).unwrap();
1114        assert_eq!(highest.transaction.nonce(), 1);
1115        pool.assert_invariants();
1116    }
1117
1118    #[test]
1119    fn test_remove_only_tx_clears_highest() {
1120        let mut f = MockTransactionFactory::default();
1121        let mut pool = PendingPool::new(MockOrdering::default());
1122        let sender = address!("0x00000000000000000000000000000000000000cc");
1123        let txs = MockTransactionSet::dependent(sender, 0, 1, TxType::Eip1559).into_vec();
1124        for tx in txs {
1125            pool.add_transaction(f.validated_arc(tx), 0);
1126        }
1127        pool.assert_invariants();
1128        let sender_id = f.ids.sender_id(&sender).unwrap();
1129        let id0 = TransactionId::new(sender_id, 0);
1130        let _ = pool.remove_transaction(&id0);
1131        assert!(!pool.highest_nonces.contains_key(&sender_id));
1132        pool.assert_invariants();
1133    }
1134}