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