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 while let Some((id, tx)) = transactions_iter.next() {
182 if tx.transaction.is_eip4844() && tx.transaction.max_fee_per_blob_gas() < Some(blob_fee)
183 {
184 removed.push(Arc::clone(&tx.transaction));
187
188 'this: while let Some((next_id, next_tx)) = transactions_iter.peek() {
190 if next_id.sender != id.sender {
191 break 'this
192 }
193 removed.push(Arc::clone(&next_tx.transaction));
194 transactions_iter.next();
195 }
196 } else {
197 self.size_of += tx.transaction.size();
198 self.update_independents_and_highest_nonces(&tx);
199 self.by_id.insert(id, tx);
200 }
201 }
202
203 removed
204 }
205
206 pub(crate) fn update_base_fee(
216 &mut self,
217 base_fee: u64,
218 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
219 let mut removed = Vec::new();
221
222 let mut transactions_iter = self.clear_transactions().into_iter().peekable();
224 while let Some((id, mut tx)) = transactions_iter.next() {
225 if tx.transaction.max_fee_per_gas() < base_fee as u128 {
226 removed.push(Arc::clone(&tx.transaction));
229
230 'this: while let Some((next_id, next_tx)) = transactions_iter.peek() {
232 if next_id.sender != id.sender {
233 break 'this
234 }
235 removed.push(Arc::clone(&next_tx.transaction));
236 transactions_iter.next();
237 }
238 } else {
239 tx.priority = self.ordering.priority(&tx.transaction.transaction, base_fee);
241
242 self.size_of += tx.transaction.size();
243 self.update_independents_and_highest_nonces(&tx);
244 self.by_id.insert(id, tx);
245 }
246 }
247
248 removed
249 }
250
251 fn update_independents_and_highest_nonces(&mut self, tx: &PendingTransaction<T>) {
254 match self.highest_nonces.entry(tx.transaction.sender_id()) {
255 Entry::Occupied(mut entry) => {
256 if entry.get().transaction.nonce() < tx.transaction.nonce() {
257 *entry.get_mut() = tx.clone();
258 }
259 }
260 Entry::Vacant(entry) => {
261 entry.insert(tx.clone());
262 }
263 }
264 match self.independent_transactions.entry(tx.transaction.sender_id()) {
265 Entry::Occupied(mut entry) => {
266 if entry.get().transaction.nonce() > tx.transaction.nonce() {
267 *entry.get_mut() = tx.clone();
268 }
269 }
270 Entry::Vacant(entry) => {
271 entry.insert(tx.clone());
272 }
273 }
274 }
275
276 pub fn add_transaction(
282 &mut self,
283 tx: Arc<ValidPoolTransaction<T::Transaction>>,
284 base_fee: u64,
285 ) {
286 debug_assert!(
287 !self.contains(tx.id()),
288 "transaction already included {:?}",
289 self.get(tx.id()).unwrap().transaction
290 );
291
292 self.size_of += tx.size();
294
295 let tx_id = *tx.id();
296
297 let submission_id = self.next_id();
298 let priority = self.ordering.priority(&tx.transaction, base_fee);
299 let tx = PendingTransaction { submission_id, transaction: tx, priority };
300
301 self.update_independents_and_highest_nonces(&tx);
302
303 if self.new_transaction_notifier.receiver_count() > 0 {
305 let _ = self.new_transaction_notifier.send(tx.clone());
306 }
307
308 self.by_id.insert(tx_id, tx);
309 }
310
311 pub(crate) fn remove_transaction(
316 &mut self,
317 id: &TransactionId,
318 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
319 if let Some(lowest) = self.independent_transactions.get(&id.sender) &&
320 lowest.transaction.nonce() == id.nonce
321 {
322 self.independent_transactions.remove(&id.sender);
323 if let Some(unlocked) = self.get(&id.descendant()) {
325 self.independent_transactions.insert(id.sender, unlocked.clone());
326 }
327 }
328
329 let tx = self.by_id.remove(id)?;
330 self.size_of -= tx.transaction.size();
331
332 match self.highest_nonces.entry(id.sender) {
333 Entry::Occupied(mut entry) => {
334 if entry.get().transaction.nonce() == id.nonce {
335 if let Some((_, new_highest)) = self
338 .by_id
339 .range((
340 id.sender.start_bound(),
341 std::ops::Bound::Included(TransactionId::new(id.sender, u64::MAX)),
342 ))
343 .last()
344 {
345 entry.insert(new_highest.clone());
347 } else {
348 entry.remove();
349 }
350 }
351 }
352 Entry::Vacant(_) => {
353 debug_assert!(
354 false,
355 "removed transaction without a tracked highest nonce {:?}",
356 id
357 );
358 }
359 }
360
361 Some(tx.transaction)
362 }
363
364 const fn next_id(&mut self) -> u64 {
365 let id = self.submission_id;
366 self.submission_id = self.submission_id.wrapping_add(1);
367 id
368 }
369
370 pub fn remove_to_limit(
385 &mut self,
386 limit: &SubPoolLimit,
387 remove_locals: bool,
388 end_removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
389 ) {
390 let mut non_local_senders = self.highest_nonces.len();
399
400 let mut unique_senders = self.highest_nonces.len();
403
404 let mut local_senders = FxHashSet::default();
406
407 let original_length = self.len();
409 let mut removed = Vec::new();
410 let mut total_removed = 0;
411
412 let original_size = self.size();
414 let mut total_size = 0;
415
416 loop {
417 let unique_removed = unique_senders - self.highest_nonces.len();
419
420 unique_senders = self.highest_nonces.len();
422 non_local_senders -= unique_removed;
423
424 removed.clear();
426
427 let mut worst_transactions = self.highest_nonces.values().collect::<Vec<_>>();
429
430 let current_len = original_length - total_removed;
435 let current_size = original_size - total_size;
436 let excess_txs = current_len.saturating_sub(limit.max_txs);
437 let avg_tx_size = (current_size / current_len.max(1)).max(1);
438 let excess_size_txs = current_size.saturating_sub(limit.max_size).div_ceil(avg_tx_size);
439 let removal_candidates = excess_txs.max(excess_size_txs).max(1) + local_senders.len();
443
444 if removal_candidates < worst_transactions.len() {
445 worst_transactions.select_nth_unstable(removal_candidates);
447 worst_transactions.truncate(removal_candidates);
448 }
449 worst_transactions.sort_unstable();
450
451 for tx in worst_transactions {
453 if !limit.is_exceeded(original_length - total_removed, original_size - total_size) ||
455 non_local_senders == 0
456 {
457 for id in &removed {
459 if let Some(tx) = self.remove_transaction(id) {
460 end_removed.push(tx);
461 }
462 }
463
464 return
465 }
466
467 if !remove_locals && tx.transaction.is_local() {
468 let sender_id = tx.transaction.sender_id();
469 if local_senders.insert(sender_id) {
470 non_local_senders -= 1;
471 }
472 continue
473 }
474
475 total_size += tx.transaction.size();
476 total_removed += 1;
477 removed.push(*tx.transaction.id());
478 }
479
480 for id in &removed {
482 if let Some(tx) = self.remove_transaction(id) {
483 end_removed.push(tx);
484 }
485 }
486
487 if !self.exceeds(limit) || non_local_senders == 0 {
490 return
491 }
492 }
493 }
494
495 pub fn truncate_pool(
506 &mut self,
507 limit: SubPoolLimit,
508 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
509 let mut removed = Vec::new();
510 if !self.exceeds(&limit) {
512 return removed
513 }
514
515 self.remove_to_limit(&limit, false, &mut removed);
517 if !self.exceeds(&limit) {
518 return removed
519 }
520
521 self.remove_to_limit(&limit, true, &mut removed);
524
525 removed
526 }
527
528 #[inline]
530 pub(crate) fn exceeds(&self, limit: &SubPoolLimit) -> bool {
531 limit.is_exceeded(self.len(), self.size())
532 }
533
534 pub(crate) fn size(&self) -> usize {
536 self.size_of.into()
537 }
538
539 pub(crate) fn len(&self) -> usize {
541 self.by_id.len()
542 }
543
544 pub const fn by_id(&self) -> &OrdMap<TransactionId, PendingTransaction<T>> {
546 &self.by_id
547 }
548
549 pub const fn independent_transactions(&self) -> &FxHashMap<SenderId, PendingTransaction<T>> {
551 &self.independent_transactions
552 }
553
554 pub fn new_transaction_receiver(&self) -> broadcast::Receiver<PendingTransaction<T>> {
556 self.new_transaction_notifier.subscribe()
557 }
558
559 #[cfg(test)]
561 pub(crate) fn is_empty(&self) -> bool {
562 self.by_id.is_empty()
563 }
564
565 pub(crate) fn contains(&self, id: &TransactionId) -> bool {
567 self.by_id.contains_key(id)
568 }
569
570 pub(crate) fn get_txs_by_sender(&self, sender: SenderId) -> Vec<TransactionId> {
572 self.iter_txs_by_sender(sender).copied().collect()
573 }
574
575 pub(crate) fn iter_txs_by_sender(
577 &self,
578 sender: SenderId,
579 ) -> impl Iterator<Item = &TransactionId> + '_ {
580 self.by_id
581 .range((sender.start_bound(), Unbounded))
582 .take_while(move |(other, _)| sender == other.sender)
583 .map(|(tx_id, _)| tx_id)
584 }
585
586 pub(crate) fn txs_by_sender(
588 &self,
589 sender: SenderId,
590 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
591 self.by_id
592 .range((sender.start_bound(), Unbounded))
593 .take_while(move |(other, _)| sender == other.sender)
594 .map(|(_, tx)| tx.transaction.clone())
595 .collect()
596 }
597
598 fn get(&self, id: &TransactionId) -> Option<&PendingTransaction<T>> {
600 self.by_id.get(id)
601 }
602
603 #[cfg(test)]
605 pub(crate) const fn independent(&self) -> &FxHashMap<SenderId, PendingTransaction<T>> {
606 &self.independent_transactions
607 }
608
609 #[cfg(any(test, feature = "test-utils"))]
611 pub(crate) fn assert_invariants(&self) {
612 assert!(
613 self.independent_transactions.len() <= self.by_id.len(),
614 "independent_transactions.len() > by_id.len()"
615 );
616 assert!(
617 self.highest_nonces.len() <= self.by_id.len(),
618 "highest_nonces.len() > by_id.len()"
619 );
620 assert_eq!(
621 self.highest_nonces.len(),
622 self.independent_transactions.len(),
623 "highest_nonces.len() != independent_transactions.len()"
624 );
625 }
626}
627
628#[derive(Debug)]
630pub struct PendingTransaction<T: TransactionOrdering> {
631 pub submission_id: u64,
633 pub transaction: Arc<ValidPoolTransaction<T::Transaction>>,
635 pub priority: Priority<T::PriorityValue>,
637}
638
639impl<T: TransactionOrdering> PendingTransaction<T> {
640 pub fn unlocks(&self) -> TransactionId {
642 self.transaction.transaction_id.descendant()
643 }
644}
645
646impl<T: TransactionOrdering> Clone for PendingTransaction<T> {
647 fn clone(&self) -> Self {
648 Self {
649 submission_id: self.submission_id,
650 transaction: Arc::clone(&self.transaction),
651 priority: self.priority.clone(),
652 }
653 }
654}
655
656impl<T: TransactionOrdering> Eq for PendingTransaction<T> {}
657
658impl<T: TransactionOrdering> PartialEq<Self> for PendingTransaction<T> {
659 fn eq(&self, other: &Self) -> bool {
660 self.cmp(other) == Ordering::Equal
661 }
662}
663
664impl<T: TransactionOrdering> PartialOrd<Self> for PendingTransaction<T> {
665 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
666 Some(self.cmp(other))
667 }
668}
669
670impl<T: TransactionOrdering> Ord for PendingTransaction<T> {
671 fn cmp(&self, other: &Self) -> Ordering {
672 self.priority
676 .cmp(&other.priority)
677 .then_with(|| other.submission_id.cmp(&self.submission_id))
678 }
679}
680
681#[cfg(test)]
682mod tests {
683 use super::*;
684 use crate::{
685 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
686 PoolTransaction,
687 };
688 use alloy_consensus::{Transaction, TxType};
689 use alloy_primitives::address;
690 use std::collections::HashSet;
691
692 #[test]
693 fn test_enforce_basefee() {
694 let mut f = MockTransactionFactory::default();
695 let mut pool = PendingPool::new(MockOrdering::default());
696 let tx = f.validated_arc(MockTransaction::eip1559().inc_price());
697 pool.add_transaction(tx.clone(), 0);
698
699 assert!(pool.contains(tx.id()));
700 assert_eq!(pool.len(), 1);
701
702 let removed = pool.update_base_fee(0);
703 assert!(removed.is_empty());
704
705 let removed = pool.update_base_fee((tx.max_fee_per_gas() + 1) as u64);
706 assert_eq!(removed.len(), 1);
707 assert!(pool.is_empty());
708 }
709
710 #[test]
711 fn test_enforce_basefee_descendant() {
712 let mut f = MockTransactionFactory::default();
713 let mut pool = PendingPool::new(MockOrdering::default());
714 let t = MockTransaction::eip1559().inc_price_by(10);
715 let root_tx = f.validated_arc(t.clone());
716 pool.add_transaction(root_tx.clone(), 0);
717
718 let descendant_tx = f.validated_arc(t.inc_nonce().decr_price());
719 pool.add_transaction(descendant_tx.clone(), 0);
720
721 assert!(pool.contains(root_tx.id()));
722 assert!(pool.contains(descendant_tx.id()));
723 assert_eq!(pool.len(), 2);
724
725 assert_eq!(pool.independent_transactions.len(), 1);
726 assert_eq!(pool.highest_nonces.len(), 1);
727
728 let removed = pool.update_base_fee(0);
729 assert!(removed.is_empty());
730
731 {
734 let mut pool2 = pool.clone();
735 let removed = pool2.update_base_fee((descendant_tx.max_fee_per_gas() + 1) as u64);
736 assert_eq!(removed.len(), 1);
737 assert_eq!(pool2.len(), 1);
738 assert!(pool2.contains(root_tx.id()));
740 assert!(!pool2.contains(descendant_tx.id()));
741 }
742
743 let removed = pool.update_base_fee((root_tx.max_fee_per_gas() + 1) as u64);
745 assert_eq!(removed.len(), 2);
746 assert!(pool.is_empty());
747 pool.assert_invariants();
748 }
749
750 #[test]
751 fn evict_worst() {
752 let mut f = MockTransactionFactory::default();
753 let mut pool = PendingPool::new(MockOrdering::default());
754
755 let t = MockTransaction::eip1559();
756 pool.add_transaction(f.validated_arc(t.clone()), 0);
757
758 let t2 = MockTransaction::eip1559().inc_price_by(10);
759 pool.add_transaction(f.validated_arc(t2), 0);
760
761 assert_eq!(
763 pool.highest_nonces.values().min().map(|tx| *tx.transaction.hash()),
764 Some(*t.hash())
765 );
766
767 let removed = pool.truncate_pool(SubPoolLimit { max_txs: 1, max_size: usize::MAX });
769 assert_eq!(removed.len(), 1);
770 assert_eq!(removed[0].hash(), t.hash());
771 }
772
773 #[test]
774 fn correct_independent_descendants() {
775 let mut f = MockTransactionFactory::default();
777 let mut pool = PendingPool::new(MockOrdering::default());
778
779 let a_sender = address!("0x000000000000000000000000000000000000000a");
780 let b_sender = address!("0x000000000000000000000000000000000000000b");
781 let c_sender = address!("0x000000000000000000000000000000000000000c");
782 let d_sender = address!("0x000000000000000000000000000000000000000d");
783
784 let mut tx_set = MockTransactionSet::dependent(a_sender, 0, 4, TxType::Eip1559);
786 let a = tx_set.clone().into_vec();
787
788 let b = MockTransactionSet::dependent(b_sender, 0, 3, TxType::Eip1559).into_vec();
789 tx_set.extend(b.clone());
790
791 let c = MockTransactionSet::dependent(c_sender, 0, 3, TxType::Eip1559).into_vec();
793 tx_set.extend(c.clone());
794
795 let d = MockTransactionSet::dependent(d_sender, 0, 1, TxType::Eip1559).into_vec();
796 tx_set.extend(d.clone());
797
798 let all_txs = tx_set.into_vec();
800 for tx in all_txs {
801 pool.add_transaction(f.validated_arc(tx), 0);
802 }
803
804 pool.assert_invariants();
805
806 let expected_highest_nonces = [d[0].clone(), c[2].clone(), b[2].clone(), a[3].clone()]
809 .iter()
810 .map(|tx| (tx.sender(), tx.nonce()))
811 .collect::<HashSet<_>>();
812 let actual_highest_nonces = pool
813 .highest_nonces
814 .values()
815 .map(|tx| (tx.transaction.sender(), tx.transaction.nonce()))
816 .collect::<HashSet<_>>();
817 assert_eq!(expected_highest_nonces, actual_highest_nonces);
818 pool.assert_invariants();
819 }
820
821 #[test]
822 fn truncate_by_sender() {
823 let mut f = MockTransactionFactory::default();
825 let mut pool = PendingPool::new(MockOrdering::default());
826
827 let a = address!("0x000000000000000000000000000000000000000a");
829 let b = address!("0x000000000000000000000000000000000000000b");
830 let c = address!("0x000000000000000000000000000000000000000c");
831 let d = address!("0x000000000000000000000000000000000000000d");
832
833 let a_txs = MockTransactionSet::sequential_transactions_by_sender(a, 4, TxType::Eip1559);
835 let b_txs = MockTransactionSet::sequential_transactions_by_sender(b, 3, TxType::Eip1559);
836 let c_txs = MockTransactionSet::sequential_transactions_by_sender(c, 3, TxType::Eip1559);
837 let d_txs = MockTransactionSet::sequential_transactions_by_sender(d, 1, TxType::Eip1559);
838
839 let expected_pending = vec![
841 a_txs.transactions[0].clone(),
842 b_txs.transactions[0].clone(),
843 c_txs.transactions[0].clone(),
844 a_txs.transactions[1].clone(),
845 ]
846 .into_iter()
847 .map(|tx| (tx.sender(), tx.nonce()))
848 .collect::<HashSet<_>>();
849
850 let expected_removed = vec![
852 d_txs.transactions[0].clone(),
853 c_txs.transactions[2].clone(),
854 b_txs.transactions[2].clone(),
855 a_txs.transactions[3].clone(),
856 c_txs.transactions[1].clone(),
857 b_txs.transactions[1].clone(),
858 a_txs.transactions[2].clone(),
859 ]
860 .into_iter()
861 .map(|tx| (tx.sender(), tx.nonce()))
862 .collect::<HashSet<_>>();
863
864 let all_txs =
866 [a_txs.into_vec(), b_txs.into_vec(), c_txs.into_vec(), d_txs.into_vec()].concat();
867
868 for tx in all_txs {
870 pool.add_transaction(f.validated_arc(tx), 0);
871 }
872
873 pool.assert_invariants();
875
876 let pool_limit = SubPoolLimit { max_txs: 4, max_size: usize::MAX };
886
887 let removed = pool.truncate_pool(pool_limit);
889 pool.assert_invariants();
890 assert_eq!(removed.len(), expected_removed.len());
891
892 let removed =
894 removed.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::<HashSet<_>>();
895 assert_eq!(removed, expected_removed);
896
897 let pending = pool.all().collect::<Vec<_>>();
899 assert_eq!(pending.len(), expected_pending.len());
900
901 let pending =
903 pending.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::<HashSet<_>>();
904 assert_eq!(pending, expected_pending);
905 }
906
907 #[test]
909 fn test_eligible_updates_promoted() {
910 let mut pool = PendingPool::new(MockOrdering::default());
911 let mut f = MockTransactionFactory::default();
912
913 let num_senders = 10;
914
915 let first_txs: Vec<_> = (0..num_senders) .map(|_| MockTransaction::eip1559())
917 .collect();
918 let second_txs: Vec<_> =
919 first_txs.iter().map(|tx| tx.clone().rng_hash().inc_nonce()).collect();
920
921 for tx in first_txs {
922 let valid_tx = f.validated(tx);
923 pool.add_transaction(Arc::new(valid_tx), 0);
924 }
925
926 let mut best = pool.best();
927
928 for _ in 0..num_senders {
929 if let Some(tx) = best.next() {
930 assert_eq!(tx.nonce(), 0);
931 } else {
932 panic!("cannot read one of first_txs");
933 }
934 }
935
936 for tx in second_txs {
937 let valid_tx = f.validated(tx);
938 pool.add_transaction(Arc::new(valid_tx), 0);
939 }
940
941 for _ in 0..num_senders {
942 if let Some(tx) = best.next() {
943 assert_eq!(tx.nonce(), 1);
944 } else {
945 panic!("cannot read one of second_txs");
946 }
947 }
948 }
949
950 #[test]
951 fn test_empty_pool_behavior() {
952 let mut pool = PendingPool::<MockOrdering>::new(MockOrdering::default());
953
954 assert!(pool.is_empty());
956 assert_eq!(pool.len(), 0);
957 assert_eq!(pool.size(), 0);
958
959 let removed = pool.truncate_pool(SubPoolLimit { max_txs: 10, max_size: 1000 });
961 assert!(removed.is_empty());
962
963 assert!(pool.all().next().is_none());
965 }
966
967 #[test]
968 fn test_add_remove_transaction() {
969 let mut f = MockTransactionFactory::default();
970 let mut pool = PendingPool::new(MockOrdering::default());
971
972 let tx = f.validated_arc(MockTransaction::eip1559());
974 pool.add_transaction(tx.clone(), 0);
975 assert!(pool.contains(tx.id()));
976 assert_eq!(pool.len(), 1);
977
978 let removed_tx = pool.remove_transaction(tx.id()).unwrap();
980 assert_eq!(removed_tx.id(), tx.id());
981 assert!(!pool.contains(tx.id()));
982 assert_eq!(pool.len(), 0);
983 }
984
985 #[test]
986 fn test_reorder_on_basefee_update() {
987 let mut f = MockTransactionFactory::default();
988 let mut pool = PendingPool::new(MockOrdering::default());
989
990 let tx1 = f.validated_arc(MockTransaction::eip1559().inc_price());
992 let tx2 = f.validated_arc(MockTransaction::eip1559().inc_price_by(20));
993 pool.add_transaction(tx1.clone(), 0);
994 pool.add_transaction(tx2.clone(), 0);
995
996 let mut best = pool.best();
998 assert_eq!(best.next().unwrap().hash(), tx2.hash());
999 assert_eq!(best.next().unwrap().hash(), tx1.hash());
1000
1001 let removed = pool.update_base_fee((tx1.max_fee_per_gas() + 1) as u64);
1003 assert_eq!(removed.len(), 1);
1004 assert_eq!(removed[0].hash(), tx1.hash());
1005
1006 assert_eq!(pool.len(), 1);
1008 assert!(pool.contains(tx2.id()));
1009 assert!(!pool.contains(tx1.id()));
1010 }
1011
1012 #[test]
1013 #[cfg(debug_assertions)]
1014 #[should_panic(expected = "transaction already included")]
1015 fn test_handle_duplicates() {
1016 let mut f = MockTransactionFactory::default();
1017 let mut pool = PendingPool::new(MockOrdering::default());
1018
1019 let tx = f.validated_arc(MockTransaction::eip1559());
1021 pool.add_transaction(tx.clone(), 0);
1022 assert!(pool.contains(tx.id()));
1023 assert_eq!(pool.len(), 1);
1024
1025 pool.add_transaction(tx, 0);
1027 }
1028
1029 #[test]
1030 fn test_update_blob_fee() {
1031 let mut f = MockTransactionFactory::default();
1032 let mut pool = PendingPool::new(MockOrdering::default());
1033
1034 let tx1 = f.validated_arc(MockTransaction::eip4844().set_blob_fee(50).clone());
1036 let tx2 = f.validated_arc(MockTransaction::eip4844().set_blob_fee(150).clone());
1037 pool.add_transaction(tx1.clone(), 0);
1038 pool.add_transaction(tx2.clone(), 0);
1039
1040 let removed = pool.update_blob_fee(100);
1042 assert_eq!(removed.len(), 1);
1043 assert_eq!(removed[0].hash(), tx1.hash());
1044
1045 assert!(pool.contains(tx2.id()));
1047 assert!(!pool.contains(tx1.id()));
1048 }
1049
1050 #[test]
1051 fn local_senders_tracking() {
1052 let mut f = MockTransactionFactory::default();
1053 let mut pool = PendingPool::new(MockOrdering::default());
1054
1055 let a = address!("0x000000000000000000000000000000000000000a");
1057 let b = address!("0x000000000000000000000000000000000000000b");
1058 let c = address!("0x000000000000000000000000000000000000000c");
1059
1060 let a_txs = MockTransactionSet::sequential_transactions_by_sender(a, 11, TxType::Eip1559);
1066 let b_txs = MockTransactionSet::sequential_transactions_by_sender(b, 2, TxType::Eip1559);
1067 let c_txs = MockTransactionSet::sequential_transactions_by_sender(c, 2, TxType::Eip1559);
1068
1069 for tx in a_txs.into_vec() {
1071 let final_tx = Arc::new(f.validated_with_origin(crate::TransactionOrigin::Local, tx));
1072
1073 pool.add_transaction(final_tx, 0);
1074 }
1075
1076 let remaining_txs = [b_txs.into_vec(), c_txs.into_vec()].concat();
1078 for tx in remaining_txs {
1079 let final_tx = f.validated_arc(tx);
1080
1081 pool.add_transaction(final_tx, 0);
1082 }
1083
1084 pool.assert_invariants();
1086
1087 let pool_limit = SubPoolLimit { max_txs: 10, max_size: usize::MAX };
1088 pool.truncate_pool(pool_limit);
1089
1090 let sender_a = f.ids.sender_id(&a).unwrap();
1091 let sender_b = f.ids.sender_id(&b).unwrap();
1092 let sender_c = f.ids.sender_id(&c).unwrap();
1093
1094 assert_eq!(pool.get_txs_by_sender(sender_a).len(), 10);
1095 assert!(pool.get_txs_by_sender(sender_b).is_empty());
1096 assert!(pool.get_txs_by_sender(sender_c).is_empty());
1097 }
1098
1099 #[test]
1100 fn test_remove_non_highest_keeps_highest() {
1101 let mut f = MockTransactionFactory::default();
1102 let mut pool = PendingPool::new(MockOrdering::default());
1103 let sender = address!("0x00000000000000000000000000000000000000aa");
1104 let txs = MockTransactionSet::dependent(sender, 0, 3, TxType::Eip1559).into_vec();
1105 for tx in txs {
1106 pool.add_transaction(f.validated_arc(tx), 0);
1107 }
1108 pool.assert_invariants();
1109 let sender_id = f.ids.sender_id(&sender).unwrap();
1110 let mid_id = TransactionId::new(sender_id, 1);
1111 let _ = pool.remove_transaction(&mid_id);
1112 let highest = pool.highest_nonces.get(&sender_id).unwrap();
1113 assert_eq!(highest.transaction.nonce(), 2);
1114 pool.assert_invariants();
1115 }
1116
1117 #[test]
1118 fn test_cascade_removal_recomputes_highest() {
1119 let mut f = MockTransactionFactory::default();
1120 let mut pool = PendingPool::new(MockOrdering::default());
1121 let sender = address!("0x00000000000000000000000000000000000000bb");
1122 let txs = MockTransactionSet::dependent(sender, 0, 4, TxType::Eip1559).into_vec();
1123 for tx in txs {
1124 pool.add_transaction(f.validated_arc(tx), 0);
1125 }
1126 pool.assert_invariants();
1127 let sender_id = f.ids.sender_id(&sender).unwrap();
1128 let id3 = TransactionId::new(sender_id, 3);
1129 let _ = pool.remove_transaction(&id3);
1130 let highest = pool.highest_nonces.get(&sender_id).unwrap();
1131 assert_eq!(highest.transaction.nonce(), 2);
1132 let id2 = TransactionId::new(sender_id, 2);
1133 let _ = pool.remove_transaction(&id2);
1134 let highest = pool.highest_nonces.get(&sender_id).unwrap();
1135 assert_eq!(highest.transaction.nonce(), 1);
1136 pool.assert_invariants();
1137 }
1138
1139 #[test]
1140 fn test_remove_only_tx_clears_highest() {
1141 let mut f = MockTransactionFactory::default();
1142 let mut pool = PendingPool::new(MockOrdering::default());
1143 let sender = address!("0x00000000000000000000000000000000000000cc");
1144 let txs = MockTransactionSet::dependent(sender, 0, 1, TxType::Eip1559).into_vec();
1145 for tx in txs {
1146 pool.add_transaction(f.validated_arc(tx), 0);
1147 }
1148 pool.assert_invariants();
1149 let sender_id = f.ids.sender_id(&sender).unwrap();
1150 let id0 = TransactionId::new(sender_id, 0);
1151 let _ = pool.remove_transaction(&id0);
1152 assert!(!pool.highest_nonces.contains_key(&sender_id));
1153 pool.assert_invariants();
1154 }
1155}