Skip to main content

reth_transaction_pool/pool/
parked.rs

1use crate::{
2    identifier::{SenderId, TransactionId},
3    pool::size::SizeTracker,
4    PoolTransaction, SubPoolLimit, ValidPoolTransaction, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
5};
6use rustc_hash::FxHashMap;
7use smallvec::SmallVec;
8use std::{
9    cmp::Ordering,
10    collections::{hash_map::Entry, BTreeMap, BTreeSet},
11    ops::{Bound::Unbounded, Deref},
12    sync::Arc,
13};
14
15/// A pool of transactions that are currently parked and are waiting for external changes (e.g.
16/// basefee, ancestor transactions, balance) that eventually move the transaction into the pending
17/// pool.
18///
19/// Note: This type is generic over [`ParkedPool`] which enforces that the underlying transaction
20/// type is [`ValidPoolTransaction`] wrapped in an [Arc].
21#[derive(Debug, Clone)]
22pub struct ParkedPool<T: ParkedOrd> {
23    /// Keeps track of transactions inserted in the pool.
24    ///
25    /// This way we can determine when transactions were submitted to the pool.
26    submission_id: u64,
27    /// _All_ Transactions that are currently inside the pool grouped by their identifier.
28    by_id: BTreeMap<TransactionId, ParkedPoolTransaction<T>>,
29    /// Keeps track of last submission id for each sender.
30    ///
31    /// This are sorted in reverse order, so the last (highest) submission id is first, and the
32    /// lowest (oldest) is the last.
33    last_sender_submission: BTreeSet<SubmissionSenderId>,
34    /// Keeps track of the number of transactions in the pool by the sender and the last submission
35    /// id.
36    sender_transaction_count: FxHashMap<SenderId, SenderTransactionCount>,
37    /// Keeps track of the size of this pool.
38    ///
39    /// See also [`reth_primitives_traits::InMemorySize::size`].
40    size_of: SizeTracker,
41}
42
43// === impl ParkedPool ===
44
45impl<T: ParkedOrd> ParkedPool<T> {
46    /// Adds a new transactions to the pending queue.
47    pub fn add_transaction(&mut self, tx: Arc<ValidPoolTransaction<T::Transaction>>) {
48        let id = *tx.id();
49        debug_assert!(
50            !self.contains(&id),
51            "transaction already included {:?}",
52            self.get(&id).unwrap().transaction.transaction
53        );
54        let submission_id = self.next_id();
55
56        // keep track of size
57        self.size_of += tx.size();
58
59        // update or create sender entry
60        self.add_sender_count(tx.sender_id(), submission_id);
61        let transaction = ParkedPoolTransaction { submission_id, transaction: tx.into() };
62
63        self.by_id.insert(id, transaction);
64    }
65
66    /// Increments the count of transactions for the given sender and updates the tracked submission
67    /// id.
68    fn add_sender_count(&mut self, sender: SenderId, submission_id: u64) {
69        match self.sender_transaction_count.entry(sender) {
70            Entry::Occupied(mut entry) => {
71                let value = entry.get_mut();
72                // remove the __currently__ tracked submission id
73                self.last_sender_submission
74                    .remove(&SubmissionSenderId::new(sender, value.last_submission_id));
75
76                value.count += 1;
77                value.last_submission_id = submission_id;
78            }
79            Entry::Vacant(entry) => {
80                entry
81                    .insert(SenderTransactionCount { count: 1, last_submission_id: submission_id });
82            }
83        }
84        // insert a new entry
85        self.last_sender_submission.insert(SubmissionSenderId::new(sender, submission_id));
86    }
87
88    /// Decrements the count of transactions for the given sender.
89    ///
90    /// If the count reaches zero, the sender is removed from the map.
91    ///
92    /// Note: this does not update the tracked submission id for the sender, because we're only
93    /// interested in the __last__ submission id when truncating the pool.
94    fn remove_sender_count(&mut self, sender_id: SenderId) {
95        let removed_sender = match self.sender_transaction_count.entry(sender_id) {
96            Entry::Occupied(mut entry) => {
97                let value = entry.get_mut();
98                value.count -= 1;
99                if value.count == 0 {
100                    entry.remove()
101                } else {
102                    return
103                }
104            }
105            Entry::Vacant(_) => {
106                // This should never happen because the bisection between the two maps
107                unreachable!("sender count not found {:?}", sender_id);
108            }
109        };
110
111        // all transactions for this sender have been removed
112        assert!(
113            self.last_sender_submission
114                .remove(&SubmissionSenderId::new(sender_id, removed_sender.last_submission_id)),
115            "last sender transaction not found {sender_id:?}"
116        );
117    }
118
119    /// Returns an iterator over all transactions in the pool
120    pub(crate) fn all(
121        &self,
122    ) -> impl ExactSizeIterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
123        self.by_id.values().map(|tx| tx.transaction.clone().into())
124    }
125
126    /// Removes the transaction from the pool
127    pub(crate) fn remove_transaction(
128        &mut self,
129        id: &TransactionId,
130    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
131        // remove from queues
132        let tx = self.by_id.remove(id)?;
133        self.remove_sender_count(tx.transaction.sender_id());
134
135        // keep track of size
136        self.size_of -= tx.transaction.size();
137
138        Some(tx.transaction.into())
139    }
140
141    /// Retrieves transactions by sender, using `SmallVec` to efficiently handle up to
142    /// `TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER` transactions.
143    pub(crate) fn get_txs_by_sender(
144        &self,
145        sender: SenderId,
146    ) -> SmallVec<[TransactionId; TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER]> {
147        self.by_id
148            .range((sender.start_bound(), Unbounded))
149            .take_while(move |(other, _)| sender == other.sender)
150            .map(|(tx_id, _)| *tx_id)
151            .collect()
152    }
153
154    /// Returns all transactions for the given sender, using a `BTree` range query.
155    pub(crate) fn txs_by_sender(
156        &self,
157        sender: SenderId,
158    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
159        self.by_id
160            .range((sender.start_bound(), Unbounded))
161            .take_while(move |(other, _)| sender == other.sender)
162            .map(|(_, tx)| Arc::clone(&tx.transaction))
163            .collect()
164    }
165
166    #[cfg(test)]
167    pub(crate) fn get_senders_by_submission_id(
168        &self,
169    ) -> impl Iterator<Item = SubmissionSenderId> + '_ {
170        self.last_sender_submission.iter().copied()
171    }
172
173    /// Truncates the pool by removing transactions, until the given [`SubPoolLimit`] has been met.
174    ///
175    /// This is done by first ordering senders by the last time they have submitted a transaction
176    ///
177    /// Uses sender ids sorted by each sender's last submission id. Senders with older last
178    /// submission ids are first. Note that _last_ submission ids are the newest submission id for
179    /// that sender, so this sorts senders by the last time they submitted a transaction in
180    /// descending order. Senders that have least recently submitted a transaction are first.
181    ///
182    /// Then, for each sender, all transactions for that sender are removed, until the pool limits
183    /// have been met.
184    ///
185    /// Any removed transactions are returned.
186    pub fn truncate_pool(
187        &mut self,
188        limit: SubPoolLimit,
189    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
190        if !self.exceeds(&limit) {
191            // if we are below the limits, we don't need to drop anything
192            return Vec::new()
193        }
194
195        let mut removed = Vec::with_capacity(limit.tx_excess(self.len()).unwrap_or(1));
196
197        while !self.last_sender_submission.is_empty() && limit.is_exceeded(self.len(), self.size())
198        {
199            // NOTE: This will not panic due to `!last_sender_transaction.is_empty()`
200            let sender_id = self.last_sender_submission.last().unwrap().sender_id;
201
202            // Drop transactions from this sender until the pool is under limits
203            while let Some((tx_id, _)) = self.by_id.range(sender_id.range()).next_back() {
204                let tx_id = *tx_id;
205                if let Some(tx) = self.remove_transaction(&tx_id) {
206                    removed.push(tx);
207                }
208
209                if !self.exceeds(&limit) {
210                    break
211                }
212            }
213        }
214
215        removed
216    }
217
218    const fn next_id(&mut self) -> u64 {
219        let id = self.submission_id;
220        self.submission_id = self.submission_id.wrapping_add(1);
221        id
222    }
223
224    /// The reported size of all transactions in this pool.
225    pub(crate) fn size(&self) -> usize {
226        self.size_of.into()
227    }
228
229    /// Number of transactions in the entire pool
230    pub(crate) fn len(&self) -> usize {
231        self.by_id.len()
232    }
233
234    /// Returns true if the pool exceeds the given limit
235    #[inline]
236    pub(crate) fn exceeds(&self, limit: &SubPoolLimit) -> bool {
237        limit.is_exceeded(self.len(), self.size())
238    }
239
240    /// Returns whether the pool is empty
241    #[cfg(test)]
242    pub(crate) fn is_empty(&self) -> bool {
243        self.by_id.is_empty()
244    }
245
246    /// Returns `true` if the transaction with the given id is already included in this pool.
247    pub(crate) fn contains(&self, id: &TransactionId) -> bool {
248        self.by_id.contains_key(id)
249    }
250
251    /// Retrieves a transaction with the given ID from the pool, if it exists.
252    fn get(&self, id: &TransactionId) -> Option<&ParkedPoolTransaction<T>> {
253        self.by_id.get(id)
254    }
255
256    /// Asserts that all subpool invariants
257    #[cfg(any(test, feature = "test-utils"))]
258    pub(crate) fn assert_invariants(&self) {
259        assert_eq!(
260            self.last_sender_submission.len(),
261            self.sender_transaction_count.len(),
262            "last_sender_submission.len() != sender_transaction_count.len()"
263        );
264    }
265}
266
267impl<T: PoolTransaction> ParkedPool<BasefeeOrd<T>> {
268    /// Returns all transactions that satisfy the given basefee.
269    ///
270    /// Note: this does _not_ remove the transactions
271    pub(crate) fn satisfy_base_fee_transactions(
272        &self,
273        basefee: u64,
274    ) -> Vec<Arc<ValidPoolTransaction<T>>> {
275        let mut txs = Vec::new();
276        self.satisfy_base_fee_ids(basefee as u128, |tx| {
277            txs.push(tx.clone());
278        });
279        txs
280    }
281
282    /// Returns all transactions that satisfy the given basefee.
283    fn satisfy_base_fee_ids<F>(&self, basefee: u128, mut tx_handler: F)
284    where
285        F: FnMut(&Arc<ValidPoolTransaction<T>>),
286    {
287        let mut iter = self.by_id.iter().peekable();
288
289        while let Some((id, tx)) = iter.next() {
290            if tx.transaction.max_fee_per_gas() < basefee {
291                // still parked -> skip descendant transactions
292                'this: while let Some((peek, _)) = iter.peek() {
293                    if peek.sender != id.sender {
294                        break 'this
295                    }
296                    iter.next();
297                }
298            } else {
299                tx_handler(&tx.transaction);
300            }
301        }
302    }
303
304    /// Removes all transactions from this subpool that can afford the given basefee,
305    /// invoking the provided handler for each transaction as it is removed.
306    ///
307    /// This method enforces the basefee constraint by identifying transactions that now
308    /// satisfy the basefee requirement (typically after a basefee decrease) and processing
309    /// them via the provided transaction handler closure.
310    ///
311    /// Respects per-sender nonce ordering: if the lowest-nonce transaction for a sender
312    /// still cannot afford the basefee, higher-nonce transactions from that sender are skipped.
313    ///
314    /// Note: the transactions are not returned in a particular order.
315    pub(crate) fn enforce_basefee_with<F>(&mut self, basefee: u64, mut tx_handler: F)
316    where
317        F: FnMut(Arc<ValidPoolTransaction<T>>),
318    {
319        let mut to_remove = Vec::new();
320        self.satisfy_base_fee_ids(basefee as u128, |tx| {
321            to_remove.push(*tx.id());
322        });
323
324        for id in to_remove {
325            if let Some(tx) = self.remove_transaction(&id) {
326                tx_handler(tx);
327            }
328        }
329    }
330
331    /// Removes all transactions and their dependent transaction from the subpool that no longer
332    /// satisfy the given basefee.
333    ///
334    /// Legacy method maintained for compatibility with read-only queries.
335    /// For basefee enforcement, prefer `enforce_basefee_with` for better performance.
336    ///
337    /// Note: the transactions are not returned in a particular order.
338    #[cfg(test)]
339    pub(crate) fn enforce_basefee(&mut self, basefee: u64) -> Vec<Arc<ValidPoolTransaction<T>>> {
340        let mut removed = Vec::new();
341        self.enforce_basefee_with(basefee, |tx| {
342            removed.push(tx);
343        });
344        removed
345    }
346}
347
348impl<T: ParkedOrd> Default for ParkedPool<T> {
349    fn default() -> Self {
350        Self {
351            submission_id: 0,
352            by_id: Default::default(),
353            last_sender_submission: Default::default(),
354            sender_transaction_count: Default::default(),
355            size_of: Default::default(),
356        }
357    }
358}
359
360/// Keeps track of the number of transactions and the latest submission id for each sender.
361#[derive(Debug, Clone, Default, PartialEq, Eq)]
362struct SenderTransactionCount {
363    count: u64,
364    last_submission_id: u64,
365}
366
367/// Represents a transaction in this pool.
368#[derive(Debug)]
369struct ParkedPoolTransaction<T: ParkedOrd> {
370    /// Identifier that tags when transaction was submitted in the pool.
371    submission_id: u64,
372    /// Actual transaction.
373    transaction: T,
374}
375
376impl<T: ParkedOrd> Clone for ParkedPoolTransaction<T> {
377    fn clone(&self) -> Self {
378        Self { submission_id: self.submission_id, transaction: self.transaction.clone() }
379    }
380}
381
382impl<T: ParkedOrd> Eq for ParkedPoolTransaction<T> {}
383
384impl<T: ParkedOrd> PartialEq<Self> for ParkedPoolTransaction<T> {
385    fn eq(&self, other: &Self) -> bool {
386        self.cmp(other) == Ordering::Equal
387    }
388}
389
390impl<T: ParkedOrd> PartialOrd<Self> for ParkedPoolTransaction<T> {
391    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
392        Some(self.cmp(other))
393    }
394}
395
396impl<T: ParkedOrd> Ord for ParkedPoolTransaction<T> {
397    fn cmp(&self, other: &Self) -> Ordering {
398        // This compares by the transactions first, and only if two tx are equal this compares
399        // the unique `submission_id`.
400        // "better" transactions are Greater
401        self.transaction
402            .cmp(&other.transaction)
403            .then_with(|| other.submission_id.cmp(&self.submission_id))
404    }
405}
406
407/// Includes a [`SenderId`] and `submission_id`. This is used to sort senders by their last
408/// submission id.
409#[derive(Debug, PartialEq, Eq, Copy, Clone)]
410pub(crate) struct SubmissionSenderId {
411    /// The sender id
412    pub(crate) sender_id: SenderId,
413    /// The submission id
414    pub(crate) submission_id: u64,
415}
416
417impl SubmissionSenderId {
418    /// Creates a new [`SubmissionSenderId`] based on the [`SenderId`] and `submission_id`.
419    const fn new(sender_id: SenderId, submission_id: u64) -> Self {
420        Self { sender_id, submission_id }
421    }
422}
423
424impl Ord for SubmissionSenderId {
425    fn cmp(&self, other: &Self) -> Ordering {
426        // Reverse ordering for `submission_id`
427        other.submission_id.cmp(&self.submission_id)
428    }
429}
430
431impl PartialOrd for SubmissionSenderId {
432    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
433        Some(self.cmp(other))
434    }
435}
436
437/// Helper trait used for custom `Ord` wrappers around a transaction.
438///
439/// This is effectively a wrapper for `Arc<ValidPoolTransaction>` with custom `Ord` implementation.
440pub trait ParkedOrd:
441    Ord
442    + Clone
443    + From<Arc<ValidPoolTransaction<Self::Transaction>>>
444    + Into<Arc<ValidPoolTransaction<Self::Transaction>>>
445    + Deref<Target = Arc<ValidPoolTransaction<Self::Transaction>>>
446{
447    /// The wrapper transaction type.
448    type Transaction: PoolTransaction;
449}
450
451/// Helper macro to implement necessary conversions for `ParkedOrd` trait
452macro_rules! impl_ord_wrapper {
453    ($name:ident) => {
454        impl<T: PoolTransaction> Clone for $name<T> {
455            fn clone(&self) -> Self {
456                Self(self.0.clone())
457            }
458        }
459
460        impl<T: PoolTransaction> Eq for $name<T> {}
461
462        impl<T: PoolTransaction> PartialEq<Self> for $name<T> {
463            fn eq(&self, other: &Self) -> bool {
464                self.cmp(other) == Ordering::Equal
465            }
466        }
467
468        impl<T: PoolTransaction> PartialOrd<Self> for $name<T> {
469            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
470                Some(self.cmp(other))
471            }
472        }
473        impl<T: PoolTransaction> Deref for $name<T> {
474            type Target = Arc<ValidPoolTransaction<T>>;
475
476            fn deref(&self) -> &Self::Target {
477                &self.0
478            }
479        }
480
481        impl<T: PoolTransaction> ParkedOrd for $name<T> {
482            type Transaction = T;
483        }
484
485        impl<T: PoolTransaction> From<Arc<ValidPoolTransaction<T>>> for $name<T> {
486            fn from(value: Arc<ValidPoolTransaction<T>>) -> Self {
487                Self(value)
488            }
489        }
490
491        impl<T: PoolTransaction> From<$name<T>> for Arc<ValidPoolTransaction<T>> {
492            fn from(value: $name<T>) -> Arc<ValidPoolTransaction<T>> {
493                value.0
494            }
495        }
496    };
497}
498
499/// A new type wrapper for [`ValidPoolTransaction`]
500///
501/// This sorts transactions by their base fee.
502///
503/// Caution: This assumes all transaction in the `BaseFee` sub-pool have a fee value.
504#[derive(Debug)]
505pub struct BasefeeOrd<T: PoolTransaction>(Arc<ValidPoolTransaction<T>>);
506
507impl_ord_wrapper!(BasefeeOrd);
508
509impl<T: PoolTransaction> Ord for BasefeeOrd<T> {
510    fn cmp(&self, other: &Self) -> Ordering {
511        self.0.transaction.max_fee_per_gas().cmp(&other.0.transaction.max_fee_per_gas())
512    }
513}
514
515/// A new type wrapper for [`ValidPoolTransaction`]
516///
517/// This sorts transactions by their distance.
518///
519/// `Queued` transactions are transactions that are currently blocked by other parked (basefee,
520/// queued) or missing transactions.
521///
522/// The primary order function always compares the transaction costs first. In case these
523/// are equal, it compares the timestamps when the transactions were created.
524#[derive(Debug)]
525pub struct QueuedOrd<T: PoolTransaction>(Arc<ValidPoolTransaction<T>>);
526
527impl_ord_wrapper!(QueuedOrd);
528
529impl<T: PoolTransaction> Ord for QueuedOrd<T> {
530    fn cmp(&self, other: &Self) -> Ordering {
531        // Higher fee is better
532        self.max_fee_per_gas().cmp(&other.max_fee_per_gas()).then_with(||
533            // Lower timestamp is better
534            other.timestamp.cmp(&self.timestamp))
535    }
536}
537
538#[cfg(test)]
539mod tests {
540    use super::*;
541    use crate::test_utils::{MockTransaction, MockTransactionFactory, MockTransactionSet};
542    use alloy_consensus::{Transaction, TxType};
543    use alloy_primitives::address;
544    use std::collections::HashSet;
545
546    #[test]
547    fn test_enforce_parked_basefee() {
548        let mut f = MockTransactionFactory::default();
549        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
550        let tx = f.validated_arc(MockTransaction::eip1559().inc_price());
551        pool.add_transaction(tx.clone());
552
553        assert!(pool.contains(tx.id()));
554        assert_eq!(pool.len(), 1);
555
556        let removed = pool.enforce_basefee(u64::MAX);
557        assert!(removed.is_empty());
558
559        let removed = pool.enforce_basefee((tx.max_fee_per_gas() - 1) as u64);
560        assert_eq!(removed.len(), 1);
561        assert!(pool.is_empty());
562    }
563
564    #[test]
565    fn test_enforce_parked_basefee_descendant() {
566        let mut f = MockTransactionFactory::default();
567        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
568        let t = MockTransaction::eip1559().inc_price_by(10);
569        let root_tx = f.validated_arc(t.clone());
570        pool.add_transaction(root_tx.clone());
571
572        let descendant_tx = f.validated_arc(t.inc_nonce().decr_price());
573        pool.add_transaction(descendant_tx.clone());
574
575        assert!(pool.contains(root_tx.id()));
576        assert!(pool.contains(descendant_tx.id()));
577        assert_eq!(pool.len(), 2);
578
579        let removed = pool.enforce_basefee(u64::MAX);
580        assert!(removed.is_empty());
581        assert_eq!(pool.len(), 2);
582        // two dependent tx in the pool with decreasing fee
583
584        // remove root transaction via descendant tx fee
585        let removed = pool.enforce_basefee(descendant_tx.max_fee_per_gas() as u64);
586        assert_eq!(removed.len(), 2);
587        assert!(pool.is_empty());
588    }
589
590    #[test]
591    fn test_enforce_parked_basefee_removes_only_eligible_ancestor() {
592        let mut f = MockTransactionFactory::default();
593        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
594        let t = MockTransaction::eip1559().inc_price_by(10);
595
596        let root_tx = f.validated_arc(t.clone());
597        pool.add_transaction(root_tx.clone());
598
599        let descendant_tx = f.validated_arc(t.inc_nonce().decr_price());
600        pool.add_transaction(descendant_tx.clone());
601
602        let removed = pool.enforce_basefee(root_tx.max_fee_per_gas() as u64);
603
604        assert_eq!(removed.len(), 1);
605        assert_eq!(removed[0].id(), root_tx.id());
606        assert_eq!(pool.len(), 1);
607        assert!(!pool.contains(root_tx.id()));
608        assert!(pool.contains(descendant_tx.id()));
609    }
610
611    #[test]
612    fn truncate_parked_by_submission_id() {
613        // this test ensures that we evict from the pending pool by sender
614        let mut f = MockTransactionFactory::default();
615        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
616
617        let a_sender = address!("0x000000000000000000000000000000000000000a");
618        let b_sender = address!("0x000000000000000000000000000000000000000b");
619        let c_sender = address!("0x000000000000000000000000000000000000000c");
620        let d_sender = address!("0x000000000000000000000000000000000000000d");
621
622        // create a chain of transactions by sender A, B, C
623        let mut tx_set = MockTransactionSet::dependent(a_sender, 0, 4, TxType::Eip1559);
624        let a = tx_set.clone().into_vec();
625
626        let b = MockTransactionSet::dependent(b_sender, 0, 3, TxType::Eip1559).into_vec();
627        tx_set.extend(b.clone());
628
629        // C has the same number of txs as B
630        let c = MockTransactionSet::dependent(c_sender, 0, 3, TxType::Eip1559).into_vec();
631        tx_set.extend(c.clone());
632
633        let d = MockTransactionSet::dependent(d_sender, 0, 1, TxType::Eip1559).into_vec();
634        tx_set.extend(d.clone());
635
636        let all_txs = tx_set.into_vec();
637
638        // just construct a list of all txs to add
639        let expected_parked = vec![c[0].clone(), c[1].clone(), c[2].clone(), d[0].clone()]
640            .into_iter()
641            .map(|tx| (tx.sender(), tx.nonce()))
642            .collect::<HashSet<_>>();
643
644        // we expect the truncate operation to go through the senders with the most txs, removing
645        // txs based on when they were submitted, removing the oldest txs first, until the pool is
646        // not over the limit
647        let expected_removed = vec![
648            a[0].clone(),
649            a[1].clone(),
650            a[2].clone(),
651            a[3].clone(),
652            b[0].clone(),
653            b[1].clone(),
654            b[2].clone(),
655        ]
656        .into_iter()
657        .map(|tx| (tx.sender(), tx.nonce()))
658        .collect::<HashSet<_>>();
659
660        // add all the transactions to the pool
661        for tx in all_txs {
662            pool.add_transaction(f.validated_arc(tx));
663        }
664
665        // we should end up with the most recently submitted transactions
666        let pool_limit = SubPoolLimit { max_txs: 4, max_size: usize::MAX };
667
668        // truncate the pool
669        let removed = pool.truncate_pool(pool_limit);
670        assert_eq!(removed.len(), expected_removed.len());
671
672        // get the inner txs from the removed txs
673        let removed =
674            removed.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::<HashSet<_>>();
675        assert_eq!(removed, expected_removed);
676
677        // get the parked pool
678        let parked = pool.all().collect::<Vec<_>>();
679        assert_eq!(parked.len(), expected_parked.len());
680
681        // get the inner txs from the parked txs
682        let parked = parked.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::<HashSet<_>>();
683        assert_eq!(parked, expected_parked);
684    }
685
686    #[test]
687    fn test_truncate_parked_with_large_tx() {
688        let mut f = MockTransactionFactory::default();
689        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
690        let default_limits = SubPoolLimit::default();
691
692        // create a chain of transactions by sender A
693        // make sure they are all one over half the limit
694        let a_sender = address!("0x000000000000000000000000000000000000000a");
695
696        // 2 txs, that should put the pool over the size limit but not max txs
697        let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip1559)
698            .into_iter()
699            .map(|mut tx| {
700                tx.set_size(default_limits.max_size / 2 + 1);
701                tx
702            })
703            .collect::<Vec<_>>();
704
705        // add all the transactions to the pool
706        for tx in a_txs {
707            pool.add_transaction(f.validated_arc(tx));
708        }
709
710        // truncate the pool, it should remove at least one transaction
711        let removed = pool.truncate_pool(default_limits);
712        assert_eq!(removed.len(), 1);
713    }
714
715    #[test]
716    fn test_senders_by_submission_id() {
717        // this test ensures that we evict from the pending pool by sender
718        let mut f = MockTransactionFactory::default();
719        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
720
721        let a_sender = address!("0x000000000000000000000000000000000000000a");
722        let b_sender = address!("0x000000000000000000000000000000000000000b");
723        let c_sender = address!("0x000000000000000000000000000000000000000c");
724        let d_sender = address!("0x000000000000000000000000000000000000000d");
725
726        // create a chain of transactions by sender A, B, C
727        let mut tx_set = MockTransactionSet::dependent(a_sender, 0, 4, TxType::Eip1559);
728        let a = tx_set.clone().into_vec();
729
730        let b = MockTransactionSet::dependent(b_sender, 0, 3, TxType::Eip1559).into_vec();
731        tx_set.extend(b.clone());
732
733        // C has the same number of txs as B
734        let c = MockTransactionSet::dependent(c_sender, 0, 3, TxType::Eip1559).into_vec();
735        tx_set.extend(c.clone());
736
737        let d = MockTransactionSet::dependent(d_sender, 0, 1, TxType::Eip1559).into_vec();
738        tx_set.extend(d.clone());
739
740        let all_txs = tx_set.into_vec();
741
742        // add all the transactions to the pool
743        for tx in all_txs {
744            pool.add_transaction(f.validated_arc(tx));
745        }
746
747        // get senders by submission id - a4, b3, c3, d1, reversed
748        let senders = pool.get_senders_by_submission_id().map(|s| s.sender_id).collect::<Vec<_>>();
749        assert_eq!(senders.len(), 4);
750        let expected_senders = vec![d_sender, c_sender, b_sender, a_sender]
751            .into_iter()
752            .map(|s| f.ids.sender_id(&s).unwrap())
753            .collect::<Vec<_>>();
754        assert_eq!(senders, expected_senders);
755
756        // manually order the txs
757        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
758        let all_txs = vec![d[0].clone(), b[0].clone(), c[0].clone(), a[0].clone()];
759
760        // add all the transactions to the pool
761        for tx in all_txs {
762            pool.add_transaction(f.validated_arc(tx));
763        }
764
765        let senders = pool.get_senders_by_submission_id().map(|s| s.sender_id).collect::<Vec<_>>();
766        assert_eq!(senders.len(), 4);
767        let expected_senders = vec![a_sender, c_sender, b_sender, d_sender]
768            .into_iter()
769            .map(|s| f.ids.sender_id(&s).unwrap())
770            .collect::<Vec<_>>();
771        assert_eq!(senders, expected_senders);
772    }
773
774    #[test]
775    fn test_add_sender_count_new_sender() {
776        // Initialize a mock transaction factory
777        let mut f = MockTransactionFactory::default();
778        // Create an empty transaction pool
779        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
780        // Generate a validated transaction and add it to the pool
781        let tx = f.validated_arc(MockTransaction::eip1559().inc_price());
782        pool.add_transaction(tx);
783
784        // Define a new sender ID and submission ID
785        let sender: SenderId = 11.into();
786        let submission_id = 1;
787
788        // Add the sender count to the pool
789        pool.add_sender_count(sender, submission_id);
790
791        // Assert that the sender transaction count is updated correctly
792        assert_eq!(pool.sender_transaction_count.len(), 2);
793        let sender_info = pool.sender_transaction_count.get(&sender).unwrap();
794        assert_eq!(sender_info.count, 1);
795        assert_eq!(sender_info.last_submission_id, submission_id);
796
797        // Assert that the last sender submission is updated correctly
798        assert_eq!(pool.last_sender_submission.len(), 2);
799        let submission_info = pool.last_sender_submission.iter().next().unwrap();
800        assert_eq!(submission_info.sender_id, sender);
801        assert_eq!(submission_info.submission_id, submission_id);
802    }
803
804    #[test]
805    fn test_add_sender_count_existing_sender() {
806        // Initialize a mock transaction factory
807        let mut f = MockTransactionFactory::default();
808        // Create an empty transaction pool
809        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
810        // Generate a validated transaction and add it to the pool
811        let tx = f.validated_arc(MockTransaction::eip1559().inc_price());
812        pool.add_transaction(tx);
813
814        // Define a sender ID and initial submission ID
815        let sender: SenderId = 11.into();
816        let initial_submission_id = 1;
817
818        // Add the sender count to the pool with the initial submission ID
819        pool.add_sender_count(sender, initial_submission_id);
820
821        // Define a new submission ID
822        let new_submission_id = 2;
823        // Add the sender count to the pool with the new submission ID
824        pool.add_sender_count(sender, new_submission_id);
825
826        // Assert that the sender transaction count is updated correctly
827        assert_eq!(pool.sender_transaction_count.len(), 2);
828        let sender_info = pool.sender_transaction_count.get(&sender).unwrap();
829        assert_eq!(sender_info.count, 2);
830        assert_eq!(sender_info.last_submission_id, new_submission_id);
831
832        // Assert that the last sender submission is updated correctly
833        assert_eq!(pool.last_sender_submission.len(), 2);
834        let submission_info = pool.last_sender_submission.iter().next().unwrap();
835        assert_eq!(submission_info.sender_id, sender);
836        assert_eq!(submission_info.submission_id, new_submission_id);
837    }
838
839    #[test]
840    fn test_add_sender_count_multiple_senders() {
841        // Initialize a mock transaction factory
842        let mut f = MockTransactionFactory::default();
843        // Create an empty transaction pool
844        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
845        // Generate two validated transactions and add them to the pool
846        let tx1 = f.validated_arc(MockTransaction::eip1559().inc_price());
847        let tx2 = f.validated_arc(MockTransaction::eip1559().inc_price());
848        pool.add_transaction(tx1);
849        pool.add_transaction(tx2);
850
851        // Define two different sender IDs and their corresponding submission IDs
852        let sender1: SenderId = 11.into();
853        let sender2: SenderId = 22.into();
854
855        // Add the sender counts to the pool
856        pool.add_sender_count(sender1, 1);
857        pool.add_sender_count(sender2, 2);
858
859        // Assert that the sender transaction counts are updated correctly
860        assert_eq!(pool.sender_transaction_count.len(), 4);
861
862        let sender1_info = pool.sender_transaction_count.get(&sender1).unwrap();
863        assert_eq!(sender1_info.count, 1);
864        assert_eq!(sender1_info.last_submission_id, 1);
865
866        let sender2_info = pool.sender_transaction_count.get(&sender2).unwrap();
867        assert_eq!(sender2_info.count, 1);
868        assert_eq!(sender2_info.last_submission_id, 2);
869
870        // Assert that the last sender submission is updated correctly
871        assert_eq!(pool.last_sender_submission.len(), 3);
872
873        // Verify that sender 1 is not in the last sender submission
874        let submission_info1 =
875            pool.last_sender_submission.iter().find(|info| info.sender_id == sender1);
876        assert!(submission_info1.is_none());
877
878        // Verify that sender 2 is in the last sender submission
879        let submission_info2 =
880            pool.last_sender_submission.iter().find(|info| info.sender_id == sender2).unwrap();
881        assert_eq!(submission_info2.sender_id, sender2);
882        assert_eq!(submission_info2.submission_id, 2);
883    }
884
885    #[test]
886    fn test_remove_sender_count() {
887        // Initialize a mock transaction factory
888        let mut f = MockTransactionFactory::default();
889        // Create an empty transaction pool
890        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
891        // Generate two validated transactions and add them to the pool
892        let tx1 = f.validated_arc(MockTransaction::eip1559().inc_price());
893        let tx2 = f.validated_arc(MockTransaction::eip1559().inc_price());
894        pool.add_transaction(tx1);
895        pool.add_transaction(tx2);
896
897        // Define two different sender IDs and their corresponding submission IDs
898        let sender1: SenderId = 11.into();
899        let sender2: SenderId = 22.into();
900
901        // Add the sender counts to the pool
902        pool.add_sender_count(sender1, 1);
903
904        // We add sender 2 multiple times to test the removal of sender counts
905        pool.add_sender_count(sender2, 2);
906        pool.add_sender_count(sender2, 3);
907
908        // Before removing the sender count we should have 4 sender transaction counts
909        assert_eq!(pool.sender_transaction_count.len(), 4);
910        assert!(pool.sender_transaction_count.contains_key(&sender1));
911
912        // We should have 1 sender transaction count for sender 1 before removing the sender count
913        assert_eq!(pool.sender_transaction_count.get(&sender1).unwrap().count, 1);
914
915        // Remove the sender count for sender 1
916        pool.remove_sender_count(sender1);
917
918        // After removing the sender count we should have 3 sender transaction counts remaining
919        assert_eq!(pool.sender_transaction_count.len(), 3);
920        assert!(!pool.sender_transaction_count.contains_key(&sender1));
921
922        // Check the sender transaction count for sender 2 before removing the sender count
923        assert_eq!(
924            *pool.sender_transaction_count.get(&sender2).unwrap(),
925            SenderTransactionCount { count: 2, last_submission_id: 3 }
926        );
927
928        // Remove the sender count for sender 2
929        pool.remove_sender_count(sender2);
930
931        // After removing the sender count for sender 2, we still have 3 sender transaction counts
932        // remaining.
933        //
934        // This is because we added sender 2 multiple times and we only removed the last submission.
935        assert_eq!(pool.sender_transaction_count.len(), 3);
936        assert!(pool.sender_transaction_count.contains_key(&sender2));
937
938        // Sender transaction count for sender 2 should be updated correctly
939        assert_eq!(
940            *pool.sender_transaction_count.get(&sender2).unwrap(),
941            SenderTransactionCount { count: 1, last_submission_id: 3 }
942        );
943    }
944
945    #[test]
946    fn test_pool_size() {
947        let mut f = MockTransactionFactory::default();
948        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
949
950        // Create a transaction with a specific size and add it to the pool
951        let tx = f.validated_arc(MockTransaction::eip1559().set_size(1024).clone());
952        pool.add_transaction(tx);
953
954        // Assert that the reported size of the pool is correct
955        assert_eq!(pool.size(), 1024);
956    }
957
958    #[test]
959    fn test_pool_len() {
960        let mut f = MockTransactionFactory::default();
961        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
962
963        // Initially, the pool should have zero transactions
964        assert_eq!(pool.len(), 0);
965
966        // Add a transaction to the pool and check the length
967        let tx = f.validated_arc(MockTransaction::eip1559());
968        pool.add_transaction(tx);
969        assert_eq!(pool.len(), 1);
970    }
971
972    #[test]
973    fn test_pool_contains() {
974        let mut f = MockTransactionFactory::default();
975        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
976
977        // Create a transaction and get its ID
978        let tx = f.validated_arc(MockTransaction::eip1559());
979        let tx_id = *tx.id();
980
981        // Before adding, the transaction should not be in the pool
982        assert!(!pool.contains(&tx_id));
983
984        // After adding, the transaction should be present in the pool
985        pool.add_transaction(tx);
986        assert!(pool.contains(&tx_id));
987    }
988
989    #[test]
990    fn test_get_transaction() {
991        let mut f = MockTransactionFactory::default();
992        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
993
994        // Add a transaction to the pool and get its ID
995        let tx = f.validated_arc(MockTransaction::eip1559());
996        let tx_id = *tx.id();
997        pool.add_transaction(tx.clone());
998
999        // Retrieve the transaction using `get()` and assert it matches the added transaction
1000        let retrieved = pool.get(&tx_id).expect("Transaction should exist in the pool");
1001        assert_eq!(retrieved.transaction.id(), tx.id());
1002    }
1003
1004    #[test]
1005    fn test_all_transactions() {
1006        let mut f = MockTransactionFactory::default();
1007        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
1008
1009        // Add two transactions to the pool
1010        let tx1 = f.validated_arc(MockTransaction::eip1559());
1011        let tx2 = f.validated_arc(MockTransaction::eip1559());
1012        pool.add_transaction(tx1.clone());
1013        pool.add_transaction(tx2.clone());
1014
1015        // Collect all transaction IDs from the pool
1016        let all_txs: Vec<_> = pool.all().map(|tx| *tx.id()).collect();
1017        assert_eq!(all_txs.len(), 2);
1018
1019        // Check that the IDs of both transactions are present
1020        assert!(all_txs.contains(tx1.id()));
1021        assert!(all_txs.contains(tx2.id()));
1022    }
1023
1024    #[test]
1025    fn test_truncate_pool_edge_case() {
1026        let mut f = MockTransactionFactory::default();
1027        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
1028
1029        // Add two transactions to the pool
1030        let tx1 = f.validated_arc(MockTransaction::eip1559());
1031        let tx2 = f.validated_arc(MockTransaction::eip1559());
1032        pool.add_transaction(tx1);
1033        pool.add_transaction(tx2);
1034
1035        // Set a limit that matches the current number of transactions
1036        let limit = SubPoolLimit { max_txs: 2, max_size: usize::MAX };
1037        let removed = pool.truncate_pool(limit);
1038
1039        // No transactions should be removed
1040        assert!(removed.is_empty());
1041
1042        // Set a stricter limit that requires truncating one transaction
1043        let limit = SubPoolLimit { max_txs: 1, max_size: usize::MAX };
1044        let removed = pool.truncate_pool(limit);
1045
1046        // One transaction should be removed, and the pool should have one left
1047        assert_eq!(removed.len(), 1);
1048        assert_eq!(pool.len(), 1);
1049    }
1050
1051    #[test]
1052    fn test_satisfy_base_fee_transactions() {
1053        let mut f = MockTransactionFactory::default();
1054        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
1055
1056        // Add two transactions with different max fees
1057        let tx1 = f.validated_arc(MockTransaction::eip1559().set_max_fee(100).clone());
1058        let tx2 = f.validated_arc(MockTransaction::eip1559().set_max_fee(200).clone());
1059        pool.add_transaction(tx1);
1060        pool.add_transaction(tx2.clone());
1061
1062        // Check that only the second transaction satisfies the base fee requirement
1063        let satisfied = pool.satisfy_base_fee_transactions(150);
1064        assert_eq!(satisfied.len(), 1);
1065        assert_eq!(satisfied[0].id(), tx2.id())
1066    }
1067
1068    #[test]
1069    fn test_remove_transaction() {
1070        let mut f = MockTransactionFactory::default();
1071        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
1072
1073        // Add a transaction to the pool and get its ID
1074        let tx = f.validated_arc(MockTransaction::eip1559());
1075        let tx_id = *tx.id();
1076        pool.add_transaction(tx);
1077
1078        // Ensure the transaction is in the pool before removal
1079        assert!(pool.contains(&tx_id));
1080
1081        // Remove the transaction and check that it is no longer in the pool
1082        let removed = pool.remove_transaction(&tx_id);
1083        assert!(removed.is_some());
1084        assert!(!pool.contains(&tx_id));
1085    }
1086
1087    #[test]
1088    fn test_enforce_basefee_with_handler_zero_allocation() {
1089        let mut f = MockTransactionFactory::default();
1090        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
1091
1092        // Add multiple transactions across different fee ranges
1093        let sender_a = address!("0x000000000000000000000000000000000000000a");
1094        let sender_b = address!("0x000000000000000000000000000000000000000b");
1095
1096        // Add transactions where nonce ordering allows proper processing:
1097        // Sender A: both transactions can afford basefee (500 >= 400, 600 >= 400)
1098        // Sender B: transaction cannot afford basefee (300 < 400)
1099        let txs = vec![
1100            f.validated_arc(
1101                MockTransaction::eip1559()
1102                    .set_sender(sender_a)
1103                    .set_nonce(0)
1104                    .set_max_fee(500)
1105                    .clone(),
1106            ),
1107            f.validated_arc(
1108                MockTransaction::eip1559()
1109                    .set_sender(sender_a)
1110                    .set_nonce(1)
1111                    .set_max_fee(600)
1112                    .clone(),
1113            ),
1114            f.validated_arc(
1115                MockTransaction::eip1559()
1116                    .set_sender(sender_b)
1117                    .set_nonce(0)
1118                    .set_max_fee(300)
1119                    .clone(),
1120            ),
1121        ];
1122
1123        let expected_affordable = vec![txs[0].clone(), txs[1].clone()]; // Both sender A txs
1124        for tx in txs {
1125            pool.add_transaction(tx);
1126        }
1127
1128        // Test the handler approach with zero allocations
1129        let mut processed_txs = Vec::new();
1130        let mut handler_call_count = 0;
1131
1132        pool.enforce_basefee_with(400, |tx| {
1133            processed_txs.push(tx);
1134            handler_call_count += 1;
1135        });
1136
1137        // Verify correct number of transactions processed
1138        assert_eq!(handler_call_count, 2);
1139        assert_eq!(processed_txs.len(), 2);
1140
1141        // Verify the correct transactions were processed (those with fee >= 400)
1142        let processed_ids: Vec<_> = processed_txs.iter().map(|tx| *tx.id()).collect();
1143        for expected_tx in expected_affordable {
1144            assert!(processed_ids.contains(expected_tx.id()));
1145        }
1146
1147        // Verify transactions were removed from pool
1148        assert_eq!(pool.len(), 1); // Only the 300 fee tx should remain
1149    }
1150}