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