1use crate::{
2 identifier::{SenderId, TransactionId},
3 pool::{
4 best::{BestTransactions, BestTransactionsWithFees},
5 size::SizeTracker,
6 },
7 Priority, SubPoolLimit, TransactionOrdering, ValidPoolTransaction,
8};
9use rustc_hash::{FxHashMap, FxHashSet};
10use std::{
11 cmp::Ordering,
12 collections::{hash_map::Entry, BTreeMap},
13 ops::Bound::Unbounded,
14 sync::Arc,
15};
16use tokio::sync::broadcast;
17
18#[derive(Debug, Clone)]
29pub struct PendingPool<T: TransactionOrdering> {
30 ordering: T,
32 submission_id: u64,
36 by_id: BTreeMap<TransactionId, PendingTransaction<T>>,
38 highest_nonces: FxHashMap<SenderId, PendingTransaction<T>>,
41 independent_transactions: FxHashMap<SenderId, PendingTransaction<T>>,
44 size_of: SizeTracker,
48 new_transaction_notifier: broadcast::Sender<PendingTransaction<T>>,
51}
52
53impl<T: TransactionOrdering> PendingPool<T> {
56 pub fn new(ordering: T) -> Self {
58 Self::with_buffer(ordering, 200)
59 }
60
61 pub fn with_buffer(ordering: T, buffer_capacity: usize) -> Self {
63 let (new_transaction_notifier, _) = broadcast::channel(buffer_capacity);
64 Self {
65 ordering,
66 submission_id: 0,
67 by_id: Default::default(),
68 independent_transactions: Default::default(),
69 highest_nonces: Default::default(),
70 size_of: Default::default(),
71 new_transaction_notifier,
72 }
73 }
74
75 fn clear_transactions(&mut self) -> BTreeMap<TransactionId, PendingTransaction<T>> {
82 self.independent_transactions.clear();
83 self.highest_nonces.clear();
84 self.size_of.reset();
85 std::mem::take(&mut self.by_id)
86 }
87
88 pub fn best(&self) -> BestTransactions<T> {
107 BestTransactions {
108 all: self.by_id.clone(),
109 independent: self.independent_transactions.values().cloned().collect(),
110 invalid: Default::default(),
111 new_transaction_receiver: Some(self.new_transaction_notifier.subscribe()),
112 skip_blobs: 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(
136 &self,
137 unlocked: Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
138 base_fee: u64,
139 ) -> BestTransactions<T> {
140 let mut best = self.best();
141 let mut submission_id = self.submission_id;
142 for tx in unlocked {
143 submission_id += 1;
144 debug_assert!(!best.all.contains_key(tx.id()), "transaction already included");
145 let priority = self.ordering.priority(&tx.transaction, base_fee);
146 let tx_id = *tx.id();
147 let transaction = PendingTransaction { submission_id, transaction: tx, priority };
148 if best.ancestor(&tx_id).is_none() {
149 best.independent.insert(transaction.clone());
150 }
151 best.all.insert(tx_id, transaction);
152 }
153
154 best
155 }
156
157 pub(crate) fn all(
159 &self,
160 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
161 self.by_id.values().map(|tx| tx.transaction.clone())
162 }
163
164 pub(crate) fn update_blob_fee(
174 &mut self,
175 blob_fee: u128,
176 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
177 let mut removed = Vec::new();
179
180 let mut transactions_iter = self.clear_transactions().into_iter().peekable();
182 while let Some((id, tx)) = transactions_iter.next() {
183 if tx.transaction.max_fee_per_blob_gas() < Some(blob_fee) {
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 fn ancestor(&self, id: &TransactionId) -> Option<&PendingTransaction<T>> {
281 self.get(&id.unchecked_ancestor()?)
282 }
283
284 pub fn add_transaction(
290 &mut self,
291 tx: Arc<ValidPoolTransaction<T::Transaction>>,
292 base_fee: u64,
293 ) {
294 assert!(
295 !self.contains(tx.id()),
296 "transaction already included {:?}",
297 self.get(tx.id()).unwrap().transaction
298 );
299
300 self.size_of += tx.size();
302
303 let tx_id = *tx.id();
304
305 let submission_id = self.next_id();
306 let priority = self.ordering.priority(&tx.transaction, base_fee);
307 let tx = PendingTransaction { submission_id, transaction: tx, priority };
308
309 self.update_independents_and_highest_nonces(&tx);
310
311 if self.new_transaction_notifier.receiver_count() > 0 {
313 let _ = self.new_transaction_notifier.send(tx.clone());
314 }
315
316 self.by_id.insert(tx_id, tx);
317 }
318
319 pub(crate) fn remove_transaction(
324 &mut self,
325 id: &TransactionId,
326 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
327 if let Some(lowest) = self.independent_transactions.get(&id.sender) {
328 if lowest.transaction.nonce() == id.nonce {
329 self.independent_transactions.remove(&id.sender);
330 if let Some(unlocked) = self.get(&id.descendant()) {
332 self.independent_transactions.insert(id.sender, unlocked.clone());
333 }
334 }
335 }
336
337 let tx = self.by_id.remove(id)?;
338 self.size_of -= tx.transaction.size();
339
340 if let Some(highest) = self.highest_nonces.get(&id.sender) {
341 if highest.transaction.nonce() == id.nonce {
342 self.highest_nonces.remove(&id.sender);
343 }
344 if let Some(ancestor) = self.ancestor(id) {
345 self.highest_nonces.insert(id.sender, ancestor.clone());
346 }
347 }
348 Some(tx.transaction)
349 }
350
351 const fn next_id(&mut self) -> u64 {
352 let id = self.submission_id;
353 self.submission_id = self.submission_id.wrapping_add(1);
354 id
355 }
356
357 pub fn remove_to_limit(
372 &mut self,
373 limit: &SubPoolLimit,
374 remove_locals: bool,
375 end_removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
376 ) {
377 let mut non_local_senders = self.highest_nonces.len();
386
387 let mut unique_senders = self.highest_nonces.len();
390
391 let mut local_senders = FxHashSet::default();
393
394 let original_length = self.len();
396 let mut removed = Vec::new();
397 let mut total_removed = 0;
398
399 let original_size = self.size();
401 let mut total_size = 0;
402
403 loop {
404 let unique_removed = unique_senders - self.highest_nonces.len();
406
407 unique_senders = self.highest_nonces.len();
409 non_local_senders -= unique_removed;
410
411 removed.clear();
413
414 let mut worst_transactions = self.highest_nonces.values().collect::<Vec<_>>();
416 worst_transactions.sort();
417
418 for tx in worst_transactions {
420 if !limit.is_exceeded(original_length - total_removed, original_size - total_size) ||
422 non_local_senders == 0
423 {
424 for id in &removed {
426 if let Some(tx) = self.remove_transaction(id) {
427 end_removed.push(tx);
428 }
429 }
430
431 return
432 }
433
434 if !remove_locals && tx.transaction.is_local() {
435 let sender_id = tx.transaction.sender_id();
436 if local_senders.insert(sender_id) {
437 non_local_senders -= 1;
438 }
439 continue
440 }
441
442 total_size += tx.transaction.size();
443 total_removed += 1;
444 removed.push(*tx.transaction.id());
445 }
446
447 for id in &removed {
449 if let Some(tx) = self.remove_transaction(id) {
450 end_removed.push(tx);
451 }
452 }
453
454 if !self.exceeds(limit) || non_local_senders == 0 {
457 return
458 }
459 }
460 }
461
462 pub fn truncate_pool(
473 &mut self,
474 limit: SubPoolLimit,
475 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
476 let mut removed = Vec::new();
477 if !self.exceeds(&limit) {
479 return removed
480 }
481
482 self.remove_to_limit(&limit, false, &mut removed);
484 if !self.exceeds(&limit) {
485 return removed
486 }
487
488 self.remove_to_limit(&limit, true, &mut removed);
491
492 removed
493 }
494
495 #[inline]
497 pub(crate) fn exceeds(&self, limit: &SubPoolLimit) -> bool {
498 limit.is_exceeded(self.len(), self.size())
499 }
500
501 pub(crate) fn size(&self) -> usize {
503 self.size_of.into()
504 }
505
506 pub(crate) fn len(&self) -> usize {
508 self.by_id.len()
509 }
510
511 #[cfg(test)]
513 pub(crate) fn is_empty(&self) -> bool {
514 self.by_id.is_empty()
515 }
516
517 pub(crate) fn contains(&self, id: &TransactionId) -> bool {
519 self.by_id.contains_key(id)
520 }
521
522 pub(crate) fn get_txs_by_sender(&self, sender: SenderId) -> Vec<TransactionId> {
524 self.by_id
525 .range((sender.start_bound(), Unbounded))
526 .take_while(move |(other, _)| sender == other.sender)
527 .map(|(tx_id, _)| *tx_id)
528 .collect()
529 }
530
531 fn get(&self, id: &TransactionId) -> Option<&PendingTransaction<T>> {
533 self.by_id.get(id)
534 }
535
536 #[cfg(test)]
538 pub(crate) const fn independent(&self) -> &FxHashMap<SenderId, PendingTransaction<T>> {
539 &self.independent_transactions
540 }
541
542 #[cfg(any(test, feature = "test-utils"))]
544 pub(crate) fn assert_invariants(&self) {
545 assert!(
546 self.independent_transactions.len() <= self.by_id.len(),
547 "independent.len() > all.len()"
548 );
549 assert!(
550 self.highest_nonces.len() <= self.by_id.len(),
551 "independent_descendants.len() > all.len()"
552 );
553 assert_eq!(
554 self.highest_nonces.len(),
555 self.independent_transactions.len(),
556 "independent.len() = independent_descendants.len()"
557 );
558 }
559}
560
561#[derive(Debug)]
563pub(crate) struct PendingTransaction<T: TransactionOrdering> {
564 pub(crate) submission_id: u64,
566 pub(crate) transaction: Arc<ValidPoolTransaction<T::Transaction>>,
568 pub(crate) priority: Priority<T::PriorityValue>,
570}
571
572impl<T: TransactionOrdering> PendingTransaction<T> {
573 pub(crate) fn unlocks(&self) -> TransactionId {
575 self.transaction.transaction_id.descendant()
576 }
577}
578
579impl<T: TransactionOrdering> Clone for PendingTransaction<T> {
580 fn clone(&self) -> Self {
581 Self {
582 submission_id: self.submission_id,
583 transaction: Arc::clone(&self.transaction),
584 priority: self.priority.clone(),
585 }
586 }
587}
588
589impl<T: TransactionOrdering> Eq for PendingTransaction<T> {}
590
591impl<T: TransactionOrdering> PartialEq<Self> for PendingTransaction<T> {
592 fn eq(&self, other: &Self) -> bool {
593 self.cmp(other) == Ordering::Equal
594 }
595}
596
597impl<T: TransactionOrdering> PartialOrd<Self> for PendingTransaction<T> {
598 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
599 Some(self.cmp(other))
600 }
601}
602
603impl<T: TransactionOrdering> Ord for PendingTransaction<T> {
604 fn cmp(&self, other: &Self) -> Ordering {
605 self.priority
609 .cmp(&other.priority)
610 .then_with(|| other.submission_id.cmp(&self.submission_id))
611 }
612}
613
614#[cfg(test)]
615mod tests {
616 use super::*;
617 use crate::{
618 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
619 PoolTransaction,
620 };
621 use alloy_consensus::{Transaction, TxType};
622 use alloy_primitives::address;
623 use std::collections::HashSet;
624
625 #[test]
626 fn test_enforce_basefee() {
627 let mut f = MockTransactionFactory::default();
628 let mut pool = PendingPool::new(MockOrdering::default());
629 let tx = f.validated_arc(MockTransaction::eip1559().inc_price());
630 pool.add_transaction(tx.clone(), 0);
631
632 assert!(pool.contains(tx.id()));
633 assert_eq!(pool.len(), 1);
634
635 let removed = pool.update_base_fee(0);
636 assert!(removed.is_empty());
637
638 let removed = pool.update_base_fee((tx.max_fee_per_gas() + 1) as u64);
639 assert_eq!(removed.len(), 1);
640 assert!(pool.is_empty());
641 }
642
643 #[test]
644 fn test_enforce_basefee_descendant() {
645 let mut f = MockTransactionFactory::default();
646 let mut pool = PendingPool::new(MockOrdering::default());
647 let t = MockTransaction::eip1559().inc_price_by(10);
648 let root_tx = f.validated_arc(t.clone());
649 pool.add_transaction(root_tx.clone(), 0);
650
651 let descendant_tx = f.validated_arc(t.inc_nonce().decr_price());
652 pool.add_transaction(descendant_tx.clone(), 0);
653
654 assert!(pool.contains(root_tx.id()));
655 assert!(pool.contains(descendant_tx.id()));
656 assert_eq!(pool.len(), 2);
657
658 assert_eq!(pool.independent_transactions.len(), 1);
659 assert_eq!(pool.highest_nonces.len(), 1);
660
661 let removed = pool.update_base_fee(0);
662 assert!(removed.is_empty());
663
664 {
667 let mut pool2 = pool.clone();
668 let removed = pool2.update_base_fee((descendant_tx.max_fee_per_gas() + 1) as u64);
669 assert_eq!(removed.len(), 1);
670 assert_eq!(pool2.len(), 1);
671 assert!(pool2.contains(root_tx.id()));
673 assert!(!pool2.contains(descendant_tx.id()));
674 }
675
676 let removed = pool.update_base_fee((root_tx.max_fee_per_gas() + 1) as u64);
678 assert_eq!(removed.len(), 2);
679 assert!(pool.is_empty());
680 pool.assert_invariants();
681 }
682
683 #[test]
684 fn evict_worst() {
685 let mut f = MockTransactionFactory::default();
686 let mut pool = PendingPool::new(MockOrdering::default());
687
688 let t = MockTransaction::eip1559();
689 pool.add_transaction(f.validated_arc(t.clone()), 0);
690
691 let t2 = MockTransaction::eip1559().inc_price_by(10);
692 pool.add_transaction(f.validated_arc(t2), 0);
693
694 assert_eq!(
696 pool.highest_nonces.values().min().map(|tx| *tx.transaction.hash()),
697 Some(*t.hash())
698 );
699
700 let removed = pool.truncate_pool(SubPoolLimit { max_txs: 1, max_size: usize::MAX });
702 assert_eq!(removed.len(), 1);
703 assert_eq!(removed[0].hash(), t.hash());
704 }
705
706 #[test]
707 fn correct_independent_descendants() {
708 let mut f = MockTransactionFactory::default();
710 let mut pool = PendingPool::new(MockOrdering::default());
711
712 let a_sender = address!("0x000000000000000000000000000000000000000a");
713 let b_sender = address!("0x000000000000000000000000000000000000000b");
714 let c_sender = address!("0x000000000000000000000000000000000000000c");
715 let d_sender = address!("0x000000000000000000000000000000000000000d");
716
717 let mut tx_set = MockTransactionSet::dependent(a_sender, 0, 4, TxType::Eip1559);
719 let a = tx_set.clone().into_vec();
720
721 let b = MockTransactionSet::dependent(b_sender, 0, 3, TxType::Eip1559).into_vec();
722 tx_set.extend(b.clone());
723
724 let c = MockTransactionSet::dependent(c_sender, 0, 3, TxType::Eip1559).into_vec();
726 tx_set.extend(c.clone());
727
728 let d = MockTransactionSet::dependent(d_sender, 0, 1, TxType::Eip1559).into_vec();
729 tx_set.extend(d.clone());
730
731 let all_txs = tx_set.into_vec();
733 for tx in all_txs {
734 pool.add_transaction(f.validated_arc(tx), 0);
735 }
736
737 pool.assert_invariants();
738
739 let expected_highest_nonces = vec![d[0].clone(), c[2].clone(), b[2].clone(), a[3].clone()]
742 .iter()
743 .map(|tx| (tx.sender(), tx.nonce()))
744 .collect::<HashSet<_>>();
745 let actual_highest_nonces = pool
746 .highest_nonces
747 .values()
748 .map(|tx| (tx.transaction.sender(), tx.transaction.nonce()))
749 .collect::<HashSet<_>>();
750 assert_eq!(expected_highest_nonces, actual_highest_nonces);
751 pool.assert_invariants();
752 }
753
754 #[test]
755 fn truncate_by_sender() {
756 let mut f = MockTransactionFactory::default();
758 let mut pool = PendingPool::new(MockOrdering::default());
759
760 let a = address!("0x000000000000000000000000000000000000000a");
762 let b = address!("0x000000000000000000000000000000000000000b");
763 let c = address!("0x000000000000000000000000000000000000000c");
764 let d = address!("0x000000000000000000000000000000000000000d");
765
766 let a_txs = MockTransactionSet::sequential_transactions_by_sender(a, 4, TxType::Eip1559);
768 let b_txs = MockTransactionSet::sequential_transactions_by_sender(b, 3, TxType::Eip1559);
769 let c_txs = MockTransactionSet::sequential_transactions_by_sender(c, 3, TxType::Eip1559);
770 let d_txs = MockTransactionSet::sequential_transactions_by_sender(d, 1, TxType::Eip1559);
771
772 let expected_pending = vec![
774 a_txs.transactions[0].clone(),
775 b_txs.transactions[0].clone(),
776 c_txs.transactions[0].clone(),
777 a_txs.transactions[1].clone(),
778 ]
779 .into_iter()
780 .map(|tx| (tx.sender(), tx.nonce()))
781 .collect::<HashSet<_>>();
782
783 let expected_removed = vec![
785 d_txs.transactions[0].clone(),
786 c_txs.transactions[2].clone(),
787 b_txs.transactions[2].clone(),
788 a_txs.transactions[3].clone(),
789 c_txs.transactions[1].clone(),
790 b_txs.transactions[1].clone(),
791 a_txs.transactions[2].clone(),
792 ]
793 .into_iter()
794 .map(|tx| (tx.sender(), tx.nonce()))
795 .collect::<HashSet<_>>();
796
797 let all_txs =
799 [a_txs.into_vec(), b_txs.into_vec(), c_txs.into_vec(), d_txs.into_vec()].concat();
800
801 for tx in all_txs {
803 pool.add_transaction(f.validated_arc(tx), 0);
804 }
805
806 pool.assert_invariants();
808
809 let pool_limit = SubPoolLimit { max_txs: 4, max_size: usize::MAX };
819
820 let removed = pool.truncate_pool(pool_limit);
822 pool.assert_invariants();
823 assert_eq!(removed.len(), expected_removed.len());
824
825 let removed =
827 removed.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::<HashSet<_>>();
828 assert_eq!(removed, expected_removed);
829
830 let pending = pool.all().collect::<Vec<_>>();
832 assert_eq!(pending.len(), expected_pending.len());
833
834 let pending =
836 pending.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::<HashSet<_>>();
837 assert_eq!(pending, expected_pending);
838 }
839
840 #[test]
842 fn test_eligible_updates_promoted() {
843 let mut pool = PendingPool::new(MockOrdering::default());
844 let mut f = MockTransactionFactory::default();
845
846 let num_senders = 10;
847
848 let first_txs: Vec<_> = (0..num_senders) .map(|_| MockTransaction::eip1559())
850 .collect();
851 let second_txs: Vec<_> =
852 first_txs.iter().map(|tx| tx.clone().rng_hash().inc_nonce()).collect();
853
854 for tx in first_txs {
855 let valid_tx = f.validated(tx);
856 pool.add_transaction(Arc::new(valid_tx), 0);
857 }
858
859 let mut best = pool.best();
860
861 for _ in 0..num_senders {
862 if let Some(tx) = best.next() {
863 assert_eq!(tx.nonce(), 0);
864 } else {
865 panic!("cannot read one of first_txs");
866 }
867 }
868
869 for tx in second_txs {
870 let valid_tx = f.validated(tx);
871 pool.add_transaction(Arc::new(valid_tx), 0);
872 }
873
874 for _ in 0..num_senders {
875 if let Some(tx) = best.next() {
876 assert_eq!(tx.nonce(), 1);
877 } else {
878 panic!("cannot read one of second_txs");
879 }
880 }
881 }
882
883 #[test]
884 fn test_empty_pool_behavior() {
885 let mut pool = PendingPool::<MockOrdering>::new(MockOrdering::default());
886
887 assert!(pool.is_empty());
889 assert_eq!(pool.len(), 0);
890 assert_eq!(pool.size(), 0);
891
892 let removed = pool.truncate_pool(SubPoolLimit { max_txs: 10, max_size: 1000 });
894 assert!(removed.is_empty());
895
896 let all_txs: Vec<_> = pool.all().collect();
898 assert!(all_txs.is_empty());
899 }
900
901 #[test]
902 fn test_add_remove_transaction() {
903 let mut f = MockTransactionFactory::default();
904 let mut pool = PendingPool::new(MockOrdering::default());
905
906 let tx = f.validated_arc(MockTransaction::eip1559());
908 pool.add_transaction(tx.clone(), 0);
909 assert!(pool.contains(tx.id()));
910 assert_eq!(pool.len(), 1);
911
912 let removed_tx = pool.remove_transaction(tx.id()).unwrap();
914 assert_eq!(removed_tx.id(), tx.id());
915 assert!(!pool.contains(tx.id()));
916 assert_eq!(pool.len(), 0);
917 }
918
919 #[test]
920 fn test_reorder_on_basefee_update() {
921 let mut f = MockTransactionFactory::default();
922 let mut pool = PendingPool::new(MockOrdering::default());
923
924 let tx1 = f.validated_arc(MockTransaction::eip1559().inc_price());
926 let tx2 = f.validated_arc(MockTransaction::eip1559().inc_price_by(20));
927 pool.add_transaction(tx1.clone(), 0);
928 pool.add_transaction(tx2.clone(), 0);
929
930 let mut best = pool.best();
932 assert_eq!(best.next().unwrap().hash(), tx2.hash());
933 assert_eq!(best.next().unwrap().hash(), tx1.hash());
934
935 let removed = pool.update_base_fee((tx1.max_fee_per_gas() + 1) as u64);
937 assert_eq!(removed.len(), 1);
938 assert_eq!(removed[0].hash(), tx1.hash());
939
940 assert_eq!(pool.len(), 1);
942 assert!(pool.contains(tx2.id()));
943 assert!(!pool.contains(tx1.id()));
944 }
945
946 #[test]
947 #[should_panic(expected = "transaction already included")]
948 fn test_handle_duplicates() {
949 let mut f = MockTransactionFactory::default();
950 let mut pool = PendingPool::new(MockOrdering::default());
951
952 let tx = f.validated_arc(MockTransaction::eip1559());
954 pool.add_transaction(tx.clone(), 0);
955 assert!(pool.contains(tx.id()));
956 assert_eq!(pool.len(), 1);
957
958 pool.add_transaction(tx, 0);
960 }
961
962 #[test]
963 fn test_update_blob_fee() {
964 let mut f = MockTransactionFactory::default();
965 let mut pool = PendingPool::new(MockOrdering::default());
966
967 let tx1 = f.validated_arc(MockTransaction::eip4844().set_blob_fee(50).clone());
969 let tx2 = f.validated_arc(MockTransaction::eip4844().set_blob_fee(150).clone());
970 pool.add_transaction(tx1.clone(), 0);
971 pool.add_transaction(tx2.clone(), 0);
972
973 let removed = pool.update_blob_fee(100);
975 assert_eq!(removed.len(), 1);
976 assert_eq!(removed[0].hash(), tx1.hash());
977
978 assert!(pool.contains(tx2.id()));
980 assert!(!pool.contains(tx1.id()));
981 }
982
983 #[test]
984 fn local_senders_tracking() {
985 let mut f = MockTransactionFactory::default();
986 let mut pool = PendingPool::new(MockOrdering::default());
987
988 let a = address!("0x000000000000000000000000000000000000000a");
990 let b = address!("0x000000000000000000000000000000000000000b");
991 let c = address!("0x000000000000000000000000000000000000000c");
992
993 let a_txs = MockTransactionSet::sequential_transactions_by_sender(a, 11, TxType::Eip1559);
999 let b_txs = MockTransactionSet::sequential_transactions_by_sender(b, 2, TxType::Eip1559);
1000 let c_txs = MockTransactionSet::sequential_transactions_by_sender(c, 2, TxType::Eip1559);
1001
1002 for tx in a_txs.into_vec() {
1004 let final_tx = Arc::new(f.validated_with_origin(crate::TransactionOrigin::Local, tx));
1005
1006 pool.add_transaction(final_tx, 0);
1007 }
1008
1009 let remaining_txs = [b_txs.into_vec(), c_txs.into_vec()].concat();
1011 for tx in remaining_txs {
1012 let final_tx = f.validated_arc(tx);
1013
1014 pool.add_transaction(final_tx, 0);
1015 }
1016
1017 pool.assert_invariants();
1019
1020 let pool_limit = SubPoolLimit { max_txs: 10, max_size: usize::MAX };
1021 pool.truncate_pool(pool_limit);
1022
1023 let sender_a = f.ids.sender_id(&a).unwrap();
1024 let sender_b = f.ids.sender_id(&b).unwrap();
1025 let sender_c = f.ids.sender_id(&c).unwrap();
1026
1027 assert_eq!(pool.get_txs_by_sender(sender_a).len(), 10);
1028 assert!(pool.get_txs_by_sender(sender_b).is_empty());
1029 assert!(pool.get_txs_by_sender(sender_c).is_empty());
1030 }
1031}