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