1use crate::{
4 identifier::{SenderId, TransactionId},
5 pool::{
6 best::{BestTransactions, BestTransactionsWithFees},
7 size::SizeTracker,
8 },
9 Priority, SubPoolLimit, TransactionOrdering, ValidPoolTransaction,
10};
11use imbl::OrdMap;
12use rustc_hash::{FxHashMap, FxHashSet};
13use std::{cmp::Ordering, collections::hash_map::Entry, ops::Bound::Unbounded, sync::Arc};
14use tokio::sync::broadcast;
15
16#[derive(Debug, Clone)]
27pub struct PendingPool<T: TransactionOrdering> {
28 ordering: T,
30 submission_id: u64,
34 by_id: OrdMap<TransactionId, PendingTransaction<T>>,
36 highest_nonces: FxHashMap<SenderId, PendingTransaction<T>>,
39 independent_transactions: FxHashMap<SenderId, PendingTransaction<T>>,
42 size_of: SizeTracker,
46 new_transaction_notifier: broadcast::Sender<PendingTransaction<T>>,
49}
50
51impl<T: TransactionOrdering> PendingPool<T> {
54 pub fn new(ordering: T) -> Self {
56 Self::with_buffer(ordering, 200)
57 }
58
59 pub fn with_buffer(ordering: T, buffer_capacity: usize) -> Self {
61 let (new_transaction_notifier, _) = broadcast::channel(buffer_capacity);
62 Self {
63 ordering,
64 submission_id: 0,
65 by_id: Default::default(),
66 independent_transactions: Default::default(),
67 highest_nonces: Default::default(),
68 size_of: Default::default(),
69 new_transaction_notifier,
70 }
71 }
72
73 fn clear_transactions(&mut self) -> OrdMap<TransactionId, PendingTransaction<T>> {
80 self.independent_transactions.clear();
81 self.highest_nonces.clear();
82 self.size_of.reset();
83 std::mem::take(&mut self.by_id)
84 }
85
86 pub fn best(&self) -> BestTransactions<T> {
105 BestTransactions {
106 all: self.by_id.clone(),
107 independent: self.independent_transactions.values().cloned().collect(),
108 invalid: Default::default(),
109 new_transaction_receiver: Some(self.new_transaction_notifier.subscribe()),
110 last_priority: None,
111 skip_blobs: false,
112 allow_updates_out_of_order: false,
113 }
114 }
115
116 pub(crate) fn best_with_basefee_and_blobfee(
118 &self,
119 base_fee: u64,
120 base_fee_per_blob_gas: u64,
121 ) -> BestTransactionsWithFees<T> {
122 BestTransactionsWithFees { best: self.best(), base_fee, base_fee_per_blob_gas }
123 }
124
125 pub(crate) fn best_with_unlocked_and_attributes(
136 &self,
137 unlocked: Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
138 base_fee: u64,
139 base_fee_per_blob_gas: u64,
140 ) -> BestTransactionsWithFees<T> {
141 let mut best = self.best();
142 for (submission_id, tx) in (self.submission_id + 1..).zip(unlocked) {
143 debug_assert!(!best.all.contains_key(tx.id()), "transaction already included");
144 let priority = self.ordering.priority(&tx.transaction, base_fee);
145 let tx_id = *tx.id();
146 let transaction = PendingTransaction { submission_id, transaction: tx, priority };
147 if best.ancestor(&tx_id).is_none() {
148 best.independent.insert(transaction.clone());
149 }
150 best.all.insert(tx_id, transaction);
151 }
152
153 BestTransactionsWithFees { best, base_fee, base_fee_per_blob_gas }
154 }
155
156 pub(crate) fn all(
158 &self,
159 ) -> impl ExactSizeIterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
160 self.by_id.values().map(|tx| tx.transaction.clone())
161 }
162
163 pub(crate) fn update_blob_fee(
173 &mut self,
174 blob_fee: u128,
175 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
176 let mut removed = Vec::new();
178
179 let mut transactions_iter = self.clear_transactions().into_iter().peekable();
181 let mut highest: Option<TransactionId> = None;
182 while let Some((id, tx)) = transactions_iter.next() {
183 self.flush_highest_nonce(&mut highest, Some(id.sender));
184 if tx.transaction.is_eip4844() && tx.transaction.max_fee_per_blob_gas() < Some(blob_fee)
185 {
186 removed.push(Arc::clone(&tx.transaction));
189
190 'this: while let Some((next_id, next_tx)) = transactions_iter.peek() {
193 if next_id.sender != id.sender {
194 break 'this
195 }
196 removed.push(Arc::clone(&next_tx.transaction));
197 transactions_iter.next();
198 }
199 } else {
200 self.size_of += tx.transaction.size();
201 if highest.is_none() {
202 self.independent_transactions.insert(id.sender, tx.clone());
204 }
205 highest = Some(id);
206 self.by_id.insert(id, tx);
207 }
208 }
209 self.flush_highest_nonce(&mut highest, None);
210
211 removed
212 }
213
214 pub(crate) fn update_base_fee(
224 &mut self,
225 base_fee: u64,
226 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
227 let mut removed = Vec::new();
229
230 let mut transactions_iter = self.clear_transactions().into_iter().peekable();
232 let mut highest: Option<TransactionId> = None;
233 while let Some((id, mut tx)) = transactions_iter.next() {
234 self.flush_highest_nonce(&mut highest, Some(id.sender));
235 if tx.transaction.max_fee_per_gas() < base_fee as u128 {
236 removed.push(Arc::clone(&tx.transaction));
239
240 'this: while let Some((next_id, next_tx)) = transactions_iter.peek() {
243 if next_id.sender != id.sender {
244 break 'this
245 }
246 removed.push(Arc::clone(&next_tx.transaction));
247 transactions_iter.next();
248 }
249 } else {
250 tx.priority = self.ordering.priority(&tx.transaction.transaction, base_fee);
252
253 self.size_of += tx.transaction.size();
254 if highest.is_none() {
255 self.independent_transactions.insert(id.sender, tx.clone());
257 }
258 highest = Some(id);
259 self.by_id.insert(id, tx);
260 }
261 }
262 self.flush_highest_nonce(&mut highest, None);
263
264 removed
265 }
266
267 fn flush_highest_nonce(
276 &mut self,
277 tracked: &mut Option<TransactionId>,
278 next_sender: Option<SenderId>,
279 ) {
280 if let Some(id) = tracked.take_if(|id| Some(id.sender) != next_sender) &&
281 let Some(tx) = self.by_id.get(&id).cloned()
282 {
283 self.highest_nonces.insert(id.sender, tx);
284 }
285 }
286
287 fn update_independents_and_highest_nonces(&mut self, tx: &PendingTransaction<T>) {
290 match self.highest_nonces.entry(tx.transaction.sender_id()) {
291 Entry::Occupied(mut entry) => {
292 if entry.get().transaction.nonce() < tx.transaction.nonce() {
293 *entry.get_mut() = tx.clone();
294 }
295 }
296 Entry::Vacant(entry) => {
297 entry.insert(tx.clone());
298 }
299 }
300 match self.independent_transactions.entry(tx.transaction.sender_id()) {
301 Entry::Occupied(mut entry) => {
302 if entry.get().transaction.nonce() > tx.transaction.nonce() {
303 *entry.get_mut() = tx.clone();
304 }
305 }
306 Entry::Vacant(entry) => {
307 entry.insert(tx.clone());
308 }
309 }
310 }
311
312 pub fn add_transaction(
318 &mut self,
319 tx: Arc<ValidPoolTransaction<T::Transaction>>,
320 base_fee: u64,
321 ) {
322 debug_assert!(
323 !self.contains(tx.id()),
324 "transaction already included {:?}",
325 self.get(tx.id()).unwrap().transaction
326 );
327
328 self.size_of += tx.size();
330
331 let tx_id = *tx.id();
332
333 let submission_id = self.next_id();
334 let priority = self.ordering.priority(&tx.transaction, base_fee);
335 let tx = PendingTransaction { submission_id, transaction: tx, priority };
336
337 self.update_independents_and_highest_nonces(&tx);
338
339 if self.new_transaction_notifier.receiver_count() > 0 {
341 let _ = self.new_transaction_notifier.send(tx.clone());
342 }
343
344 self.by_id.insert(tx_id, tx);
345 }
346
347 pub(crate) fn remove_transaction(
352 &mut self,
353 id: &TransactionId,
354 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
355 if let Some(lowest) = self.independent_transactions.get(&id.sender) &&
356 lowest.transaction.nonce() == id.nonce
357 {
358 self.independent_transactions.remove(&id.sender);
359 if let Some(unlocked) = self.get(&id.descendant()) {
361 self.independent_transactions.insert(id.sender, unlocked.clone());
362 }
363 }
364
365 let tx = self.by_id.remove(id)?;
366 self.size_of -= tx.transaction.size();
367
368 match self.highest_nonces.entry(id.sender) {
369 Entry::Occupied(mut entry) => {
370 if entry.get().transaction.nonce() == id.nonce {
371 if let Some((_, new_highest)) = self
374 .by_id
375 .range((
376 id.sender.start_bound(),
377 std::ops::Bound::Included(TransactionId::new(id.sender, u64::MAX)),
378 ))
379 .next_back()
380 {
381 entry.insert(new_highest.clone());
383 } else {
384 entry.remove();
385 }
386 }
387 }
388 Entry::Vacant(_) => {
389 debug_assert!(
390 false,
391 "removed transaction without a tracked highest nonce {:?}",
392 id
393 );
394 }
395 }
396
397 Some(tx.transaction)
398 }
399
400 const fn next_id(&mut self) -> u64 {
401 let id = self.submission_id;
402 self.submission_id = self.submission_id.wrapping_add(1);
403 id
404 }
405
406 pub fn remove_to_limit(
421 &mut self,
422 limit: &SubPoolLimit,
423 remove_locals: bool,
424 end_removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
425 ) {
426 let mut non_local_senders = self.highest_nonces.len();
435
436 let mut unique_senders = self.highest_nonces.len();
439
440 let mut local_senders = FxHashSet::default();
442
443 let original_length = self.len();
445 let mut removed = Vec::new();
446 let mut total_removed = 0;
447
448 let original_size = self.size();
450 let mut total_size = 0;
451
452 loop {
453 let unique_removed = unique_senders - self.highest_nonces.len();
455
456 unique_senders = self.highest_nonces.len();
458 non_local_senders -= unique_removed;
459
460 removed.clear();
462
463 let mut worst_transactions = self.highest_nonces.values().collect::<Vec<_>>();
465
466 let current_len = original_length - total_removed;
471 let current_size = original_size - total_size;
472 let excess_txs = current_len.saturating_sub(limit.max_txs);
473 let avg_tx_size = (current_size / current_len.max(1)).max(1);
474 let excess_size_txs = current_size.saturating_sub(limit.max_size).div_ceil(avg_tx_size);
475 let removal_candidates = excess_txs.max(excess_size_txs).max(1) + local_senders.len();
479
480 if removal_candidates < worst_transactions.len() {
481 worst_transactions.select_nth_unstable(removal_candidates);
483 worst_transactions.truncate(removal_candidates);
484 }
485 worst_transactions.sort_unstable();
486
487 for tx in worst_transactions {
489 if !limit.is_exceeded(original_length - total_removed, original_size - total_size) ||
491 non_local_senders == 0
492 {
493 for id in &removed {
495 if let Some(tx) = self.remove_transaction(id) {
496 end_removed.push(tx);
497 }
498 }
499
500 return
501 }
502
503 if !remove_locals && tx.transaction.is_local() {
504 let sender_id = tx.transaction.sender_id();
505 if local_senders.insert(sender_id) {
506 non_local_senders -= 1;
507 }
508 continue
509 }
510
511 total_size += tx.transaction.size();
512 total_removed += 1;
513 removed.push(*tx.transaction.id());
514 }
515
516 for id in &removed {
518 if let Some(tx) = self.remove_transaction(id) {
519 end_removed.push(tx);
520 }
521 }
522
523 if !self.exceeds(limit) || non_local_senders == 0 {
526 return
527 }
528 }
529 }
530
531 pub fn truncate_pool(
542 &mut self,
543 limit: SubPoolLimit,
544 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
545 let mut removed = Vec::new();
546 if !self.exceeds(&limit) {
548 return removed
549 }
550
551 self.remove_to_limit(&limit, false, &mut removed);
553 if !self.exceeds(&limit) {
554 return removed
555 }
556
557 self.remove_to_limit(&limit, true, &mut removed);
560
561 removed
562 }
563
564 #[inline]
566 pub(crate) fn exceeds(&self, limit: &SubPoolLimit) -> bool {
567 limit.is_exceeded(self.len(), self.size())
568 }
569
570 pub(crate) fn size(&self) -> usize {
572 self.size_of.into()
573 }
574
575 pub(crate) fn len(&self) -> usize {
577 self.by_id.len()
578 }
579
580 pub const fn by_id(&self) -> &OrdMap<TransactionId, PendingTransaction<T>> {
582 &self.by_id
583 }
584
585 pub const fn independent_transactions(&self) -> &FxHashMap<SenderId, PendingTransaction<T>> {
587 &self.independent_transactions
588 }
589
590 pub fn new_transaction_receiver(&self) -> broadcast::Receiver<PendingTransaction<T>> {
592 self.new_transaction_notifier.subscribe()
593 }
594
595 #[cfg(test)]
597 pub(crate) fn is_empty(&self) -> bool {
598 self.by_id.is_empty()
599 }
600
601 pub(crate) fn contains(&self, id: &TransactionId) -> bool {
603 self.by_id.contains_key(id)
604 }
605
606 pub(crate) fn get_txs_by_sender(&self, sender: SenderId) -> Vec<TransactionId> {
608 self.iter_txs_by_sender(sender).copied().collect()
609 }
610
611 pub(crate) fn iter_txs_by_sender(
613 &self,
614 sender: SenderId,
615 ) -> impl Iterator<Item = &TransactionId> + '_ {
616 self.by_id
617 .range((sender.start_bound(), Unbounded))
618 .take_while(move |(other, _)| sender == other.sender)
619 .map(|(tx_id, _)| tx_id)
620 }
621
622 pub(crate) fn txs_by_sender(
625 &self,
626 sender: SenderId,
627 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
628 self.by_id
629 .range((sender.start_bound(), Unbounded))
630 .take_while(move |(other, _)| sender == other.sender)
631 .map(|(_, tx)| tx.transaction.clone())
632 }
633
634 fn get(&self, id: &TransactionId) -> Option<&PendingTransaction<T>> {
636 self.by_id.get(id)
637 }
638
639 #[cfg(test)]
641 pub(crate) const fn independent(&self) -> &FxHashMap<SenderId, PendingTransaction<T>> {
642 &self.independent_transactions
643 }
644
645 #[cfg(any(test, feature = "test-utils"))]
651 pub(crate) fn assert_invariants(&self) {
652 let mut senders = 0;
653 let mut txs = self.by_id.iter().peekable();
654 while let Some((lowest_id, lowest)) = txs.next() {
655 let sender = lowest_id.sender;
656 senders += 1;
657
658 let mut highest = lowest;
659 while let Some((_, tx)) = txs.next_if(|(id, _)| id.sender == sender) {
660 highest = tx;
661 }
662
663 let independent = self
664 .independent_transactions
665 .get(&sender)
666 .unwrap_or_else(|| panic!("no independent transaction tracked for {sender:?}"));
667 assert_eq!(
668 independent, lowest,
669 "independent transaction of {sender:?} does not match its lowest nonce pool entry"
670 );
671 let tracked_highest = self
672 .highest_nonces
673 .get(&sender)
674 .unwrap_or_else(|| panic!("no highest nonce tracked for {sender:?}"));
675 assert_eq!(
676 tracked_highest, highest,
677 "highest nonce of {sender:?} does not match its highest nonce pool entry"
678 );
679 }
680
681 assert_eq!(
682 self.independent_transactions.len(),
683 senders,
684 "independent_transactions tracks senders without transactions"
685 );
686 assert_eq!(
687 self.highest_nonces.len(),
688 senders,
689 "highest_nonces tracks senders without transactions"
690 );
691 assert_eq!(
692 self.size(),
693 self.by_id.values().map(|tx| tx.transaction.size()).sum::<usize>(),
694 "size_of does not match the size of all transactions"
695 );
696 }
697}
698
699#[derive(Debug)]
701pub struct PendingTransaction<T: TransactionOrdering> {
702 pub submission_id: u64,
704 pub transaction: Arc<ValidPoolTransaction<T::Transaction>>,
706 pub priority: Priority<T::PriorityValue>,
708}
709
710impl<T: TransactionOrdering> PendingTransaction<T> {
711 pub fn unlocks(&self) -> TransactionId {
713 self.transaction.transaction_id.descendant()
714 }
715}
716
717impl<T: TransactionOrdering> Clone for PendingTransaction<T> {
718 fn clone(&self) -> Self {
719 Self {
720 submission_id: self.submission_id,
721 transaction: Arc::clone(&self.transaction),
722 priority: self.priority.clone(),
723 }
724 }
725}
726
727impl<T: TransactionOrdering> Eq for PendingTransaction<T> {}
728
729impl<T: TransactionOrdering> PartialEq<Self> for PendingTransaction<T> {
730 fn eq(&self, other: &Self) -> bool {
731 self.cmp(other) == Ordering::Equal
732 }
733}
734
735impl<T: TransactionOrdering> PartialOrd<Self> for PendingTransaction<T> {
736 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
737 Some(self.cmp(other))
738 }
739}
740
741impl<T: TransactionOrdering> Ord for PendingTransaction<T> {
742 fn cmp(&self, other: &Self) -> Ordering {
743 self.priority
747 .cmp(&other.priority)
748 .then_with(|| other.submission_id.cmp(&self.submission_id))
749 }
750}
751
752#[cfg(test)]
753mod tests {
754 use super::*;
755 use crate::{
756 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
757 PoolTransaction,
758 };
759 use alloy_consensus::{Transaction, TxType};
760 use alloy_primitives::address;
761 use std::collections::HashSet;
762
763 #[test]
764 fn test_enforce_basefee() {
765 let mut f = MockTransactionFactory::default();
766 let mut pool = PendingPool::new(MockOrdering::default());
767 let tx = f.validated_arc(MockTransaction::eip1559().inc_price());
768 pool.add_transaction(tx.clone(), 0);
769
770 assert!(pool.contains(tx.id()));
771 assert_eq!(pool.len(), 1);
772
773 let removed = pool.update_base_fee(0);
774 assert!(removed.is_empty());
775
776 let removed = pool.update_base_fee((tx.max_fee_per_gas() + 1) as u64);
777 assert_eq!(removed.len(), 1);
778 assert!(pool.is_empty());
779 }
780
781 #[test]
782 fn test_enforce_basefee_descendant() {
783 let mut f = MockTransactionFactory::default();
784 let mut pool = PendingPool::new(MockOrdering::default());
785 let t = MockTransaction::eip1559().inc_price_by(10);
786 let root_tx = f.validated_arc(t.clone());
787 pool.add_transaction(root_tx.clone(), 0);
788
789 let descendant_tx = f.validated_arc(t.inc_nonce().decr_price());
790 pool.add_transaction(descendant_tx.clone(), 0);
791
792 assert!(pool.contains(root_tx.id()));
793 assert!(pool.contains(descendant_tx.id()));
794 assert_eq!(pool.len(), 2);
795
796 assert_eq!(pool.independent_transactions.len(), 1);
797 assert_eq!(pool.highest_nonces.len(), 1);
798
799 let removed = pool.update_base_fee(0);
800 assert!(removed.is_empty());
801
802 {
805 let mut pool2 = pool.clone();
806 let removed = pool2.update_base_fee((descendant_tx.max_fee_per_gas() + 1) as u64);
807 assert_eq!(removed.len(), 1);
808 assert_eq!(pool2.len(), 1);
809 assert!(pool2.contains(root_tx.id()));
811 assert!(!pool2.contains(descendant_tx.id()));
812 }
813
814 let removed = pool.update_base_fee((root_tx.max_fee_per_gas() + 1) as u64);
816 assert_eq!(removed.len(), 2);
817 assert!(pool.is_empty());
818 pool.assert_invariants();
819 }
820
821 #[test]
822 fn test_enforce_basefee_tracks_highest_nonce_of_partially_removed_sender() {
823 let mut f = MockTransactionFactory::default();
824 let mut pool = PendingPool::new(MockOrdering::default());
825
826 let a = MockTransaction::eip1559().inc_price_by(20);
828 let a0 = f.validated_arc(a.clone());
829 let a1 = f.validated_arc(a.inc_nonce().rng_hash().decr_price_by(15));
830 let b = MockTransaction::eip1559().inc_price_by(20);
832 let b0 = f.validated_arc(b.clone());
833 let b1 = f.validated_arc(b.inc_nonce().rng_hash());
834
835 for tx in [&a0, &a1, &b0, &b1] {
836 pool.add_transaction(tx.clone(), 0);
837 }
838
839 let removed = pool.update_base_fee((a1.max_fee_per_gas() + 1) as u64);
840 assert_eq!(removed.len(), 1);
841 assert_eq!(removed[0].hash(), a1.hash());
842
843 pool.assert_invariants();
844 assert_eq!(pool.highest_nonces[&a0.sender_id()].transaction.nonce(), a0.nonce());
845 assert_eq!(pool.independent_transactions[&a0.sender_id()].transaction.nonce(), a0.nonce());
846 assert_eq!(pool.highest_nonces[&b1.sender_id()].transaction.nonce(), b1.nonce());
847 assert_eq!(pool.independent_transactions[&b0.sender_id()].transaction.nonce(), b0.nonce());
848 }
849
850 #[test]
851 fn test_enforce_blobfee_tracks_highest_nonce_of_partially_removed_sender() {
852 let mut f = MockTransactionFactory::default();
853 let mut pool = PendingPool::new(MockOrdering::default());
854
855 let a = MockTransaction::eip4844().with_blob_fee(150);
857 let a0 = f.validated_arc(a.clone());
858 let a1 = f.validated_arc(a.inc_nonce().rng_hash().with_blob_fee(50));
859 let b = MockTransaction::eip4844().with_blob_fee(150);
861 let b0 = f.validated_arc(b.clone());
862 let b1 = f.validated_arc(b.inc_nonce().rng_hash());
863
864 for tx in [&a0, &a1, &b0, &b1] {
865 pool.add_transaction(tx.clone(), 0);
866 }
867
868 let removed = pool.update_blob_fee(100);
869 assert_eq!(removed.len(), 1);
870 assert_eq!(removed[0].hash(), a1.hash());
871 pool.assert_invariants();
872 }
873
874 #[test]
875 fn evict_worst() {
876 let mut f = MockTransactionFactory::default();
877 let mut pool = PendingPool::new(MockOrdering::default());
878
879 let t = MockTransaction::eip1559();
880 pool.add_transaction(f.validated_arc(t.clone()), 0);
881
882 let t2 = MockTransaction::eip1559().inc_price_by(10);
883 pool.add_transaction(f.validated_arc(t2), 0);
884
885 assert_eq!(
887 pool.highest_nonces.values().min().map(|tx| *tx.transaction.hash()),
888 Some(*t.hash())
889 );
890
891 let removed = pool.truncate_pool(SubPoolLimit { max_txs: 1, max_size: usize::MAX });
893 assert_eq!(removed.len(), 1);
894 assert_eq!(removed[0].hash(), t.hash());
895 }
896
897 #[test]
898 fn correct_independent_descendants() {
899 let mut f = MockTransactionFactory::default();
901 let mut pool = PendingPool::new(MockOrdering::default());
902
903 let a_sender = address!("0x000000000000000000000000000000000000000a");
904 let b_sender = address!("0x000000000000000000000000000000000000000b");
905 let c_sender = address!("0x000000000000000000000000000000000000000c");
906 let d_sender = address!("0x000000000000000000000000000000000000000d");
907
908 let mut tx_set = MockTransactionSet::dependent(a_sender, 0, 4, TxType::Eip1559);
910 let a = tx_set.clone().into_vec();
911
912 let b = MockTransactionSet::dependent(b_sender, 0, 3, TxType::Eip1559).into_vec();
913 tx_set.extend(b.clone());
914
915 let c = MockTransactionSet::dependent(c_sender, 0, 3, TxType::Eip1559).into_vec();
917 tx_set.extend(c.clone());
918
919 let d = MockTransactionSet::dependent(d_sender, 0, 1, TxType::Eip1559).into_vec();
920 tx_set.extend(d.clone());
921
922 let all_txs = tx_set.into_vec();
924 for tx in all_txs {
925 pool.add_transaction(f.validated_arc(tx), 0);
926 }
927
928 pool.assert_invariants();
929
930 let expected_highest_nonces = [d[0].clone(), c[2].clone(), b[2].clone(), a[3].clone()]
933 .iter()
934 .map(|tx| (tx.sender(), tx.nonce()))
935 .collect::<HashSet<_>>();
936 let actual_highest_nonces = pool
937 .highest_nonces
938 .values()
939 .map(|tx| (tx.transaction.sender(), tx.transaction.nonce()))
940 .collect::<HashSet<_>>();
941 assert_eq!(expected_highest_nonces, actual_highest_nonces);
942 pool.assert_invariants();
943 }
944
945 #[test]
946 fn truncate_by_sender() {
947 let mut f = MockTransactionFactory::default();
949 let mut pool = PendingPool::new(MockOrdering::default());
950
951 let a = address!("0x000000000000000000000000000000000000000a");
953 let b = address!("0x000000000000000000000000000000000000000b");
954 let c = address!("0x000000000000000000000000000000000000000c");
955 let d = address!("0x000000000000000000000000000000000000000d");
956
957 let a_txs = MockTransactionSet::sequential_transactions_by_sender(a, 4, TxType::Eip1559);
959 let b_txs = MockTransactionSet::sequential_transactions_by_sender(b, 3, TxType::Eip1559);
960 let c_txs = MockTransactionSet::sequential_transactions_by_sender(c, 3, TxType::Eip1559);
961 let d_txs = MockTransactionSet::sequential_transactions_by_sender(d, 1, TxType::Eip1559);
962
963 let expected_pending = vec![
965 a_txs.transactions[0].clone(),
966 b_txs.transactions[0].clone(),
967 c_txs.transactions[0].clone(),
968 a_txs.transactions[1].clone(),
969 ]
970 .into_iter()
971 .map(|tx| (tx.sender(), tx.nonce()))
972 .collect::<HashSet<_>>();
973
974 let expected_removed = vec![
976 d_txs.transactions[0].clone(),
977 c_txs.transactions[2].clone(),
978 b_txs.transactions[2].clone(),
979 a_txs.transactions[3].clone(),
980 c_txs.transactions[1].clone(),
981 b_txs.transactions[1].clone(),
982 a_txs.transactions[2].clone(),
983 ]
984 .into_iter()
985 .map(|tx| (tx.sender(), tx.nonce()))
986 .collect::<HashSet<_>>();
987
988 let all_txs =
990 [a_txs.into_vec(), b_txs.into_vec(), c_txs.into_vec(), d_txs.into_vec()].concat();
991
992 for tx in all_txs {
994 pool.add_transaction(f.validated_arc(tx), 0);
995 }
996
997 pool.assert_invariants();
999
1000 let pool_limit = SubPoolLimit { max_txs: 4, max_size: usize::MAX };
1010
1011 let removed = pool.truncate_pool(pool_limit);
1013 pool.assert_invariants();
1014 assert_eq!(removed.len(), expected_removed.len());
1015
1016 let removed =
1018 removed.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::<HashSet<_>>();
1019 assert_eq!(removed, expected_removed);
1020
1021 let pending = pool.all().collect::<Vec<_>>();
1023 assert_eq!(pending.len(), expected_pending.len());
1024
1025 let pending =
1027 pending.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::<HashSet<_>>();
1028 assert_eq!(pending, expected_pending);
1029 }
1030
1031 #[test]
1033 fn test_eligible_updates_promoted() {
1034 let mut pool = PendingPool::new(MockOrdering::default());
1035 let mut f = MockTransactionFactory::default();
1036
1037 let num_senders = 10;
1038
1039 let first_txs: Vec<_> = (0..num_senders) .map(|_| MockTransaction::eip1559())
1041 .collect();
1042 let second_txs: Vec<_> =
1043 first_txs.iter().map(|tx| tx.clone().rng_hash().inc_nonce()).collect();
1044
1045 for tx in first_txs {
1046 let valid_tx = f.validated(tx);
1047 pool.add_transaction(Arc::new(valid_tx), 0);
1048 }
1049
1050 let mut best = pool.best();
1051
1052 for _ in 0..num_senders {
1053 if let Some(tx) = best.next() {
1054 assert_eq!(tx.nonce(), 0);
1055 } else {
1056 panic!("cannot read one of first_txs");
1057 }
1058 }
1059
1060 for tx in second_txs {
1061 let valid_tx = f.validated(tx);
1062 pool.add_transaction(Arc::new(valid_tx), 0);
1063 }
1064
1065 for _ in 0..num_senders {
1066 if let Some(tx) = best.next() {
1067 assert_eq!(tx.nonce(), 1);
1068 } else {
1069 panic!("cannot read one of second_txs");
1070 }
1071 }
1072 }
1073
1074 #[test]
1075 fn test_empty_pool_behavior() {
1076 let mut pool = PendingPool::<MockOrdering>::new(MockOrdering::default());
1077
1078 assert!(pool.is_empty());
1080 assert_eq!(pool.len(), 0);
1081 assert_eq!(pool.size(), 0);
1082
1083 let removed = pool.truncate_pool(SubPoolLimit { max_txs: 10, max_size: 1000 });
1085 assert!(removed.is_empty());
1086
1087 assert!(pool.all().next().is_none());
1089 }
1090
1091 #[test]
1092 fn test_add_remove_transaction() {
1093 let mut f = MockTransactionFactory::default();
1094 let mut pool = PendingPool::new(MockOrdering::default());
1095
1096 let tx = f.validated_arc(MockTransaction::eip1559());
1098 pool.add_transaction(tx.clone(), 0);
1099 assert!(pool.contains(tx.id()));
1100 assert_eq!(pool.len(), 1);
1101
1102 let removed_tx = pool.remove_transaction(tx.id()).unwrap();
1104 assert_eq!(removed_tx.id(), tx.id());
1105 assert!(!pool.contains(tx.id()));
1106 assert_eq!(pool.len(), 0);
1107 }
1108
1109 #[test]
1110 fn test_reorder_on_basefee_update() {
1111 let mut f = MockTransactionFactory::default();
1112 let mut pool = PendingPool::new(MockOrdering::default());
1113
1114 let tx1 = f.validated_arc(MockTransaction::eip1559().inc_price());
1116 let tx2 = f.validated_arc(MockTransaction::eip1559().inc_price_by(20));
1117 pool.add_transaction(tx1.clone(), 0);
1118 pool.add_transaction(tx2.clone(), 0);
1119
1120 let mut best = pool.best();
1122 assert_eq!(best.next().unwrap().hash(), tx2.hash());
1123 assert_eq!(best.next().unwrap().hash(), tx1.hash());
1124
1125 let removed = pool.update_base_fee((tx1.max_fee_per_gas() + 1) as u64);
1127 assert_eq!(removed.len(), 1);
1128 assert_eq!(removed[0].hash(), tx1.hash());
1129
1130 assert_eq!(pool.len(), 1);
1132 assert!(pool.contains(tx2.id()));
1133 assert!(!pool.contains(tx1.id()));
1134 }
1135
1136 #[test]
1137 #[cfg(debug_assertions)]
1138 #[should_panic(expected = "transaction already included")]
1139 fn test_handle_duplicates() {
1140 let mut f = MockTransactionFactory::default();
1141 let mut pool = PendingPool::new(MockOrdering::default());
1142
1143 let tx = f.validated_arc(MockTransaction::eip1559());
1145 pool.add_transaction(tx.clone(), 0);
1146 assert!(pool.contains(tx.id()));
1147 assert_eq!(pool.len(), 1);
1148
1149 pool.add_transaction(tx, 0);
1151 }
1152
1153 #[test]
1154 fn test_update_blob_fee() {
1155 let mut f = MockTransactionFactory::default();
1156 let mut pool = PendingPool::new(MockOrdering::default());
1157
1158 let tx1 = f.validated_arc(MockTransaction::eip4844().set_blob_fee(50).clone());
1160 let tx2 = f.validated_arc(MockTransaction::eip4844().set_blob_fee(150).clone());
1161 pool.add_transaction(tx1.clone(), 0);
1162 pool.add_transaction(tx2.clone(), 0);
1163
1164 let removed = pool.update_blob_fee(100);
1166 assert_eq!(removed.len(), 1);
1167 assert_eq!(removed[0].hash(), tx1.hash());
1168
1169 assert!(pool.contains(tx2.id()));
1171 assert!(!pool.contains(tx1.id()));
1172 }
1173
1174 #[test]
1175 fn local_senders_tracking() {
1176 let mut f = MockTransactionFactory::default();
1177 let mut pool = PendingPool::new(MockOrdering::default());
1178
1179 let a = address!("0x000000000000000000000000000000000000000a");
1181 let b = address!("0x000000000000000000000000000000000000000b");
1182 let c = address!("0x000000000000000000000000000000000000000c");
1183
1184 let a_txs = MockTransactionSet::sequential_transactions_by_sender(a, 11, TxType::Eip1559);
1190 let b_txs = MockTransactionSet::sequential_transactions_by_sender(b, 2, TxType::Eip1559);
1191 let c_txs = MockTransactionSet::sequential_transactions_by_sender(c, 2, TxType::Eip1559);
1192
1193 for tx in a_txs.into_vec() {
1195 let final_tx = Arc::new(f.validated_with_origin(crate::TransactionOrigin::Local, tx));
1196
1197 pool.add_transaction(final_tx, 0);
1198 }
1199
1200 let remaining_txs = [b_txs.into_vec(), c_txs.into_vec()].concat();
1202 for tx in remaining_txs {
1203 let final_tx = f.validated_arc(tx);
1204
1205 pool.add_transaction(final_tx, 0);
1206 }
1207
1208 pool.assert_invariants();
1210
1211 let pool_limit = SubPoolLimit { max_txs: 10, max_size: usize::MAX };
1212 pool.truncate_pool(pool_limit);
1213
1214 let sender_a = f.ids.sender_id(&a).unwrap();
1215 let sender_b = f.ids.sender_id(&b).unwrap();
1216 let sender_c = f.ids.sender_id(&c).unwrap();
1217
1218 assert_eq!(pool.get_txs_by_sender(sender_a).len(), 10);
1219 assert!(pool.get_txs_by_sender(sender_b).is_empty());
1220 assert!(pool.get_txs_by_sender(sender_c).is_empty());
1221 }
1222
1223 #[test]
1224 fn test_remove_non_highest_keeps_highest() {
1225 let mut f = MockTransactionFactory::default();
1226 let mut pool = PendingPool::new(MockOrdering::default());
1227 let sender = address!("0x00000000000000000000000000000000000000aa");
1228 let txs = MockTransactionSet::dependent(sender, 0, 3, TxType::Eip1559).into_vec();
1229 for tx in txs {
1230 pool.add_transaction(f.validated_arc(tx), 0);
1231 }
1232 pool.assert_invariants();
1233 let sender_id = f.ids.sender_id(&sender).unwrap();
1234 let mid_id = TransactionId::new(sender_id, 1);
1235 let _ = pool.remove_transaction(&mid_id);
1236 let highest = pool.highest_nonces.get(&sender_id).unwrap();
1237 assert_eq!(highest.transaction.nonce(), 2);
1238 pool.assert_invariants();
1239 }
1240
1241 #[test]
1242 fn test_cascade_removal_recomputes_highest() {
1243 let mut f = MockTransactionFactory::default();
1244 let mut pool = PendingPool::new(MockOrdering::default());
1245 let sender = address!("0x00000000000000000000000000000000000000bb");
1246 let txs = MockTransactionSet::dependent(sender, 0, 4, TxType::Eip1559).into_vec();
1247 for tx in txs {
1248 pool.add_transaction(f.validated_arc(tx), 0);
1249 }
1250 pool.assert_invariants();
1251 let sender_id = f.ids.sender_id(&sender).unwrap();
1252 let id3 = TransactionId::new(sender_id, 3);
1253 let _ = pool.remove_transaction(&id3);
1254 let highest = pool.highest_nonces.get(&sender_id).unwrap();
1255 assert_eq!(highest.transaction.nonce(), 2);
1256 let id2 = TransactionId::new(sender_id, 2);
1257 let _ = pool.remove_transaction(&id2);
1258 let highest = pool.highest_nonces.get(&sender_id).unwrap();
1259 assert_eq!(highest.transaction.nonce(), 1);
1260 pool.assert_invariants();
1261 }
1262
1263 #[test]
1264 fn test_remove_only_tx_clears_highest() {
1265 let mut f = MockTransactionFactory::default();
1266 let mut pool = PendingPool::new(MockOrdering::default());
1267 let sender = address!("0x00000000000000000000000000000000000000cc");
1268 let txs = MockTransactionSet::dependent(sender, 0, 1, TxType::Eip1559).into_vec();
1269 for tx in txs {
1270 pool.add_transaction(f.validated_arc(tx), 0);
1271 }
1272 pool.assert_invariants();
1273 let sender_id = f.ids.sender_id(&sender).unwrap();
1274 let id0 = TransactionId::new(sender_id, 0);
1275 let _ = pool.remove_transaction(&id0);
1276 assert!(!pool.highest_nonces.contains_key(&sender_id));
1277 pool.assert_invariants();
1278 }
1279}