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