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