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