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 }
113 }
114
115 pub(crate) fn best_with_basefee_and_blobfee(
117 &self,
118 base_fee: u64,
119 base_fee_per_blob_gas: u64,
120 ) -> BestTransactionsWithFees<T> {
121 BestTransactionsWithFees { best: self.best(), base_fee, base_fee_per_blob_gas }
122 }
123
124 pub(crate) fn best_with_unlocked_and_attributes(
135 &self,
136 unlocked: Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
137 base_fee: u64,
138 base_fee_per_blob_gas: u64,
139 ) -> BestTransactionsWithFees<T> {
140 let mut best = self.best();
141 for (submission_id, tx) in (self.submission_id + 1..).zip(unlocked) {
142 debug_assert!(!best.all.contains_key(tx.id()), "transaction already included");
143 let priority = self.ordering.priority(&tx.transaction, base_fee);
144 let tx_id = *tx.id();
145 let transaction = PendingTransaction { submission_id, transaction: tx, priority };
146 if best.ancestor(&tx_id).is_none() {
147 best.independent.insert(transaction.clone());
148 }
149 best.all.insert(tx_id, transaction);
150 }
151
152 BestTransactionsWithFees { best, base_fee, base_fee_per_blob_gas }
153 }
154
155 pub(crate) fn all(
157 &self,
158 ) -> impl ExactSizeIterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
159 self.by_id.values().map(|tx| tx.transaction.clone())
160 }
161
162 pub(crate) fn update_blob_fee(
172 &mut self,
173 blob_fee: u128,
174 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
175 let mut removed = Vec::new();
177
178 let mut transactions_iter = self.clear_transactions().into_iter().peekable();
180 while let Some((id, tx)) = transactions_iter.next() {
181 if tx.transaction.is_eip4844() && tx.transaction.max_fee_per_blob_gas() < Some(blob_fee)
182 {
183 removed.push(Arc::clone(&tx.transaction));
186
187 'this: while let Some((next_id, next_tx)) = transactions_iter.peek() {
189 if next_id.sender != id.sender {
190 break 'this
191 }
192 removed.push(Arc::clone(&next_tx.transaction));
193 transactions_iter.next();
194 }
195 } else {
196 self.size_of += tx.transaction.size();
197 self.update_independents_and_highest_nonces(&tx);
198 self.by_id.insert(id, tx);
199 }
200 }
201
202 removed
203 }
204
205 pub(crate) fn update_base_fee(
215 &mut self,
216 base_fee: u64,
217 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
218 let mut removed = Vec::new();
220
221 let mut transactions_iter = self.clear_transactions().into_iter().peekable();
223 while let Some((id, mut tx)) = transactions_iter.next() {
224 if tx.transaction.max_fee_per_gas() < base_fee as u128 {
225 removed.push(Arc::clone(&tx.transaction));
228
229 'this: while let Some((next_id, next_tx)) = transactions_iter.peek() {
231 if next_id.sender != id.sender {
232 break 'this
233 }
234 removed.push(Arc::clone(&next_tx.transaction));
235 transactions_iter.next();
236 }
237 } else {
238 tx.priority = self.ordering.priority(&tx.transaction.transaction, base_fee);
240
241 self.size_of += tx.transaction.size();
242 self.update_independents_and_highest_nonces(&tx);
243 self.by_id.insert(id, tx);
244 }
245 }
246
247 removed
248 }
249
250 fn update_independents_and_highest_nonces(&mut self, tx: &PendingTransaction<T>) {
253 match self.highest_nonces.entry(tx.transaction.sender_id()) {
254 Entry::Occupied(mut entry) => {
255 if entry.get().transaction.nonce() < tx.transaction.nonce() {
256 *entry.get_mut() = tx.clone();
257 }
258 }
259 Entry::Vacant(entry) => {
260 entry.insert(tx.clone());
261 }
262 }
263 match self.independent_transactions.entry(tx.transaction.sender_id()) {
264 Entry::Occupied(mut entry) => {
265 if entry.get().transaction.nonce() > tx.transaction.nonce() {
266 *entry.get_mut() = tx.clone();
267 }
268 }
269 Entry::Vacant(entry) => {
270 entry.insert(tx.clone());
271 }
272 }
273 }
274
275 pub fn add_transaction(
281 &mut self,
282 tx: Arc<ValidPoolTransaction<T::Transaction>>,
283 base_fee: u64,
284 ) {
285 debug_assert!(
286 !self.contains(tx.id()),
287 "transaction already included {:?}",
288 self.get(tx.id()).unwrap().transaction
289 );
290
291 self.size_of += tx.size();
293
294 let tx_id = *tx.id();
295
296 let submission_id = self.next_id();
297 let priority = self.ordering.priority(&tx.transaction, base_fee);
298 let tx = PendingTransaction { submission_id, transaction: tx, priority };
299
300 self.update_independents_and_highest_nonces(&tx);
301
302 if self.new_transaction_notifier.receiver_count() > 0 {
304 let _ = self.new_transaction_notifier.send(tx.clone());
305 }
306
307 self.by_id.insert(tx_id, tx);
308 }
309
310 pub(crate) fn remove_transaction(
315 &mut self,
316 id: &TransactionId,
317 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
318 if let Some(lowest) = self.independent_transactions.get(&id.sender) &&
319 lowest.transaction.nonce() == id.nonce
320 {
321 self.independent_transactions.remove(&id.sender);
322 if let Some(unlocked) = self.get(&id.descendant()) {
324 self.independent_transactions.insert(id.sender, unlocked.clone());
325 }
326 }
327
328 let tx = self.by_id.remove(id)?;
329 self.size_of -= tx.transaction.size();
330
331 match self.highest_nonces.entry(id.sender) {
332 Entry::Occupied(mut entry) => {
333 if entry.get().transaction.nonce() == id.nonce {
334 if let Some((_, new_highest)) = self
337 .by_id
338 .range((
339 id.sender.start_bound(),
340 std::ops::Bound::Included(TransactionId::new(id.sender, u64::MAX)),
341 ))
342 .last()
343 {
344 entry.insert(new_highest.clone());
346 } else {
347 entry.remove();
348 }
349 }
350 }
351 Entry::Vacant(_) => {
352 debug_assert!(
353 false,
354 "removed transaction without a tracked highest nonce {:?}",
355 id
356 );
357 }
358 }
359
360 Some(tx.transaction)
361 }
362
363 const fn next_id(&mut self) -> u64 {
364 let id = self.submission_id;
365 self.submission_id = self.submission_id.wrapping_add(1);
366 id
367 }
368
369 pub fn remove_to_limit(
384 &mut self,
385 limit: &SubPoolLimit,
386 remove_locals: bool,
387 end_removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
388 ) {
389 let mut non_local_senders = self.highest_nonces.len();
398
399 let mut unique_senders = self.highest_nonces.len();
402
403 let mut local_senders = FxHashSet::default();
405
406 let original_length = self.len();
408 let mut removed = Vec::new();
409 let mut total_removed = 0;
410
411 let original_size = self.size();
413 let mut total_size = 0;
414
415 loop {
416 let unique_removed = unique_senders - self.highest_nonces.len();
418
419 unique_senders = self.highest_nonces.len();
421 non_local_senders -= unique_removed;
422
423 removed.clear();
425
426 let mut worst_transactions = self.highest_nonces.values().collect::<Vec<_>>();
428 worst_transactions.sort_unstable();
429
430 for tx in worst_transactions {
432 if !limit.is_exceeded(original_length - total_removed, original_size - total_size) ||
434 non_local_senders == 0
435 {
436 for id in &removed {
438 if let Some(tx) = self.remove_transaction(id) {
439 end_removed.push(tx);
440 }
441 }
442
443 return
444 }
445
446 if !remove_locals && tx.transaction.is_local() {
447 let sender_id = tx.transaction.sender_id();
448 if local_senders.insert(sender_id) {
449 non_local_senders -= 1;
450 }
451 continue
452 }
453
454 total_size += tx.transaction.size();
455 total_removed += 1;
456 removed.push(*tx.transaction.id());
457 }
458
459 for id in &removed {
461 if let Some(tx) = self.remove_transaction(id) {
462 end_removed.push(tx);
463 }
464 }
465
466 if !self.exceeds(limit) || non_local_senders == 0 {
469 return
470 }
471 }
472 }
473
474 pub fn truncate_pool(
485 &mut self,
486 limit: SubPoolLimit,
487 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
488 let mut removed = Vec::new();
489 if !self.exceeds(&limit) {
491 return removed
492 }
493
494 self.remove_to_limit(&limit, false, &mut removed);
496 if !self.exceeds(&limit) {
497 return removed
498 }
499
500 self.remove_to_limit(&limit, true, &mut removed);
503
504 removed
505 }
506
507 #[inline]
509 pub(crate) fn exceeds(&self, limit: &SubPoolLimit) -> bool {
510 limit.is_exceeded(self.len(), self.size())
511 }
512
513 pub(crate) fn size(&self) -> usize {
515 self.size_of.into()
516 }
517
518 pub(crate) fn len(&self) -> usize {
520 self.by_id.len()
521 }
522
523 pub const fn by_id(&self) -> &OrdMap<TransactionId, PendingTransaction<T>> {
525 &self.by_id
526 }
527
528 pub const fn independent_transactions(&self) -> &FxHashMap<SenderId, PendingTransaction<T>> {
530 &self.independent_transactions
531 }
532
533 pub fn new_transaction_receiver(&self) -> broadcast::Receiver<PendingTransaction<T>> {
535 self.new_transaction_notifier.subscribe()
536 }
537
538 #[cfg(test)]
540 pub(crate) fn is_empty(&self) -> bool {
541 self.by_id.is_empty()
542 }
543
544 pub(crate) fn contains(&self, id: &TransactionId) -> bool {
546 self.by_id.contains_key(id)
547 }
548
549 pub(crate) fn get_txs_by_sender(&self, sender: SenderId) -> Vec<TransactionId> {
551 self.iter_txs_by_sender(sender).copied().collect()
552 }
553
554 pub(crate) fn iter_txs_by_sender(
556 &self,
557 sender: SenderId,
558 ) -> impl Iterator<Item = &TransactionId> + '_ {
559 self.by_id
560 .range((sender.start_bound(), Unbounded))
561 .take_while(move |(other, _)| sender == other.sender)
562 .map(|(tx_id, _)| tx_id)
563 }
564
565 pub(crate) fn txs_by_sender(
567 &self,
568 sender: SenderId,
569 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
570 self.by_id
571 .range((sender.start_bound(), Unbounded))
572 .take_while(move |(other, _)| sender == other.sender)
573 .map(|(_, tx)| tx.transaction.clone())
574 .collect()
575 }
576
577 fn get(&self, id: &TransactionId) -> Option<&PendingTransaction<T>> {
579 self.by_id.get(id)
580 }
581
582 #[cfg(test)]
584 pub(crate) const fn independent(&self) -> &FxHashMap<SenderId, PendingTransaction<T>> {
585 &self.independent_transactions
586 }
587
588 #[cfg(any(test, feature = "test-utils"))]
590 pub(crate) fn assert_invariants(&self) {
591 assert!(
592 self.independent_transactions.len() <= self.by_id.len(),
593 "independent_transactions.len() > by_id.len()"
594 );
595 assert!(
596 self.highest_nonces.len() <= self.by_id.len(),
597 "highest_nonces.len() > by_id.len()"
598 );
599 assert_eq!(
600 self.highest_nonces.len(),
601 self.independent_transactions.len(),
602 "highest_nonces.len() != independent_transactions.len()"
603 );
604 }
605}
606
607#[derive(Debug)]
609pub struct PendingTransaction<T: TransactionOrdering> {
610 pub submission_id: u64,
612 pub transaction: Arc<ValidPoolTransaction<T::Transaction>>,
614 pub priority: Priority<T::PriorityValue>,
616}
617
618impl<T: TransactionOrdering> PendingTransaction<T> {
619 pub fn unlocks(&self) -> TransactionId {
621 self.transaction.transaction_id.descendant()
622 }
623}
624
625impl<T: TransactionOrdering> Clone for PendingTransaction<T> {
626 fn clone(&self) -> Self {
627 Self {
628 submission_id: self.submission_id,
629 transaction: Arc::clone(&self.transaction),
630 priority: self.priority.clone(),
631 }
632 }
633}
634
635impl<T: TransactionOrdering> Eq for PendingTransaction<T> {}
636
637impl<T: TransactionOrdering> PartialEq<Self> for PendingTransaction<T> {
638 fn eq(&self, other: &Self) -> bool {
639 self.cmp(other) == Ordering::Equal
640 }
641}
642
643impl<T: TransactionOrdering> PartialOrd<Self> for PendingTransaction<T> {
644 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
645 Some(self.cmp(other))
646 }
647}
648
649impl<T: TransactionOrdering> Ord for PendingTransaction<T> {
650 fn cmp(&self, other: &Self) -> Ordering {
651 self.priority
655 .cmp(&other.priority)
656 .then_with(|| other.submission_id.cmp(&self.submission_id))
657 }
658}
659
660#[cfg(test)]
661mod tests {
662 use super::*;
663 use crate::{
664 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
665 PoolTransaction,
666 };
667 use alloy_consensus::{Transaction, TxType};
668 use alloy_primitives::address;
669 use std::collections::HashSet;
670
671 #[test]
672 fn test_enforce_basefee() {
673 let mut f = MockTransactionFactory::default();
674 let mut pool = PendingPool::new(MockOrdering::default());
675 let tx = f.validated_arc(MockTransaction::eip1559().inc_price());
676 pool.add_transaction(tx.clone(), 0);
677
678 assert!(pool.contains(tx.id()));
679 assert_eq!(pool.len(), 1);
680
681 let removed = pool.update_base_fee(0);
682 assert!(removed.is_empty());
683
684 let removed = pool.update_base_fee((tx.max_fee_per_gas() + 1) as u64);
685 assert_eq!(removed.len(), 1);
686 assert!(pool.is_empty());
687 }
688
689 #[test]
690 fn test_enforce_basefee_descendant() {
691 let mut f = MockTransactionFactory::default();
692 let mut pool = PendingPool::new(MockOrdering::default());
693 let t = MockTransaction::eip1559().inc_price_by(10);
694 let root_tx = f.validated_arc(t.clone());
695 pool.add_transaction(root_tx.clone(), 0);
696
697 let descendant_tx = f.validated_arc(t.inc_nonce().decr_price());
698 pool.add_transaction(descendant_tx.clone(), 0);
699
700 assert!(pool.contains(root_tx.id()));
701 assert!(pool.contains(descendant_tx.id()));
702 assert_eq!(pool.len(), 2);
703
704 assert_eq!(pool.independent_transactions.len(), 1);
705 assert_eq!(pool.highest_nonces.len(), 1);
706
707 let removed = pool.update_base_fee(0);
708 assert!(removed.is_empty());
709
710 {
713 let mut pool2 = pool.clone();
714 let removed = pool2.update_base_fee((descendant_tx.max_fee_per_gas() + 1) as u64);
715 assert_eq!(removed.len(), 1);
716 assert_eq!(pool2.len(), 1);
717 assert!(pool2.contains(root_tx.id()));
719 assert!(!pool2.contains(descendant_tx.id()));
720 }
721
722 let removed = pool.update_base_fee((root_tx.max_fee_per_gas() + 1) as u64);
724 assert_eq!(removed.len(), 2);
725 assert!(pool.is_empty());
726 pool.assert_invariants();
727 }
728
729 #[test]
730 fn evict_worst() {
731 let mut f = MockTransactionFactory::default();
732 let mut pool = PendingPool::new(MockOrdering::default());
733
734 let t = MockTransaction::eip1559();
735 pool.add_transaction(f.validated_arc(t.clone()), 0);
736
737 let t2 = MockTransaction::eip1559().inc_price_by(10);
738 pool.add_transaction(f.validated_arc(t2), 0);
739
740 assert_eq!(
742 pool.highest_nonces.values().min().map(|tx| *tx.transaction.hash()),
743 Some(*t.hash())
744 );
745
746 let removed = pool.truncate_pool(SubPoolLimit { max_txs: 1, max_size: usize::MAX });
748 assert_eq!(removed.len(), 1);
749 assert_eq!(removed[0].hash(), t.hash());
750 }
751
752 #[test]
753 fn correct_independent_descendants() {
754 let mut f = MockTransactionFactory::default();
756 let mut pool = PendingPool::new(MockOrdering::default());
757
758 let a_sender = address!("0x000000000000000000000000000000000000000a");
759 let b_sender = address!("0x000000000000000000000000000000000000000b");
760 let c_sender = address!("0x000000000000000000000000000000000000000c");
761 let d_sender = address!("0x000000000000000000000000000000000000000d");
762
763 let mut tx_set = MockTransactionSet::dependent(a_sender, 0, 4, TxType::Eip1559);
765 let a = tx_set.clone().into_vec();
766
767 let b = MockTransactionSet::dependent(b_sender, 0, 3, TxType::Eip1559).into_vec();
768 tx_set.extend(b.clone());
769
770 let c = MockTransactionSet::dependent(c_sender, 0, 3, TxType::Eip1559).into_vec();
772 tx_set.extend(c.clone());
773
774 let d = MockTransactionSet::dependent(d_sender, 0, 1, TxType::Eip1559).into_vec();
775 tx_set.extend(d.clone());
776
777 let all_txs = tx_set.into_vec();
779 for tx in all_txs {
780 pool.add_transaction(f.validated_arc(tx), 0);
781 }
782
783 pool.assert_invariants();
784
785 let expected_highest_nonces = [d[0].clone(), c[2].clone(), b[2].clone(), a[3].clone()]
788 .iter()
789 .map(|tx| (tx.sender(), tx.nonce()))
790 .collect::<HashSet<_>>();
791 let actual_highest_nonces = pool
792 .highest_nonces
793 .values()
794 .map(|tx| (tx.transaction.sender(), tx.transaction.nonce()))
795 .collect::<HashSet<_>>();
796 assert_eq!(expected_highest_nonces, actual_highest_nonces);
797 pool.assert_invariants();
798 }
799
800 #[test]
801 fn truncate_by_sender() {
802 let mut f = MockTransactionFactory::default();
804 let mut pool = PendingPool::new(MockOrdering::default());
805
806 let a = address!("0x000000000000000000000000000000000000000a");
808 let b = address!("0x000000000000000000000000000000000000000b");
809 let c = address!("0x000000000000000000000000000000000000000c");
810 let d = address!("0x000000000000000000000000000000000000000d");
811
812 let a_txs = MockTransactionSet::sequential_transactions_by_sender(a, 4, TxType::Eip1559);
814 let b_txs = MockTransactionSet::sequential_transactions_by_sender(b, 3, TxType::Eip1559);
815 let c_txs = MockTransactionSet::sequential_transactions_by_sender(c, 3, TxType::Eip1559);
816 let d_txs = MockTransactionSet::sequential_transactions_by_sender(d, 1, TxType::Eip1559);
817
818 let expected_pending = vec![
820 a_txs.transactions[0].clone(),
821 b_txs.transactions[0].clone(),
822 c_txs.transactions[0].clone(),
823 a_txs.transactions[1].clone(),
824 ]
825 .into_iter()
826 .map(|tx| (tx.sender(), tx.nonce()))
827 .collect::<HashSet<_>>();
828
829 let expected_removed = vec![
831 d_txs.transactions[0].clone(),
832 c_txs.transactions[2].clone(),
833 b_txs.transactions[2].clone(),
834 a_txs.transactions[3].clone(),
835 c_txs.transactions[1].clone(),
836 b_txs.transactions[1].clone(),
837 a_txs.transactions[2].clone(),
838 ]
839 .into_iter()
840 .map(|tx| (tx.sender(), tx.nonce()))
841 .collect::<HashSet<_>>();
842
843 let all_txs =
845 [a_txs.into_vec(), b_txs.into_vec(), c_txs.into_vec(), d_txs.into_vec()].concat();
846
847 for tx in all_txs {
849 pool.add_transaction(f.validated_arc(tx), 0);
850 }
851
852 pool.assert_invariants();
854
855 let pool_limit = SubPoolLimit { max_txs: 4, max_size: usize::MAX };
865
866 let removed = pool.truncate_pool(pool_limit);
868 pool.assert_invariants();
869 assert_eq!(removed.len(), expected_removed.len());
870
871 let removed =
873 removed.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::<HashSet<_>>();
874 assert_eq!(removed, expected_removed);
875
876 let pending = pool.all().collect::<Vec<_>>();
878 assert_eq!(pending.len(), expected_pending.len());
879
880 let pending =
882 pending.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::<HashSet<_>>();
883 assert_eq!(pending, expected_pending);
884 }
885
886 #[test]
888 fn test_eligible_updates_promoted() {
889 let mut pool = PendingPool::new(MockOrdering::default());
890 let mut f = MockTransactionFactory::default();
891
892 let num_senders = 10;
893
894 let first_txs: Vec<_> = (0..num_senders) .map(|_| MockTransaction::eip1559())
896 .collect();
897 let second_txs: Vec<_> =
898 first_txs.iter().map(|tx| tx.clone().rng_hash().inc_nonce()).collect();
899
900 for tx in first_txs {
901 let valid_tx = f.validated(tx);
902 pool.add_transaction(Arc::new(valid_tx), 0);
903 }
904
905 let mut best = pool.best();
906
907 for _ in 0..num_senders {
908 if let Some(tx) = best.next() {
909 assert_eq!(tx.nonce(), 0);
910 } else {
911 panic!("cannot read one of first_txs");
912 }
913 }
914
915 for tx in second_txs {
916 let valid_tx = f.validated(tx);
917 pool.add_transaction(Arc::new(valid_tx), 0);
918 }
919
920 for _ in 0..num_senders {
921 if let Some(tx) = best.next() {
922 assert_eq!(tx.nonce(), 1);
923 } else {
924 panic!("cannot read one of second_txs");
925 }
926 }
927 }
928
929 #[test]
930 fn test_empty_pool_behavior() {
931 let mut pool = PendingPool::<MockOrdering>::new(MockOrdering::default());
932
933 assert!(pool.is_empty());
935 assert_eq!(pool.len(), 0);
936 assert_eq!(pool.size(), 0);
937
938 let removed = pool.truncate_pool(SubPoolLimit { max_txs: 10, max_size: 1000 });
940 assert!(removed.is_empty());
941
942 assert!(pool.all().next().is_none());
944 }
945
946 #[test]
947 fn test_add_remove_transaction() {
948 let mut f = MockTransactionFactory::default();
949 let mut pool = PendingPool::new(MockOrdering::default());
950
951 let tx = f.validated_arc(MockTransaction::eip1559());
953 pool.add_transaction(tx.clone(), 0);
954 assert!(pool.contains(tx.id()));
955 assert_eq!(pool.len(), 1);
956
957 let removed_tx = pool.remove_transaction(tx.id()).unwrap();
959 assert_eq!(removed_tx.id(), tx.id());
960 assert!(!pool.contains(tx.id()));
961 assert_eq!(pool.len(), 0);
962 }
963
964 #[test]
965 fn test_reorder_on_basefee_update() {
966 let mut f = MockTransactionFactory::default();
967 let mut pool = PendingPool::new(MockOrdering::default());
968
969 let tx1 = f.validated_arc(MockTransaction::eip1559().inc_price());
971 let tx2 = f.validated_arc(MockTransaction::eip1559().inc_price_by(20));
972 pool.add_transaction(tx1.clone(), 0);
973 pool.add_transaction(tx2.clone(), 0);
974
975 let mut best = pool.best();
977 assert_eq!(best.next().unwrap().hash(), tx2.hash());
978 assert_eq!(best.next().unwrap().hash(), tx1.hash());
979
980 let removed = pool.update_base_fee((tx1.max_fee_per_gas() + 1) as u64);
982 assert_eq!(removed.len(), 1);
983 assert_eq!(removed[0].hash(), tx1.hash());
984
985 assert_eq!(pool.len(), 1);
987 assert!(pool.contains(tx2.id()));
988 assert!(!pool.contains(tx1.id()));
989 }
990
991 #[test]
992 #[cfg(debug_assertions)]
993 #[should_panic(expected = "transaction already included")]
994 fn test_handle_duplicates() {
995 let mut f = MockTransactionFactory::default();
996 let mut pool = PendingPool::new(MockOrdering::default());
997
998 let tx = f.validated_arc(MockTransaction::eip1559());
1000 pool.add_transaction(tx.clone(), 0);
1001 assert!(pool.contains(tx.id()));
1002 assert_eq!(pool.len(), 1);
1003
1004 pool.add_transaction(tx, 0);
1006 }
1007
1008 #[test]
1009 fn test_update_blob_fee() {
1010 let mut f = MockTransactionFactory::default();
1011 let mut pool = PendingPool::new(MockOrdering::default());
1012
1013 let tx1 = f.validated_arc(MockTransaction::eip4844().set_blob_fee(50).clone());
1015 let tx2 = f.validated_arc(MockTransaction::eip4844().set_blob_fee(150).clone());
1016 pool.add_transaction(tx1.clone(), 0);
1017 pool.add_transaction(tx2.clone(), 0);
1018
1019 let removed = pool.update_blob_fee(100);
1021 assert_eq!(removed.len(), 1);
1022 assert_eq!(removed[0].hash(), tx1.hash());
1023
1024 assert!(pool.contains(tx2.id()));
1026 assert!(!pool.contains(tx1.id()));
1027 }
1028
1029 #[test]
1030 fn local_senders_tracking() {
1031 let mut f = MockTransactionFactory::default();
1032 let mut pool = PendingPool::new(MockOrdering::default());
1033
1034 let a = address!("0x000000000000000000000000000000000000000a");
1036 let b = address!("0x000000000000000000000000000000000000000b");
1037 let c = address!("0x000000000000000000000000000000000000000c");
1038
1039 let a_txs = MockTransactionSet::sequential_transactions_by_sender(a, 11, TxType::Eip1559);
1045 let b_txs = MockTransactionSet::sequential_transactions_by_sender(b, 2, TxType::Eip1559);
1046 let c_txs = MockTransactionSet::sequential_transactions_by_sender(c, 2, TxType::Eip1559);
1047
1048 for tx in a_txs.into_vec() {
1050 let final_tx = Arc::new(f.validated_with_origin(crate::TransactionOrigin::Local, tx));
1051
1052 pool.add_transaction(final_tx, 0);
1053 }
1054
1055 let remaining_txs = [b_txs.into_vec(), c_txs.into_vec()].concat();
1057 for tx in remaining_txs {
1058 let final_tx = f.validated_arc(tx);
1059
1060 pool.add_transaction(final_tx, 0);
1061 }
1062
1063 pool.assert_invariants();
1065
1066 let pool_limit = SubPoolLimit { max_txs: 10, max_size: usize::MAX };
1067 pool.truncate_pool(pool_limit);
1068
1069 let sender_a = f.ids.sender_id(&a).unwrap();
1070 let sender_b = f.ids.sender_id(&b).unwrap();
1071 let sender_c = f.ids.sender_id(&c).unwrap();
1072
1073 assert_eq!(pool.get_txs_by_sender(sender_a).len(), 10);
1074 assert!(pool.get_txs_by_sender(sender_b).is_empty());
1075 assert!(pool.get_txs_by_sender(sender_c).is_empty());
1076 }
1077
1078 #[test]
1079 fn test_remove_non_highest_keeps_highest() {
1080 let mut f = MockTransactionFactory::default();
1081 let mut pool = PendingPool::new(MockOrdering::default());
1082 let sender = address!("0x00000000000000000000000000000000000000aa");
1083 let txs = MockTransactionSet::dependent(sender, 0, 3, TxType::Eip1559).into_vec();
1084 for tx in txs {
1085 pool.add_transaction(f.validated_arc(tx), 0);
1086 }
1087 pool.assert_invariants();
1088 let sender_id = f.ids.sender_id(&sender).unwrap();
1089 let mid_id = TransactionId::new(sender_id, 1);
1090 let _ = pool.remove_transaction(&mid_id);
1091 let highest = pool.highest_nonces.get(&sender_id).unwrap();
1092 assert_eq!(highest.transaction.nonce(), 2);
1093 pool.assert_invariants();
1094 }
1095
1096 #[test]
1097 fn test_cascade_removal_recomputes_highest() {
1098 let mut f = MockTransactionFactory::default();
1099 let mut pool = PendingPool::new(MockOrdering::default());
1100 let sender = address!("0x00000000000000000000000000000000000000bb");
1101 let txs = MockTransactionSet::dependent(sender, 0, 4, TxType::Eip1559).into_vec();
1102 for tx in txs {
1103 pool.add_transaction(f.validated_arc(tx), 0);
1104 }
1105 pool.assert_invariants();
1106 let sender_id = f.ids.sender_id(&sender).unwrap();
1107 let id3 = TransactionId::new(sender_id, 3);
1108 let _ = pool.remove_transaction(&id3);
1109 let highest = pool.highest_nonces.get(&sender_id).unwrap();
1110 assert_eq!(highest.transaction.nonce(), 2);
1111 let id2 = TransactionId::new(sender_id, 2);
1112 let _ = pool.remove_transaction(&id2);
1113 let highest = pool.highest_nonces.get(&sender_id).unwrap();
1114 assert_eq!(highest.transaction.nonce(), 1);
1115 pool.assert_invariants();
1116 }
1117
1118 #[test]
1119 fn test_remove_only_tx_clears_highest() {
1120 let mut f = MockTransactionFactory::default();
1121 let mut pool = PendingPool::new(MockOrdering::default());
1122 let sender = address!("0x00000000000000000000000000000000000000cc");
1123 let txs = MockTransactionSet::dependent(sender, 0, 1, TxType::Eip1559).into_vec();
1124 for tx in txs {
1125 pool.add_transaction(f.validated_arc(tx), 0);
1126 }
1127 pool.assert_invariants();
1128 let sender_id = f.ids.sender_id(&sender).unwrap();
1129 let id0 = TransactionId::new(sender_id, 0);
1130 let _ = pool.remove_transaction(&id0);
1131 assert!(!pool.highest_nonces.contains_key(&sender_id));
1132 pool.assert_invariants();
1133 }
1134}