1use crate::{
4 config::{LocalTransactionConfig, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER},
5 error::{Eip4844PoolTransactionError, InvalidPoolTransactionError, PoolError, PoolErrorKind},
6 identifier::{SenderId, TransactionId},
7 metrics::{AllTransactionsMetrics, TxPoolMetrics},
8 pool::{
9 best::BestTransactions,
10 blob::BlobTransactions,
11 parked::{BasefeeOrd, ParkedPool, QueuedOrd},
12 pending::PendingPool,
13 state::{SubPool, TxState},
14 update::{Destination, PoolUpdate, UpdateOutcome},
15 AddedPendingTransaction, AddedTransaction, OnNewCanonicalStateOutcome,
16 },
17 traits::{BestTransactionsAttributes, BlockInfo, PoolSize},
18 PoolConfig, PoolResult, PoolTransaction, PoolUpdateKind, PriceBumpConfig, TransactionOrdering,
19 ValidPoolTransaction, U256,
20};
21use alloy_consensus::constants::{
22 EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID,
23 LEGACY_TX_TYPE_ID,
24};
25use alloy_eips::{
26 eip1559::{ETHEREUM_BLOCK_GAS_LIMIT_30M, MIN_PROTOCOL_BASE_FEE},
27 eip4844::BLOB_TX_MIN_BLOB_GASPRICE,
28 Typed2718,
29};
30use alloy_primitives::{Address, TxHash, B256};
31use rustc_hash::FxHashMap;
32use smallvec::SmallVec;
33use std::{
34 cmp::Ordering,
35 collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet},
36 fmt,
37 ops::Bound::{Excluded, Unbounded},
38 sync::Arc,
39};
40use tracing::trace;
41
42#[cfg_attr(doc, aquamarine::aquamarine)]
43pub struct TxPool<T: TransactionOrdering> {
85 sender_info: FxHashMap<SenderId, SenderInfo>,
87 pending_pool: PendingPool<T>,
91 config: PoolConfig,
93 queued_pool: ParkedPool<QueuedOrd<T::Transaction>>,
100 basefee_pool: ParkedPool<BasefeeOrd<T::Transaction>>,
105 blob_pool: BlobTransactions<T::Transaction>,
112 all_transactions: AllTransactions<T::Transaction>,
114 metrics: TxPoolMetrics,
116 latest_update_kind: Option<PoolUpdateKind>,
118}
119
120impl<T: TransactionOrdering> TxPool<T> {
123 pub fn new(ordering: T, config: PoolConfig) -> Self {
125 Self {
126 sender_info: Default::default(),
127 pending_pool: PendingPool::with_buffer(
128 ordering,
129 config.max_new_pending_txs_notifications,
130 ),
131 queued_pool: Default::default(),
132 basefee_pool: Default::default(),
133 blob_pool: Default::default(),
134 all_transactions: AllTransactions::new(&config),
135 config,
136 metrics: Default::default(),
137 latest_update_kind: None,
138 }
139 }
140
141 pub fn get_highest_nonce_by_sender(&self, sender: SenderId) -> Option<u64> {
143 self.all().txs_iter(sender).last().map(|(_, tx)| tx.transaction.nonce())
144 }
145
146 pub fn get_highest_transaction_by_sender(
149 &self,
150 sender: SenderId,
151 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
152 self.all().txs_iter(sender).last().map(|(_, tx)| Arc::clone(&tx.transaction))
153 }
154
155 pub(crate) fn get_highest_consecutive_transaction_by_sender(
162 &self,
163 mut on_chain: TransactionId,
164 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
165 let mut last_consecutive_tx = None;
166
167 if let Some(current) = self.sender_info.get(&on_chain.sender) {
169 on_chain.nonce = on_chain.nonce.max(current.state_nonce);
170 }
171
172 let mut next_expected_nonce = on_chain.nonce;
173 for (id, tx) in self.all().descendant_txs_inclusive(&on_chain) {
174 if next_expected_nonce != id.nonce {
175 break
176 }
177 next_expected_nonce = id.next_nonce();
178 last_consecutive_tx = Some(tx);
179 }
180
181 last_consecutive_tx.map(|tx| Arc::clone(&tx.transaction))
182 }
183
184 pub(crate) const fn all(&self) -> &AllTransactions<T::Transaction> {
186 &self.all_transactions
187 }
188
189 pub(crate) fn unique_senders(&self) -> HashSet<Address> {
191 self.all_transactions.txs.values().map(|tx| tx.transaction.sender()).collect()
192 }
193
194 pub fn size(&self) -> PoolSize {
196 PoolSize {
197 pending: self.pending_pool.len(),
198 pending_size: self.pending_pool.size(),
199 basefee: self.basefee_pool.len(),
200 basefee_size: self.basefee_pool.size(),
201 queued: self.queued_pool.len(),
202 queued_size: self.queued_pool.size(),
203 blob: self.blob_pool.len(),
204 blob_size: self.blob_pool.size(),
205 total: self.all_transactions.len(),
206 }
207 }
208
209 pub const fn block_info(&self) -> BlockInfo {
211 BlockInfo {
212 block_gas_limit: self.all_transactions.block_gas_limit,
213 last_seen_block_hash: self.all_transactions.last_seen_block_hash,
214 last_seen_block_number: self.all_transactions.last_seen_block_number,
215 pending_basefee: self.all_transactions.pending_fees.base_fee,
216 pending_blob_fee: Some(self.all_transactions.pending_fees.blob_fee),
217 }
218 }
219
220 fn update_blob_fee(&mut self, mut pending_blob_fee: u128, base_fee_update: Ordering) {
222 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut pending_blob_fee);
223 match (self.all_transactions.pending_fees.blob_fee.cmp(&pending_blob_fee), base_fee_update)
224 {
225 (Ordering::Equal, Ordering::Equal | Ordering::Greater) => {
226 }
228 (Ordering::Greater, Ordering::Equal | Ordering::Greater) => {
229 let removed =
231 self.pending_pool.update_blob_fee(self.all_transactions.pending_fees.blob_fee);
232 for tx in removed {
233 let to = {
234 let tx =
235 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
236
237 tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
239 tx.subpool = tx.state.into();
240 tx.subpool
241 };
242 self.add_transaction_to_subpool(to, tx);
243 }
244 }
245 (Ordering::Less, _) | (_, Ordering::Less) => {
246 let removed =
248 self.blob_pool.enforce_pending_fees(&self.all_transactions.pending_fees);
249 for tx in removed {
250 let to = {
251 let tx =
252 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
253 tx.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
254 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
255 tx.subpool = tx.state.into();
256 tx.subpool
257 };
258 self.add_transaction_to_subpool(to, tx);
259 }
260 }
261 }
262 }
263
264 fn update_basefee(&mut self, mut pending_basefee: u64) -> Ordering {
269 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut pending_basefee);
270 match self.all_transactions.pending_fees.base_fee.cmp(&pending_basefee) {
271 Ordering::Equal => {
272 Ordering::Equal
274 }
275 Ordering::Greater => {
276 let removed =
278 self.pending_pool.update_base_fee(self.all_transactions.pending_fees.base_fee);
279 for tx in removed {
280 let to = {
281 let tx =
282 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
283 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
284 tx.subpool = tx.state.into();
285 tx.subpool
286 };
287 self.add_transaction_to_subpool(to, tx);
288 }
289
290 Ordering::Greater
291 }
292 Ordering::Less => {
293 let removed =
295 self.basefee_pool.enforce_basefee(self.all_transactions.pending_fees.base_fee);
296 for tx in removed {
297 let to = {
298 let tx =
299 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
300 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
301 tx.subpool = tx.state.into();
302 tx.subpool
303 };
304 self.add_transaction_to_subpool(to, tx);
305 }
306
307 Ordering::Less
308 }
309 }
310 }
311
312 pub fn set_block_info(&mut self, info: BlockInfo) {
316 let BlockInfo {
317 block_gas_limit,
318 last_seen_block_hash,
319 last_seen_block_number,
320 pending_basefee,
321 pending_blob_fee,
322 } = info;
323 self.all_transactions.last_seen_block_hash = last_seen_block_hash;
324 self.all_transactions.last_seen_block_number = last_seen_block_number;
325 let basefee_ordering = self.update_basefee(pending_basefee);
326
327 self.all_transactions.block_gas_limit = block_gas_limit;
328
329 if let Some(blob_fee) = pending_blob_fee {
330 self.update_blob_fee(blob_fee, basefee_ordering)
331 }
332 }
333
334 pub(crate) fn best_transactions(&self) -> BestTransactions<T> {
337 self.pending_pool.best()
338 }
339
340 pub(crate) fn best_transactions_with_attributes(
347 &self,
348 best_transactions_attributes: BestTransactionsAttributes,
349 ) -> Box<dyn crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T::Transaction>>>>
350 {
351 match best_transactions_attributes.basefee.cmp(&self.all_transactions.pending_fees.base_fee)
354 {
355 Ordering::Equal => {
356 if best_transactions_attributes
360 .blob_fee
361 .is_some_and(|fee| fee < self.all_transactions.pending_fees.blob_fee as u64)
362 {
363 let unlocked_by_blob_fee =
364 self.blob_pool.satisfy_attributes(best_transactions_attributes);
365
366 Box::new(self.pending_pool.best_with_unlocked(
367 unlocked_by_blob_fee,
368 self.all_transactions.pending_fees.base_fee,
369 ))
370 } else {
371 Box::new(self.pending_pool.best())
372 }
373 }
374 Ordering::Greater => {
375 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
377 best_transactions_attributes.basefee,
378 best_transactions_attributes.blob_fee.unwrap_or_default(),
379 ))
380 }
381 Ordering::Less => {
382 let mut unlocked = self
385 .basefee_pool
386 .satisfy_base_fee_transactions(best_transactions_attributes.basefee);
387
388 unlocked.extend(self.blob_pool.satisfy_attributes(best_transactions_attributes));
390
391 Box::new(
392 self.pending_pool
393 .best_with_unlocked(unlocked, self.all_transactions.pending_fees.base_fee),
394 )
395 }
396 }
397 }
398
399 pub(crate) fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
401 self.pending_pool.all().collect()
402 }
403 pub(crate) fn pending_transactions_iter(
405 &self,
406 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
407 self.pending_pool.all()
408 }
409
410 pub(crate) fn pending_transactions_with_predicate(
412 &self,
413 mut predicate: impl FnMut(&ValidPoolTransaction<T::Transaction>) -> bool,
414 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
415 self.pending_transactions_iter().filter(|tx| predicate(tx)).collect()
416 }
417
418 pub(crate) fn pending_txs_by_sender(
420 &self,
421 sender: SenderId,
422 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
423 self.pending_transactions_iter().filter(|tx| tx.sender_id() == sender).collect()
424 }
425
426 pub(crate) fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
428 self.basefee_pool.all().chain(self.queued_pool.all()).collect()
429 }
430
431 pub(crate) fn queued_transactions_iter(
433 &self,
434 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
435 self.basefee_pool.all().chain(self.queued_pool.all())
436 }
437
438 pub fn queued_and_pending_txs_by_sender(
440 &self,
441 sender: SenderId,
442 ) -> (SmallVec<[TransactionId; TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER]>, Vec<TransactionId>) {
443 (self.queued_pool.get_txs_by_sender(sender), self.pending_pool.get_txs_by_sender(sender))
444 }
445
446 pub(crate) fn queued_txs_by_sender(
448 &self,
449 sender: SenderId,
450 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
451 self.queued_transactions_iter().filter(|tx| tx.sender_id() == sender).collect()
452 }
453
454 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
456 self.all_transactions.contains(tx_hash)
457 }
458
459 #[cfg(test)]
461 pub(crate) fn subpool_contains(&self, subpool: SubPool, id: &TransactionId) -> bool {
462 match subpool {
463 SubPool::Queued => self.queued_pool.contains(id),
464 SubPool::Pending => self.pending_pool.contains(id),
465 SubPool::BaseFee => self.basefee_pool.contains(id),
466 SubPool::Blob => self.blob_pool.contains(id),
467 }
468 }
469
470 #[inline]
472 pub(crate) fn is_exceeded(&self) -> bool {
473 self.config.is_exceeded(self.size())
474 }
475
476 pub(crate) fn get(
478 &self,
479 tx_hash: &TxHash,
480 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
481 self.all_transactions.by_hash.get(tx_hash).cloned()
482 }
483
484 pub(crate) fn get_all(
486 &self,
487 txs: Vec<TxHash>,
488 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
489 txs.into_iter().filter_map(|tx| self.get(&tx))
490 }
491
492 pub(crate) fn get_transactions_by_sender(
494 &self,
495 sender: SenderId,
496 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
497 self.all_transactions.txs_iter(sender).map(|(_, tx)| Arc::clone(&tx.transaction)).collect()
498 }
499
500 pub(crate) fn update_accounts(
502 &mut self,
503 changed_senders: FxHashMap<SenderId, SenderInfo>,
504 ) -> UpdateOutcome<T::Transaction> {
505 let updates = self.all_transactions.update(&changed_senders);
507
508 self.sender_info.extend(changed_senders);
510
511 let update = self.process_updates(updates);
513 self.update_size_metrics();
515 update
516 }
517
518 pub(crate) fn on_canonical_state_change(
523 &mut self,
524 block_info: BlockInfo,
525 mined_transactions: Vec<TxHash>,
526 changed_senders: FxHashMap<SenderId, SenderInfo>,
527 update_kind: PoolUpdateKind,
528 ) -> OnNewCanonicalStateOutcome<T::Transaction> {
529 let block_hash = block_info.last_seen_block_hash;
531 self.all_transactions.set_block_info(block_info);
532
533 let mut removed_txs_count = 0;
535 for tx_hash in &mined_transactions {
536 if self.prune_transaction_by_hash(tx_hash).is_some() {
537 removed_txs_count += 1;
538 }
539 }
540
541 self.metrics.removed_transactions.increment(removed_txs_count);
543
544 let UpdateOutcome { promoted, discarded } = self.update_accounts(changed_senders);
545
546 self.update_transaction_type_metrics();
547 self.metrics.performed_state_updates.increment(1);
548
549 self.latest_update_kind = Some(update_kind);
551
552 OnNewCanonicalStateOutcome { block_hash, mined: mined_transactions, promoted, discarded }
553 }
554
555 pub(crate) fn update_size_metrics(&self) {
557 let stats = self.size();
558 self.metrics.pending_pool_transactions.set(stats.pending as f64);
559 self.metrics.pending_pool_size_bytes.set(stats.pending_size as f64);
560 self.metrics.basefee_pool_transactions.set(stats.basefee as f64);
561 self.metrics.basefee_pool_size_bytes.set(stats.basefee_size as f64);
562 self.metrics.queued_pool_transactions.set(stats.queued as f64);
563 self.metrics.queued_pool_size_bytes.set(stats.queued_size as f64);
564 self.metrics.blob_pool_transactions.set(stats.blob as f64);
565 self.metrics.blob_pool_size_bytes.set(stats.blob_size as f64);
566 self.metrics.total_transactions.set(stats.total as f64);
567 }
568
569 pub(crate) fn update_transaction_type_metrics(&self) {
571 let mut legacy_count = 0;
572 let mut eip2930_count = 0;
573 let mut eip1559_count = 0;
574 let mut eip4844_count = 0;
575 let mut eip7702_count = 0;
576
577 for tx in self.all_transactions.transactions_iter() {
578 match tx.transaction.ty() {
579 LEGACY_TX_TYPE_ID => legacy_count += 1,
580 EIP2930_TX_TYPE_ID => eip2930_count += 1,
581 EIP1559_TX_TYPE_ID => eip1559_count += 1,
582 EIP4844_TX_TYPE_ID => eip4844_count += 1,
583 EIP7702_TX_TYPE_ID => eip7702_count += 1,
584 _ => {} }
586 }
587
588 self.metrics.total_legacy_transactions.set(legacy_count as f64);
589 self.metrics.total_eip2930_transactions.set(eip2930_count as f64);
590 self.metrics.total_eip1559_transactions.set(eip1559_count as f64);
591 self.metrics.total_eip4844_transactions.set(eip4844_count as f64);
592 self.metrics.total_eip7702_transactions.set(eip7702_count as f64);
593 }
594
595 pub(crate) fn add_transaction(
621 &mut self,
622 tx: ValidPoolTransaction<T::Transaction>,
623 on_chain_balance: U256,
624 on_chain_nonce: u64,
625 ) -> PoolResult<AddedTransaction<T::Transaction>> {
626 if self.contains(tx.hash()) {
627 return Err(PoolError::new(*tx.hash(), PoolErrorKind::AlreadyImported))
628 }
629
630 self.sender_info
632 .entry(tx.sender_id())
633 .or_default()
634 .update(on_chain_nonce, on_chain_balance);
635
636 match self.all_transactions.insert_tx(tx, on_chain_balance, on_chain_nonce) {
637 Ok(InsertOk { transaction, move_to, replaced_tx, updates, .. }) => {
638 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
640 self.metrics.inserted_transactions.increment(1);
642 let UpdateOutcome { promoted, discarded } = self.process_updates(updates);
643
644 let replaced = replaced_tx.map(|(tx, _)| tx);
645
646 let res = if move_to.is_pending() {
648 AddedTransaction::Pending(AddedPendingTransaction {
649 transaction,
650 promoted,
651 discarded,
652 replaced,
653 })
654 } else {
655 AddedTransaction::Parked { transaction, subpool: move_to, replaced }
656 };
657
658 self.update_size_metrics();
660
661 Ok(res)
662 }
663 Err(err) => {
664 self.metrics.invalid_transactions.increment(1);
666 match err {
667 InsertErr::Underpriced { existing: _, transaction } => Err(PoolError::new(
668 *transaction.hash(),
669 PoolErrorKind::ReplacementUnderpriced,
670 )),
671 InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap } => {
672 Err(PoolError::new(
673 *transaction.hash(),
674 PoolErrorKind::FeeCapBelowMinimumProtocolFeeCap(fee_cap),
675 ))
676 }
677 InsertErr::ExceededSenderTransactionsCapacity { transaction } => {
678 Err(PoolError::new(
679 *transaction.hash(),
680 PoolErrorKind::SpammerExceededCapacity(transaction.sender()),
681 ))
682 }
683 InsertErr::TxGasLimitMoreThanAvailableBlockGas {
684 transaction,
685 block_gas_limit,
686 tx_gas_limit,
687 } => Err(PoolError::new(
688 *transaction.hash(),
689 PoolErrorKind::InvalidTransaction(
690 InvalidPoolTransactionError::ExceedsGasLimit(
691 tx_gas_limit,
692 block_gas_limit,
693 ),
694 ),
695 )),
696 InsertErr::BlobTxHasNonceGap { transaction } => Err(PoolError::new(
697 *transaction.hash(),
698 PoolErrorKind::InvalidTransaction(
699 Eip4844PoolTransactionError::Eip4844NonceGap.into(),
700 ),
701 )),
702 InsertErr::Overdraft { transaction } => Err(PoolError::new(
703 *transaction.hash(),
704 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Overdraft {
705 cost: *transaction.cost(),
706 balance: on_chain_balance,
707 }),
708 )),
709 InsertErr::TxTypeConflict { transaction } => Err(PoolError::new(
710 *transaction.hash(),
711 PoolErrorKind::ExistingConflictingTransactionType(
712 transaction.sender(),
713 transaction.tx_type(),
714 ),
715 )),
716 }
717 }
718 }
719 }
720
721 fn process_updates(&mut self, updates: Vec<PoolUpdate>) -> UpdateOutcome<T::Transaction> {
725 let mut outcome = UpdateOutcome::default();
726 for PoolUpdate { id, hash, current, destination } in updates {
727 match destination {
728 Destination::Discard => {
729 if let Some(tx) = self.prune_transaction_by_hash(&hash) {
731 outcome.discarded.push(tx);
732 }
733 self.metrics.removed_transactions.increment(1);
734 }
735 Destination::Pool(move_to) => {
736 debug_assert_ne!(&move_to, ¤t, "destination must be different");
737 let moved = self.move_transaction(current, move_to, &id);
738 if matches!(move_to, SubPool::Pending) {
739 if let Some(tx) = moved {
740 trace!(target: "txpool", hash=%tx.transaction.hash(), "Promoted transaction to pending");
741 outcome.promoted.push(tx);
742 }
743 }
744 }
745 }
746 }
747 outcome
748 }
749
750 fn move_transaction(
755 &mut self,
756 from: SubPool,
757 to: SubPool,
758 id: &TransactionId,
759 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
760 let tx = self.remove_from_subpool(from, id)?;
761 self.add_transaction_to_subpool(to, tx.clone());
762 Some(tx)
763 }
764
765 pub(crate) fn remove_transactions(
770 &mut self,
771 hashes: Vec<TxHash>,
772 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
773 let txs =
774 hashes.into_iter().filter_map(|hash| self.remove_transaction_by_hash(&hash)).collect();
775 self.update_size_metrics();
776 txs
777 }
778
779 pub(crate) fn remove_transactions_and_descendants(
781 &mut self,
782 hashes: Vec<TxHash>,
783 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
784 let mut removed = Vec::new();
785 for hash in hashes {
786 if let Some(tx) = self.remove_transaction_by_hash(&hash) {
787 removed.push(tx.clone());
788 self.remove_descendants(tx.id(), &mut removed);
789 }
790 }
791 self.update_size_metrics();
792 removed
793 }
794
795 pub(crate) fn remove_transactions_by_sender(
797 &mut self,
798 sender_id: SenderId,
799 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
800 let mut removed = Vec::new();
801 let txs = self.get_transactions_by_sender(sender_id);
802 for tx in txs {
803 if let Some(tx) = self.remove_transaction(tx.id()) {
804 removed.push(tx);
805 }
806 }
807 self.update_size_metrics();
808 removed
809 }
810
811 fn remove_transaction(
815 &mut self,
816 id: &TransactionId,
817 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
818 let (tx, pool) = self.all_transactions.remove_transaction(id)?;
819 self.remove_from_subpool(pool, tx.id())
820 }
821
822 fn remove_transaction_by_hash(
828 &mut self,
829 tx_hash: &B256,
830 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
831 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
832
833 let updates = self.all_transactions.park_descendant_transactions(tx.id());
835 self.process_updates(updates);
836 self.remove_from_subpool(pool, tx.id())
837 }
838
839 fn prune_transaction_by_hash(
846 &mut self,
847 tx_hash: &B256,
848 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
849 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
850 self.remove_from_subpool(pool, tx.id())
851 }
852
853 fn remove_from_subpool(
857 &mut self,
858 pool: SubPool,
859 tx: &TransactionId,
860 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
861 let tx = match pool {
862 SubPool::Queued => self.queued_pool.remove_transaction(tx),
863 SubPool::Pending => self.pending_pool.remove_transaction(tx),
864 SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
865 SubPool::Blob => self.blob_pool.remove_transaction(tx),
866 };
867
868 if let Some(ref tx) = tx {
869 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Removed transaction from a subpool");
873 }
874
875 tx
876 }
877
878 fn remove_descendants(
882 &mut self,
883 tx: &TransactionId,
884 removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
885 ) {
886 let mut id = *tx;
887
888 loop {
890 let descendant =
891 self.all_transactions.descendant_txs_exclusive(&id).map(|(id, _)| *id).next();
892 if let Some(descendant) = descendant {
893 if let Some(tx) = self.remove_transaction(&descendant) {
894 removed.push(tx)
895 }
896 id = descendant;
897 } else {
898 return
899 }
900 }
901 }
902
903 fn add_transaction_to_subpool(
905 &mut self,
906 pool: SubPool,
907 tx: Arc<ValidPoolTransaction<T::Transaction>>,
908 ) {
909 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Adding transaction to a subpool");
913 match pool {
914 SubPool::Queued => self.queued_pool.add_transaction(tx),
915 SubPool::Pending => {
916 self.pending_pool.add_transaction(tx, self.all_transactions.pending_fees.base_fee);
917 }
918 SubPool::BaseFee => self.basefee_pool.add_transaction(tx),
919 SubPool::Blob => self.blob_pool.add_transaction(tx),
920 }
921 }
922
923 fn add_new_transaction(
926 &mut self,
927 transaction: Arc<ValidPoolTransaction<T::Transaction>>,
928 replaced: Option<(Arc<ValidPoolTransaction<T::Transaction>>, SubPool)>,
929 pool: SubPool,
930 ) {
931 if let Some((replaced, replaced_pool)) = replaced {
932 self.remove_from_subpool(replaced_pool, replaced.id());
934 }
935
936 self.add_transaction_to_subpool(pool, transaction)
937 }
938
939 pub(crate) fn discard_worst(&mut self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
946 let mut removed = Vec::new();
947
948 macro_rules! discard_worst {
950 ($this:ident, $removed:ident, [$($limit:ident => ($pool:ident, $metric:ident)),* $(,)*]) => {
951 $ (
952 while $this.$pool.exceeds(&$this.config.$limit)
953 {
954 trace!(
955 target: "txpool",
956 "discarding transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
957 stringify!($pool),
958 $this.config.$limit,
959 $this.$pool.size(),
960 $this.$pool.len(),
961 );
962
963 let removed_from_subpool = $this.$pool.truncate_pool($this.config.$limit.clone());
965
966 trace!(
967 target: "txpool",
968 "removed {} transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
969 removed_from_subpool.len(),
970 stringify!($pool),
971 $this.config.$limit,
972 $this.$pool.size(),
973 $this.$pool.len()
974 );
975 $this.metrics.$metric.increment(removed_from_subpool.len() as u64);
976
977 for tx in removed_from_subpool {
979 $this.all_transactions.remove_transaction(tx.id());
980
981 let id = *tx.id();
982
983 removed.push(tx);
985
986 $this.remove_descendants(&id, &mut $removed);
988 }
989 }
990
991 )*
992 };
993 }
994
995 discard_worst!(
996 self, removed, [
997 pending_limit => (pending_pool, pending_transactions_evicted),
998 basefee_limit => (basefee_pool, basefee_transactions_evicted),
999 blob_limit => (blob_pool, blob_transactions_evicted),
1000 queued_limit => (queued_pool, queued_transactions_evicted),
1001 ]
1002 );
1003
1004 removed
1005 }
1006
1007 pub(crate) fn len(&self) -> usize {
1009 self.all_transactions.len()
1010 }
1011
1012 pub(crate) fn is_empty(&self) -> bool {
1014 self.all_transactions.is_empty()
1015 }
1016
1017 #[cfg(any(test, feature = "test-utils"))]
1025 pub fn assert_invariants(&self) {
1026 let size = self.size();
1027 let actual = size.basefee + size.pending + size.queued + size.blob;
1028 assert_eq!(
1029 size.total, actual,
1030 "total size must be equal to the sum of all sub-pools, basefee:{}, pending:{}, queued:{}, blob:{}",
1031 size.basefee, size.pending, size.queued, size.blob
1032 );
1033 self.all_transactions.assert_invariants();
1034 self.pending_pool.assert_invariants();
1035 self.basefee_pool.assert_invariants();
1036 self.queued_pool.assert_invariants();
1037 self.blob_pool.assert_invariants();
1038 }
1039}
1040
1041#[cfg(any(test, feature = "test-utils"))]
1042impl TxPool<crate::test_utils::MockOrdering> {
1043 pub fn mock() -> Self {
1045 Self::new(crate::test_utils::MockOrdering::default(), PoolConfig::default())
1046 }
1047}
1048
1049#[cfg(test)]
1050impl<T: TransactionOrdering> Drop for TxPool<T> {
1051 fn drop(&mut self) {
1052 self.assert_invariants();
1053 }
1054}
1055
1056#[cfg(any(test, feature = "test-utils"))]
1058impl<T: TransactionOrdering> TxPool<T> {
1059 pub(crate) const fn pending(&self) -> &PendingPool<T> {
1060 &self.pending_pool
1061 }
1062
1063 pub(crate) const fn base_fee(&self) -> &ParkedPool<BasefeeOrd<T::Transaction>> {
1064 &self.basefee_pool
1065 }
1066
1067 pub(crate) const fn queued(&self) -> &ParkedPool<QueuedOrd<T::Transaction>> {
1068 &self.queued_pool
1069 }
1070}
1071
1072impl<T: TransactionOrdering> fmt::Debug for TxPool<T> {
1073 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1074 f.debug_struct("TxPool").field("config", &self.config).finish_non_exhaustive()
1075 }
1076}
1077
1078pub(crate) struct AllTransactions<T: PoolTransaction> {
1083 minimal_protocol_basefee: u64,
1087 block_gas_limit: u64,
1089 max_account_slots: usize,
1091 by_hash: HashMap<TxHash, Arc<ValidPoolTransaction<T>>>,
1093 txs: BTreeMap<TransactionId, PoolInternalTransaction<T>>,
1095 tx_counter: FxHashMap<SenderId, usize>,
1097 last_seen_block_number: u64,
1099 last_seen_block_hash: B256,
1101 pending_fees: PendingFees,
1103 price_bumps: PriceBumpConfig,
1105 local_transactions_config: LocalTransactionConfig,
1107 metrics: AllTransactionsMetrics,
1109}
1110
1111impl<T: PoolTransaction> AllTransactions<T> {
1112 fn new(config: &PoolConfig) -> Self {
1114 Self {
1115 max_account_slots: config.max_account_slots,
1116 price_bumps: config.price_bumps,
1117 local_transactions_config: config.local_transactions_config.clone(),
1118 minimal_protocol_basefee: config.minimal_protocol_basefee,
1119 block_gas_limit: config.gas_limit,
1120 ..Default::default()
1121 }
1122 }
1123
1124 #[expect(dead_code)]
1126 pub(crate) fn hashes_iter(&self) -> impl Iterator<Item = TxHash> + '_ {
1127 self.by_hash.keys().copied()
1128 }
1129
1130 pub(crate) fn transactions_iter(
1132 &self,
1133 ) -> impl Iterator<Item = &Arc<ValidPoolTransaction<T>>> + '_ {
1134 self.by_hash.values()
1135 }
1136
1137 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
1139 self.by_hash.contains_key(tx_hash)
1140 }
1141
1142 pub(crate) fn get(&self, id: &TransactionId) -> Option<&PoolInternalTransaction<T>> {
1144 self.txs.get(id)
1145 }
1146
1147 pub(crate) fn tx_inc(&mut self, sender: SenderId) {
1149 let count = self.tx_counter.entry(sender).or_default();
1150 *count += 1;
1151 self.metrics.all_transactions_by_all_senders.increment(1.0);
1152 }
1153
1154 pub(crate) fn tx_decr(&mut self, sender: SenderId) {
1156 if let hash_map::Entry::Occupied(mut entry) = self.tx_counter.entry(sender) {
1157 let count = entry.get_mut();
1158 if *count == 1 {
1159 entry.remove();
1160 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1161 return
1162 }
1163 *count -= 1;
1164 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1165 }
1166 }
1167
1168 fn set_block_info(&mut self, block_info: BlockInfo) {
1170 let BlockInfo {
1171 block_gas_limit,
1172 last_seen_block_hash,
1173 last_seen_block_number,
1174 pending_basefee,
1175 pending_blob_fee,
1176 } = block_info;
1177 self.last_seen_block_number = last_seen_block_number;
1178 self.last_seen_block_hash = last_seen_block_hash;
1179
1180 self.pending_fees.base_fee = pending_basefee;
1181 self.metrics.base_fee.set(pending_basefee as f64);
1182
1183 self.block_gas_limit = block_gas_limit;
1184
1185 if let Some(pending_blob_fee) = pending_blob_fee {
1186 self.pending_fees.blob_fee = pending_blob_fee;
1187 self.metrics.blob_base_fee.set(pending_blob_fee as f64);
1188 }
1189 }
1190
1191 pub(crate) fn update_size_metrics(&self) {
1193 self.metrics.all_transactions_by_hash.set(self.by_hash.len() as f64);
1194 self.metrics.all_transactions_by_id.set(self.txs.len() as f64);
1195 }
1196
1197 pub(crate) fn update(
1214 &mut self,
1215 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1216 ) -> Vec<PoolUpdate> {
1217 let mut updates = Vec::with_capacity(64);
1219
1220 let mut iter = self.txs.iter_mut().peekable();
1221
1222 'transactions: while let Some((id, tx)) = iter.next() {
1232 macro_rules! next_sender {
1233 ($iter:ident) => {
1234 'this: while let Some((peek, _)) = iter.peek() {
1235 if peek.sender != id.sender {
1236 break 'this
1237 }
1238 iter.next();
1239 }
1240 };
1241 }
1242 let mut changed_balance = None;
1244
1245 if let Some(info) = changed_accounts.get(&id.sender) {
1247 if id.nonce < info.state_nonce {
1249 updates.push(PoolUpdate {
1250 id: *tx.transaction.id(),
1251 hash: *tx.transaction.hash(),
1252 current: tx.subpool,
1253 destination: Destination::Discard,
1254 });
1255 continue 'transactions
1256 }
1257
1258 let ancestor = TransactionId::ancestor(id.nonce, info.state_nonce, id.sender);
1259 if ancestor.is_none() {
1261 tx.state.insert(TxState::NO_NONCE_GAPS);
1262 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1263 tx.cumulative_cost = U256::ZERO;
1264 if tx.transaction.cost() > &info.balance {
1265 tx.state.remove(TxState::ENOUGH_BALANCE);
1267 } else {
1268 tx.state.insert(TxState::ENOUGH_BALANCE);
1269 }
1270 }
1271
1272 changed_balance = Some(&info.balance);
1273 }
1274
1275 if tx.state.has_nonce_gap() {
1277 next_sender!(iter);
1278 continue 'transactions
1279 }
1280
1281 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1283
1284 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1286 Self::record_subpool_update(&mut updates, tx);
1288
1289 let mut has_parked_ancestor = !tx.state.is_pending();
1291
1292 let mut cumulative_cost = tx.next_cumulative_cost();
1293
1294 let mut next_nonce_in_line = tx.transaction.nonce().saturating_add(1);
1296
1297 while let Some((peek, tx)) = iter.peek_mut() {
1299 if peek.sender != id.sender {
1300 continue 'transactions
1302 }
1303
1304 if tx.transaction.nonce() == next_nonce_in_line {
1305 tx.state.insert(TxState::NO_NONCE_GAPS);
1307 } else {
1308 next_sender!(iter);
1310 continue 'transactions
1311 }
1312
1313 next_nonce_in_line = next_nonce_in_line.saturating_add(1);
1315
1316 tx.cumulative_cost = cumulative_cost;
1318 cumulative_cost = tx.next_cumulative_cost();
1320
1321 if let Some(changed_balance) = changed_balance {
1323 if &cumulative_cost > changed_balance {
1324 tx.state.remove(TxState::ENOUGH_BALANCE);
1326 } else {
1327 tx.state.insert(TxState::ENOUGH_BALANCE);
1328 }
1329 }
1330
1331 if has_parked_ancestor {
1333 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1334 } else {
1335 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1336 }
1337 has_parked_ancestor = !tx.state.is_pending();
1338
1339 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1341 Self::record_subpool_update(&mut updates, tx);
1342
1343 iter.next();
1345 }
1346 }
1347
1348 updates
1349 }
1350
1351 fn record_subpool_update(updates: &mut Vec<PoolUpdate>, tx: &mut PoolInternalTransaction<T>) {
1356 let current_pool = tx.subpool;
1357 tx.subpool = tx.state.into();
1358 if current_pool != tx.subpool {
1359 updates.push(PoolUpdate {
1360 id: *tx.transaction.id(),
1361 hash: *tx.transaction.hash(),
1362 current: current_pool,
1363 destination: tx.subpool.into(),
1364 })
1365 }
1366 }
1367
1368 fn update_tx_base_fee(pending_block_base_fee: u64, tx: &mut PoolInternalTransaction<T>) {
1370 match tx.transaction.max_fee_per_gas().cmp(&(pending_block_base_fee as u128)) {
1372 Ordering::Greater | Ordering::Equal => {
1373 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1374 }
1375 Ordering::Less => {
1376 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
1377 }
1378 }
1379 }
1380
1381 pub(crate) fn txs_iter(
1384 &self,
1385 sender: SenderId,
1386 ) -> impl Iterator<Item = (&TransactionId, &PoolInternalTransaction<T>)> + '_ {
1387 self.txs
1388 .range((sender.start_bound(), Unbounded))
1389 .take_while(move |(other, _)| sender == other.sender)
1390 }
1391
1392 #[cfg(test)]
1395 #[expect(dead_code)]
1396 pub(crate) fn txs_iter_mut(
1397 &mut self,
1398 sender: SenderId,
1399 ) -> impl Iterator<Item = (&TransactionId, &mut PoolInternalTransaction<T>)> + '_ {
1400 self.txs
1401 .range_mut((sender.start_bound(), Unbounded))
1402 .take_while(move |(other, _)| sender == other.sender)
1403 }
1404
1405 pub(crate) fn descendant_txs_exclusive<'a, 'b: 'a>(
1409 &'a self,
1410 id: &'b TransactionId,
1411 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1412 self.txs.range((Excluded(id), Unbounded)).take_while(|(other, _)| id.sender == other.sender)
1413 }
1414
1415 pub(crate) fn descendant_txs_inclusive<'a, 'b: 'a>(
1420 &'a self,
1421 id: &'b TransactionId,
1422 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1423 self.txs.range(id..).take_while(|(other, _)| id.sender == other.sender)
1424 }
1425
1426 pub(crate) fn descendant_txs_mut<'a, 'b: 'a>(
1431 &'a mut self,
1432 id: &'b TransactionId,
1433 ) -> impl Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)> + 'a {
1434 self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender)
1435 }
1436
1437 pub(crate) fn remove_transaction_by_hash(
1439 &mut self,
1440 tx_hash: &B256,
1441 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1442 let tx = self.by_hash.remove(tx_hash)?;
1443 let internal = self.txs.remove(&tx.transaction_id)?;
1444 self.tx_decr(tx.sender_id());
1446 self.update_size_metrics();
1447 Some((tx, internal.subpool))
1448 }
1449
1450 pub(crate) fn park_descendant_transactions(
1452 &mut self,
1453 tx_id: &TransactionId,
1454 ) -> Vec<PoolUpdate> {
1455 let mut updates = Vec::new();
1456
1457 for (id, tx) in self.descendant_txs_mut(tx_id) {
1458 let current_pool = tx.subpool;
1459
1460 tx.state.remove(TxState::NO_NONCE_GAPS);
1461
1462 tx.subpool = tx.state.into();
1464
1465 if current_pool != tx.subpool {
1467 updates.push(PoolUpdate {
1468 id: *id,
1469 hash: *tx.transaction.hash(),
1470 current: current_pool,
1471 destination: tx.subpool.into(),
1472 })
1473 }
1474 }
1475
1476 updates
1477 }
1478
1479 pub(crate) fn remove_transaction(
1485 &mut self,
1486 id: &TransactionId,
1487 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1488 let internal = self.txs.remove(id)?;
1489
1490 self.tx_decr(internal.transaction.sender_id());
1492
1493 let result =
1494 self.by_hash.remove(internal.transaction.hash()).map(|tx| (tx, internal.subpool));
1495
1496 self.update_size_metrics();
1497
1498 result
1499 }
1500
1501 #[inline]
1507 fn contains_conflicting_transaction(&self, tx: &ValidPoolTransaction<T>) -> bool {
1508 self.txs_iter(tx.transaction_id.sender)
1509 .next()
1510 .is_some_and(|(_, existing)| tx.tx_type_conflicts_with(&existing.transaction))
1511 }
1512
1513 fn ensure_valid(
1522 &self,
1523 transaction: ValidPoolTransaction<T>,
1524 on_chain_nonce: u64,
1525 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1526 if !self.local_transactions_config.is_local(transaction.origin, transaction.sender_ref()) {
1527 let current_txs =
1528 self.tx_counter.get(&transaction.sender_id()).copied().unwrap_or_default();
1529
1530 if current_txs >= self.max_account_slots && transaction.nonce() > on_chain_nonce {
1533 return Err(InsertErr::ExceededSenderTransactionsCapacity {
1534 transaction: Arc::new(transaction),
1535 })
1536 }
1537 }
1538 if transaction.gas_limit() > self.block_gas_limit {
1539 return Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas {
1540 block_gas_limit: self.block_gas_limit,
1541 tx_gas_limit: transaction.gas_limit(),
1542 transaction: Arc::new(transaction),
1543 })
1544 }
1545
1546 if self.contains_conflicting_transaction(&transaction) {
1547 return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) })
1549 }
1550
1551 Ok(transaction)
1552 }
1553
1554 fn ensure_valid_blob_transaction(
1560 &self,
1561 new_blob_tx: ValidPoolTransaction<T>,
1562 on_chain_balance: U256,
1563 ancestor: Option<TransactionId>,
1564 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1565 if let Some(ancestor) = ancestor {
1566 let Some(ancestor_tx) = self.txs.get(&ancestor) else {
1567 self.metrics.blob_transactions_nonce_gaps.increment(1);
1569 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1570 };
1571 if ancestor_tx.state.has_nonce_gap() {
1572 self.metrics.blob_transactions_nonce_gaps.increment(1);
1575 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1576 }
1577
1578 let mut cumulative_cost = ancestor_tx.next_cumulative_cost() + new_blob_tx.cost();
1580
1581 if cumulative_cost > on_chain_balance {
1583 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1585 }
1586
1587 let id = new_blob_tx.transaction_id;
1590 let mut descendants = self.descendant_txs_inclusive(&id).peekable();
1591 if let Some((maybe_replacement, _)) = descendants.peek() {
1592 if **maybe_replacement == new_blob_tx.transaction_id {
1593 descendants.next();
1595
1596 for (_, tx) in descendants {
1598 cumulative_cost += tx.transaction.cost();
1599 if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance {
1600 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1602 }
1603 }
1604 }
1605 }
1606 } else if new_blob_tx.cost() > &on_chain_balance {
1607 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1609 }
1610
1611 Ok(new_blob_tx)
1612 }
1613
1614 pub(crate) fn insert_tx(
1646 &mut self,
1647 transaction: ValidPoolTransaction<T>,
1648 on_chain_balance: U256,
1649 on_chain_nonce: u64,
1650 ) -> InsertResult<T> {
1651 assert!(on_chain_nonce <= transaction.nonce(), "Invalid transaction");
1652
1653 let mut transaction = self.ensure_valid(transaction, on_chain_nonce)?;
1654
1655 let inserted_tx_id = *transaction.id();
1656 let mut state = TxState::default();
1657 let mut cumulative_cost = U256::ZERO;
1658 let mut updates = Vec::new();
1659
1660 state.insert(TxState::NOT_TOO_MUCH_GAS);
1662
1663 let ancestor = TransactionId::ancestor(
1666 transaction.transaction.nonce(),
1667 on_chain_nonce,
1668 inserted_tx_id.sender,
1669 );
1670
1671 if transaction.is_eip4844() {
1674 state.insert(TxState::BLOB_TRANSACTION);
1675
1676 transaction =
1677 self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
1678 let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();
1679 if blob_fee_cap >= self.pending_fees.blob_fee {
1680 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1681 }
1682 } else {
1683 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1685 }
1686
1687 let transaction = Arc::new(transaction);
1688
1689 if ancestor.is_none() {
1691 state.insert(TxState::NO_NONCE_GAPS);
1692 state.insert(TxState::NO_PARKED_ANCESTORS);
1693 }
1694
1695 let fee_cap = transaction.max_fee_per_gas();
1697
1698 if fee_cap < self.minimal_protocol_basefee as u128 {
1699 return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap })
1700 }
1701 if fee_cap >= self.pending_fees.base_fee as u128 {
1702 state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1703 }
1704
1705 let mut replaced_tx = None;
1707
1708 let pool_tx = PoolInternalTransaction {
1709 transaction: Arc::clone(&transaction),
1710 subpool: state.into(),
1711 state,
1712 cumulative_cost,
1713 };
1714
1715 match self.txs.entry(*transaction.id()) {
1717 Entry::Vacant(entry) => {
1718 self.by_hash.insert(*pool_tx.transaction.hash(), pool_tx.transaction.clone());
1720 entry.insert(pool_tx);
1721 }
1722 Entry::Occupied(mut entry) => {
1723 let existing_transaction = entry.get().transaction.as_ref();
1725 let maybe_replacement = transaction.as_ref();
1726
1727 if existing_transaction.is_underpriced(maybe_replacement, &self.price_bumps) {
1729 return Err(InsertErr::Underpriced {
1730 transaction: pool_tx.transaction,
1731 existing: *entry.get().transaction.hash(),
1732 })
1733 }
1734 let new_hash = *pool_tx.transaction.hash();
1735 let new_transaction = pool_tx.transaction.clone();
1736 let replaced = entry.insert(pool_tx);
1737 self.by_hash.remove(replaced.transaction.hash());
1738 self.by_hash.insert(new_hash, new_transaction);
1739 replaced_tx = Some((replaced.transaction, replaced.subpool));
1741 }
1742 }
1743
1744 let on_chain_id = TransactionId::new(transaction.sender_id(), on_chain_nonce);
1746 {
1747 let mut next_nonce = on_chain_id.nonce;
1749
1750 let mut has_parked_ancestor = false;
1754
1755 for (id, tx) in self.descendant_txs_mut(&on_chain_id) {
1758 let current_pool = tx.subpool;
1759
1760 if next_nonce != id.nonce {
1762 break
1763 }
1764
1765 tx.state.insert(TxState::NO_NONCE_GAPS);
1767
1768 tx.cumulative_cost = cumulative_cost;
1770
1771 cumulative_cost = tx.next_cumulative_cost();
1773
1774 if cumulative_cost > on_chain_balance {
1775 tx.state.remove(TxState::ENOUGH_BALANCE);
1777 } else {
1778 tx.state.insert(TxState::ENOUGH_BALANCE);
1779 }
1780
1781 if has_parked_ancestor {
1783 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1784 } else {
1785 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1786 }
1787 has_parked_ancestor = !tx.state.is_pending();
1788
1789 tx.subpool = tx.state.into();
1791
1792 if inserted_tx_id.eq(id) {
1793 state = tx.state;
1795 } else {
1796 if current_pool != tx.subpool {
1798 updates.push(PoolUpdate {
1799 id: *id,
1800 hash: *tx.transaction.hash(),
1801 current: current_pool,
1802 destination: tx.subpool.into(),
1803 })
1804 }
1805 }
1806
1807 next_nonce = id.next_nonce();
1809 }
1810 }
1811
1812 if replaced_tx.is_none() {
1814 self.tx_inc(inserted_tx_id.sender);
1815 }
1816
1817 self.update_size_metrics();
1818
1819 Ok(InsertOk { transaction, move_to: state.into(), state, replaced_tx, updates })
1820 }
1821
1822 pub(crate) fn len(&self) -> usize {
1824 self.txs.len()
1825 }
1826
1827 pub(crate) fn is_empty(&self) -> bool {
1829 self.txs.is_empty()
1830 }
1831
1832 #[cfg(any(test, feature = "test-utils"))]
1834 pub(crate) fn assert_invariants(&self) {
1835 assert_eq!(self.by_hash.len(), self.txs.len(), "by_hash.len() != txs.len()");
1836 }
1837}
1838
1839#[cfg(test)]
1840impl<T: PoolTransaction> AllTransactions<T> {
1841 pub(crate) fn tx_count(&self, sender: SenderId) -> usize {
1845 self.tx_counter.get(&sender).copied().unwrap_or_default()
1846 }
1847}
1848
1849impl<T: PoolTransaction> Default for AllTransactions<T> {
1850 fn default() -> Self {
1851 Self {
1852 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
1853 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
1854 block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
1855 by_hash: Default::default(),
1856 txs: Default::default(),
1857 tx_counter: Default::default(),
1858 last_seen_block_number: Default::default(),
1859 last_seen_block_hash: Default::default(),
1860 pending_fees: Default::default(),
1861 price_bumps: Default::default(),
1862 local_transactions_config: Default::default(),
1863 metrics: Default::default(),
1864 }
1865 }
1866}
1867
1868#[derive(Debug, Clone)]
1870pub(crate) struct PendingFees {
1871 pub(crate) base_fee: u64,
1873 pub(crate) blob_fee: u128,
1875}
1876
1877impl Default for PendingFees {
1878 fn default() -> Self {
1879 Self { base_fee: Default::default(), blob_fee: BLOB_TX_MIN_BLOB_GASPRICE }
1880 }
1881}
1882
1883pub(crate) type InsertResult<T> = Result<InsertOk<T>, InsertErr<T>>;
1885
1886#[derive(Debug)]
1888pub(crate) enum InsertErr<T: PoolTransaction> {
1889 Underpriced {
1891 transaction: Arc<ValidPoolTransaction<T>>,
1892 #[expect(dead_code)]
1893 existing: TxHash,
1894 },
1895 BlobTxHasNonceGap { transaction: Arc<ValidPoolTransaction<T>> },
1897 Overdraft { transaction: Arc<ValidPoolTransaction<T>> },
1900 FeeCapBelowMinimumProtocolFeeCap { transaction: Arc<ValidPoolTransaction<T>>, fee_cap: u128 },
1904 ExceededSenderTransactionsCapacity { transaction: Arc<ValidPoolTransaction<T>> },
1908 TxGasLimitMoreThanAvailableBlockGas {
1910 transaction: Arc<ValidPoolTransaction<T>>,
1911 block_gas_limit: u64,
1912 tx_gas_limit: u64,
1913 },
1914 TxTypeConflict { transaction: Arc<ValidPoolTransaction<T>> },
1916}
1917
1918#[derive(Debug)]
1920pub(crate) struct InsertOk<T: PoolTransaction> {
1921 transaction: Arc<ValidPoolTransaction<T>>,
1923 move_to: SubPool,
1925 #[cfg_attr(not(test), expect(dead_code))]
1927 state: TxState,
1928 replaced_tx: Option<(Arc<ValidPoolTransaction<T>>, SubPool)>,
1930 updates: Vec<PoolUpdate>,
1932}
1933
1934#[derive(Debug)]
1937pub(crate) struct PoolInternalTransaction<T: PoolTransaction> {
1938 pub(crate) transaction: Arc<ValidPoolTransaction<T>>,
1940 pub(crate) subpool: SubPool,
1942 pub(crate) state: TxState,
1945 pub(crate) cumulative_cost: U256,
1950}
1951
1952impl<T: PoolTransaction> PoolInternalTransaction<T> {
1955 fn next_cumulative_cost(&self) -> U256 {
1956 self.cumulative_cost + self.transaction.cost()
1957 }
1958}
1959
1960#[derive(Debug, Clone, Default)]
1962pub(crate) struct SenderInfo {
1963 pub(crate) state_nonce: u64,
1965 pub(crate) balance: U256,
1967}
1968
1969impl SenderInfo {
1972 const fn update(&mut self, state_nonce: u64, balance: U256) {
1974 *self = Self { state_nonce, balance };
1975 }
1976}
1977
1978#[cfg(test)]
1979mod tests {
1980 use super::*;
1981 use crate::{
1982 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
1983 traits::TransactionOrigin,
1984 SubPoolLimit,
1985 };
1986 use alloy_consensus::{Transaction, TxType};
1987 use alloy_primitives::address;
1988
1989 #[test]
1990 fn test_insert_blob() {
1991 let on_chain_balance = U256::MAX;
1992 let on_chain_nonce = 0;
1993 let mut f = MockTransactionFactory::default();
1994 let mut pool = AllTransactions::default();
1995 let tx = MockTransaction::eip4844().inc_price().inc_limit();
1996 let valid_tx = f.validated(tx);
1997 let InsertOk { updates, replaced_tx, move_to, state, .. } =
1998 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
1999 assert!(updates.is_empty());
2000 assert!(replaced_tx.is_none());
2001 assert!(state.contains(TxState::NO_NONCE_GAPS));
2002 assert!(state.contains(TxState::ENOUGH_BALANCE));
2003 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2004 assert_eq!(move_to, SubPool::Pending);
2005
2006 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2007 assert_eq!(inserted.subpool, SubPool::Pending);
2008 }
2009
2010 #[test]
2011 fn test_insert_blob_not_enough_blob_fee() {
2012 let on_chain_balance = U256::MAX;
2013 let on_chain_nonce = 0;
2014 let mut f = MockTransactionFactory::default();
2015 let mut pool = AllTransactions {
2016 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2017 ..Default::default()
2018 };
2019 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2020 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2021 let valid_tx = f.validated(tx);
2022 let InsertOk { state, .. } =
2023 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2024 assert!(state.contains(TxState::NO_NONCE_GAPS));
2025 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2026
2027 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2028 }
2029
2030 #[test]
2031 fn test_valid_tx_with_decreasing_blob_fee() {
2032 let on_chain_balance = U256::MAX;
2033 let on_chain_nonce = 0;
2034 let mut f = MockTransactionFactory::default();
2035 let mut pool = AllTransactions {
2036 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2037 ..Default::default()
2038 };
2039 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2040
2041 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2042 let valid_tx = f.validated(tx.clone());
2043 let InsertOk { state, .. } =
2044 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2045 assert!(state.contains(TxState::NO_NONCE_GAPS));
2046 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2047
2048 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2049 pool.remove_transaction(&valid_tx.transaction_id);
2050
2051 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
2052 let InsertOk { state, .. } =
2053 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2054 assert!(state.contains(TxState::NO_NONCE_GAPS));
2055 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2056 }
2057
2058 #[test]
2059 fn test_demote_valid_tx_with_increasing_blob_fee() {
2060 let on_chain_balance = U256::MAX;
2061 let on_chain_nonce = 0;
2062 let mut f = MockTransactionFactory::default();
2063 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2064 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2065
2066 let mut block_info = pool.block_info();
2068 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2069 pool.set_block_info(block_info);
2070
2071 let validated = f.validated(tx.clone());
2072 let id = *validated.id();
2073 pool.add_transaction(validated, on_chain_balance, on_chain_nonce).unwrap();
2074
2075 assert!(pool.blob_pool.is_empty());
2077 assert_eq!(pool.pending_pool.len(), 1);
2078
2079 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2081 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2082 assert_eq!(internal_tx.subpool, SubPool::Pending);
2083
2084 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2086 pool.set_block_info(block_info);
2087
2088 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2090 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2091 assert_eq!(internal_tx.subpool, SubPool::Blob);
2092
2093 assert_eq!(pool.blob_pool.len(), 1);
2095 assert!(pool.pending_pool.is_empty());
2096 }
2097
2098 #[test]
2099 fn test_promote_valid_tx_with_decreasing_blob_fee() {
2100 let on_chain_balance = U256::MAX;
2101 let on_chain_nonce = 0;
2102 let mut f = MockTransactionFactory::default();
2103 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2104 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2105
2106 let mut block_info = pool.block_info();
2108 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2109 pool.set_block_info(block_info);
2110
2111 let validated = f.validated(tx.clone());
2112 let id = *validated.id();
2113 pool.add_transaction(validated, on_chain_balance, on_chain_nonce).unwrap();
2114
2115 assert!(pool.pending_pool.is_empty());
2117 assert_eq!(pool.blob_pool.len(), 1);
2118
2119 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2121 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2122 assert_eq!(internal_tx.subpool, SubPool::Blob);
2123
2124 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2126 pool.set_block_info(block_info);
2127
2128 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2130 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2131 assert_eq!(internal_tx.subpool, SubPool::Pending);
2132
2133 assert_eq!(pool.pending_pool.len(), 1);
2135 assert!(pool.blob_pool.is_empty());
2136 }
2137
2138 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
2140 struct PromotionTest {
2141 basefee: u64,
2143 blobfee: u128,
2145 subpool: SubPool,
2147 basefee_update: u64,
2149 blobfee_update: u128,
2151 new_subpool: SubPool,
2153 }
2154
2155 impl PromotionTest {
2156 const fn opposite(&self) -> Self {
2158 Self {
2159 basefee: self.basefee_update,
2160 blobfee: self.blobfee_update,
2161 subpool: self.new_subpool,
2162 blobfee_update: self.blobfee,
2163 basefee_update: self.basefee,
2164 new_subpool: self.subpool,
2165 }
2166 }
2167
2168 fn assert_subpool_lengths<T: TransactionOrdering>(
2169 &self,
2170 pool: &TxPool<T>,
2171 failure_message: String,
2172 check_subpool: SubPool,
2173 ) {
2174 match check_subpool {
2175 SubPool::Blob => {
2176 assert_eq!(pool.blob_pool.len(), 1, "{failure_message}");
2177 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2178 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2179 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2180 }
2181 SubPool::Pending => {
2182 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2183 assert_eq!(pool.pending_pool.len(), 1, "{failure_message}");
2184 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2185 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2186 }
2187 SubPool::BaseFee => {
2188 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2189 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2190 assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}");
2191 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2192 }
2193 SubPool::Queued => {
2194 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2195 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2196 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2197 assert_eq!(pool.queued_pool.len(), 1, "{failure_message}");
2198 }
2199 }
2200 }
2201
2202 fn assert_single_tx_starting_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2206 self.assert_subpool_lengths(
2207 pool,
2208 format!("pool length check failed at start of test: {self:?}"),
2209 self.subpool,
2210 );
2211 }
2212
2213 fn assert_single_tx_ending_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2217 self.assert_subpool_lengths(
2218 pool,
2219 format!("pool length check failed at end of test: {self:?}"),
2220 self.new_subpool,
2221 );
2222 }
2223 }
2224
2225 #[test]
2226 fn test_promote_blob_tx_with_both_pending_fee_updates() {
2227 let on_chain_balance = U256::MAX;
2230 let on_chain_nonce = 0;
2231 let mut f = MockTransactionFactory::default();
2232 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2233
2234 let max_fee_per_blob_gas = tx.max_fee_per_blob_gas().unwrap();
2235 let max_fee_per_gas = tx.max_fee_per_gas() as u64;
2236
2237 let mut expected_promotions = vec![
2239 PromotionTest {
2240 blobfee: max_fee_per_blob_gas + 1,
2241 basefee: max_fee_per_gas + 1,
2242 subpool: SubPool::Blob,
2243 blobfee_update: max_fee_per_blob_gas + 1,
2244 basefee_update: max_fee_per_gas + 1,
2245 new_subpool: SubPool::Blob,
2246 },
2247 PromotionTest {
2248 blobfee: max_fee_per_blob_gas + 1,
2249 basefee: max_fee_per_gas + 1,
2250 subpool: SubPool::Blob,
2251 blobfee_update: max_fee_per_blob_gas,
2252 basefee_update: max_fee_per_gas + 1,
2253 new_subpool: SubPool::Blob,
2254 },
2255 PromotionTest {
2256 blobfee: max_fee_per_blob_gas + 1,
2257 basefee: max_fee_per_gas + 1,
2258 subpool: SubPool::Blob,
2259 blobfee_update: max_fee_per_blob_gas + 1,
2260 basefee_update: max_fee_per_gas,
2261 new_subpool: SubPool::Blob,
2262 },
2263 PromotionTest {
2264 blobfee: max_fee_per_blob_gas + 1,
2265 basefee: max_fee_per_gas + 1,
2266 subpool: SubPool::Blob,
2267 blobfee_update: max_fee_per_blob_gas,
2268 basefee_update: max_fee_per_gas,
2269 new_subpool: SubPool::Pending,
2270 },
2271 PromotionTest {
2272 blobfee: max_fee_per_blob_gas,
2273 basefee: max_fee_per_gas + 1,
2274 subpool: SubPool::Blob,
2275 blobfee_update: max_fee_per_blob_gas,
2276 basefee_update: max_fee_per_gas,
2277 new_subpool: SubPool::Pending,
2278 },
2279 PromotionTest {
2280 blobfee: max_fee_per_blob_gas + 1,
2281 basefee: max_fee_per_gas,
2282 subpool: SubPool::Blob,
2283 blobfee_update: max_fee_per_blob_gas,
2284 basefee_update: max_fee_per_gas,
2285 new_subpool: SubPool::Pending,
2286 },
2287 PromotionTest {
2288 blobfee: max_fee_per_blob_gas,
2289 basefee: max_fee_per_gas,
2290 subpool: SubPool::Pending,
2291 blobfee_update: max_fee_per_blob_gas,
2292 basefee_update: max_fee_per_gas,
2293 new_subpool: SubPool::Pending,
2294 },
2295 ];
2296
2297 let reversed = expected_promotions.iter().map(|test| test.opposite()).collect::<Vec<_>>();
2299 expected_promotions.extend(reversed);
2300
2301 let expected_promotions = expected_promotions.into_iter().collect::<HashSet<_>>();
2303
2304 for promotion_test in &expected_promotions {
2305 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2306
2307 let mut block_info = pool.block_info();
2309
2310 block_info.pending_blob_fee = Some(promotion_test.blobfee);
2311 block_info.pending_basefee = promotion_test.basefee;
2312 pool.set_block_info(block_info);
2313
2314 let validated = f.validated(tx.clone());
2315 let id = *validated.id();
2316 pool.add_transaction(validated, on_chain_balance, on_chain_nonce).unwrap();
2317
2318 promotion_test.assert_single_tx_starting_subpool(&pool);
2320
2321 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2323 assert_eq!(
2324 internal_tx.subpool, promotion_test.subpool,
2325 "Subpools do not match at start of test: {promotion_test:?}"
2326 );
2327
2328 block_info.pending_basefee = promotion_test.basefee_update;
2330 block_info.pending_blob_fee = Some(promotion_test.blobfee_update);
2331 pool.set_block_info(block_info);
2332
2333 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2335 assert_eq!(
2336 internal_tx.subpool, promotion_test.new_subpool,
2337 "Subpools do not match at end of test: {promotion_test:?}"
2338 );
2339
2340 promotion_test.assert_single_tx_ending_subpool(&pool);
2342 }
2343 }
2344
2345 #[test]
2346 fn test_insert_pending() {
2347 let on_chain_balance = U256::MAX;
2348 let on_chain_nonce = 0;
2349 let mut f = MockTransactionFactory::default();
2350 let mut pool = AllTransactions::default();
2351 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2352 let valid_tx = f.validated(tx);
2353 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2354 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2355 assert!(updates.is_empty());
2356 assert!(replaced_tx.is_none());
2357 assert!(state.contains(TxState::NO_NONCE_GAPS));
2358 assert!(state.contains(TxState::ENOUGH_BALANCE));
2359 assert_eq!(move_to, SubPool::Pending);
2360
2361 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2362 assert_eq!(inserted.subpool, SubPool::Pending);
2363 }
2364
2365 #[test]
2366 fn test_simple_insert() {
2367 let on_chain_balance = U256::ZERO;
2368 let on_chain_nonce = 0;
2369 let mut f = MockTransactionFactory::default();
2370 let mut pool = AllTransactions::default();
2371 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2372 tx.set_priority_fee(100);
2373 tx.set_max_fee(100);
2374 let valid_tx = f.validated(tx.clone());
2375 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2376 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2377 assert!(updates.is_empty());
2378 assert!(replaced_tx.is_none());
2379 assert!(state.contains(TxState::NO_NONCE_GAPS));
2380 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2381 assert_eq!(move_to, SubPool::Queued);
2382
2383 assert_eq!(pool.len(), 1);
2384 assert!(pool.contains(valid_tx.hash()));
2385 let expected_state = TxState::ENOUGH_FEE_CAP_BLOCK | TxState::NO_NONCE_GAPS;
2386 let inserted = pool.get(valid_tx.id()).unwrap();
2387 assert!(inserted.state.intersects(expected_state));
2388
2389 let res = pool.insert_tx(valid_tx, on_chain_balance, on_chain_nonce);
2391 res.unwrap_err();
2392 assert_eq!(pool.len(), 1);
2393
2394 let valid_tx = f.validated(tx.next());
2395 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2396 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2397
2398 assert!(updates.is_empty());
2399 assert!(replaced_tx.is_none());
2400 assert!(state.contains(TxState::NO_NONCE_GAPS));
2401 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2402 assert_eq!(move_to, SubPool::Queued);
2403
2404 assert!(pool.contains(valid_tx.hash()));
2405 assert_eq!(pool.len(), 2);
2406 let inserted = pool.get(valid_tx.id()).unwrap();
2407 assert!(inserted.state.intersects(expected_state));
2408 }
2409
2410 #[test]
2411 fn insert_already_imported() {
2412 let on_chain_balance = U256::ZERO;
2413 let on_chain_nonce = 0;
2414 let mut f = MockTransactionFactory::default();
2415 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2416 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2417 let tx = f.validated(tx);
2418 pool.add_transaction(tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2419 match pool.add_transaction(tx, on_chain_balance, on_chain_nonce).unwrap_err().kind {
2420 PoolErrorKind::AlreadyImported => {}
2421 _ => unreachable!(),
2422 }
2423 }
2424
2425 #[test]
2426 fn insert_replace() {
2427 let on_chain_balance = U256::ZERO;
2428 let on_chain_nonce = 0;
2429 let mut f = MockTransactionFactory::default();
2430 let mut pool = AllTransactions::default();
2431 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2432 let first = f.validated(tx.clone());
2433 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
2434 let replacement = f.validated(tx.rng_hash().inc_price());
2435 let InsertOk { updates, replaced_tx, .. } =
2436 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
2437 assert!(updates.is_empty());
2438 let replaced = replaced_tx.unwrap();
2439 assert_eq!(replaced.0.hash(), first.hash());
2440
2441 assert!(!pool.contains(first.hash()));
2443 assert!(pool.contains(replacement.hash()));
2444 assert_eq!(pool.len(), 1);
2445 }
2446
2447 #[test]
2448 fn insert_replace_txpool() {
2449 let on_chain_balance = U256::ZERO;
2450 let on_chain_nonce = 0;
2451 let mut f = MockTransactionFactory::default();
2452 let mut pool = TxPool::mock();
2453
2454 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2455 let first = f.validated(tx.clone());
2456 let first_added = pool.add_transaction(first, on_chain_balance, on_chain_nonce).unwrap();
2457 let replacement = f.validated(tx.rng_hash().inc_price());
2458 let replacement_added =
2459 pool.add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
2460
2461 assert!(!pool.contains(first_added.hash()));
2463 assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));
2465
2466 assert!(pool.contains(replacement.hash()));
2467 let size = pool.size();
2468 assert_eq!(size.total, 1);
2469 size.assert_invariants();
2470 }
2471
2472 #[test]
2473 fn insert_replace_underpriced() {
2474 let on_chain_balance = U256::ZERO;
2475 let on_chain_nonce = 0;
2476 let mut f = MockTransactionFactory::default();
2477 let mut pool = AllTransactions::default();
2478 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2479 let first = f.validated(tx.clone());
2480 let _res = pool.insert_tx(first, on_chain_balance, on_chain_nonce);
2481 let mut replacement = f.validated(tx.rng_hash());
2482 replacement.transaction = replacement.transaction.decr_price();
2483 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
2484 assert!(matches!(err, InsertErr::Underpriced { .. }));
2485 }
2486
2487 #[test]
2488 fn insert_replace_underpriced_not_enough_bump() {
2489 let on_chain_balance = U256::ZERO;
2490 let on_chain_nonce = 0;
2491 let mut f = MockTransactionFactory::default();
2492 let mut pool = AllTransactions::default();
2493 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2494 tx.set_priority_fee(100);
2495 tx.set_max_fee(100);
2496 let first = f.validated(tx.clone());
2497 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
2498 let mut replacement = f.validated(tx.rng_hash().inc_price());
2499
2500 replacement.transaction.set_priority_fee(109);
2502 replacement.transaction.set_max_fee(109);
2503 let err =
2504 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
2505 assert!(matches!(err, InsertErr::Underpriced { .. }));
2506 assert!(pool.contains(first.hash()));
2508 assert_eq!(pool.len(), 1);
2509
2510 replacement.transaction.set_priority_fee(110);
2512 replacement.transaction.set_max_fee(109);
2513 let err =
2514 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
2515 assert!(matches!(err, InsertErr::Underpriced { .. }));
2516 assert!(pool.contains(first.hash()));
2517 assert_eq!(pool.len(), 1);
2518
2519 replacement.transaction.set_priority_fee(109);
2521 replacement.transaction.set_max_fee(110);
2522 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
2523 assert!(matches!(err, InsertErr::Underpriced { .. }));
2524 assert!(pool.contains(first.hash()));
2525 assert_eq!(pool.len(), 1);
2526 }
2527
2528 #[test]
2529 fn insert_conflicting_type_normal_to_blob() {
2530 let on_chain_balance = U256::from(10_000);
2531 let on_chain_nonce = 0;
2532 let mut f = MockTransactionFactory::default();
2533 let mut pool = AllTransactions::default();
2534 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2535 let first = f.validated(tx.clone());
2536 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
2537 let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
2538 let blob = f.validated(tx);
2539 let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
2540 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
2541 }
2542
2543 #[test]
2544 fn insert_conflicting_type_blob_to_normal() {
2545 let on_chain_balance = U256::from(10_000);
2546 let on_chain_nonce = 0;
2547 let mut f = MockTransactionFactory::default();
2548 let mut pool = AllTransactions::default();
2549 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2550 let first = f.validated(tx.clone());
2551 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
2552 let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
2553 let tx = f.validated(tx);
2554 let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
2555 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
2556 }
2557
2558 #[test]
2560 fn insert_previous() {
2561 let on_chain_balance = U256::ZERO;
2562 let on_chain_nonce = 0;
2563 let mut f = MockTransactionFactory::default();
2564 let mut pool = AllTransactions::default();
2565 let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
2566 let first = f.validated(tx.clone());
2567 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
2568
2569 let first_in_pool = pool.get(first.id()).unwrap();
2570
2571 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2573
2574 let prev = f.validated(tx.prev());
2575 let InsertOk { updates, replaced_tx, state, move_to, .. } =
2576 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
2577
2578 assert!(updates.is_empty());
2580 assert!(replaced_tx.is_none());
2581 assert!(state.contains(TxState::NO_NONCE_GAPS));
2582 assert_eq!(move_to, SubPool::Queued);
2583
2584 let first_in_pool = pool.get(first.id()).unwrap();
2585 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2587 }
2588
2589 #[test]
2591 fn insert_with_updates() {
2592 let on_chain_balance = U256::from(10_000);
2593 let on_chain_nonce = 0;
2594 let mut f = MockTransactionFactory::default();
2595 let mut pool = AllTransactions::default();
2596 let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
2597 let first = f.validated(tx.clone());
2598 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
2599
2600 let first_in_pool = pool.get(first.id()).unwrap();
2601 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2603 assert_eq!(SubPool::Queued, first_in_pool.subpool);
2604
2605 let prev = f.validated(tx.prev());
2606 let InsertOk { updates, replaced_tx, state, move_to, .. } =
2607 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
2608
2609 assert_eq!(updates.len(), 1);
2611 assert!(replaced_tx.is_none());
2612 assert!(state.contains(TxState::NO_NONCE_GAPS));
2613 assert_eq!(move_to, SubPool::Pending);
2614
2615 let first_in_pool = pool.get(first.id()).unwrap();
2616 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2618 assert_eq!(SubPool::Pending, first_in_pool.subpool);
2619 }
2620
2621 #[test]
2622 fn insert_previous_blocking() {
2623 let on_chain_balance = U256::from(1_000);
2624 let on_chain_nonce = 0;
2625 let mut f = MockTransactionFactory::default();
2626 let mut pool = AllTransactions::default();
2627 pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
2628 let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
2629 let first = f.validated(tx.clone());
2630
2631 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
2632
2633 let first_in_pool = pool.get(first.id()).unwrap();
2634
2635 assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
2636 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2638
2639 let prev = f.validated(tx.prev());
2640 let InsertOk { updates, replaced_tx, state, move_to, .. } =
2641 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
2642
2643 assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
2644 assert!(updates.is_empty());
2646 assert!(replaced_tx.is_none());
2647 assert!(state.contains(TxState::NO_NONCE_GAPS));
2648 assert_eq!(move_to, SubPool::BaseFee);
2649
2650 let first_in_pool = pool.get(first.id()).unwrap();
2651 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2653 }
2654
2655 #[test]
2656 fn rejects_spammer() {
2657 let on_chain_balance = U256::from(1_000);
2658 let on_chain_nonce = 0;
2659 let mut f = MockTransactionFactory::default();
2660 let mut pool = AllTransactions::default();
2661
2662 let mut tx = MockTransaction::eip1559();
2663 let unblocked_tx = tx.clone();
2664 for _ in 0..pool.max_account_slots {
2665 tx = tx.next();
2666 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
2667 }
2668
2669 assert_eq!(
2670 pool.max_account_slots,
2671 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
2672 );
2673
2674 let err =
2675 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
2676 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
2677
2678 assert!(pool
2679 .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
2680 .is_ok());
2681 }
2682
2683 #[test]
2684 fn allow_local_spamming() {
2685 let on_chain_balance = U256::from(1_000);
2686 let on_chain_nonce = 0;
2687 let mut f = MockTransactionFactory::default();
2688 let mut pool = AllTransactions::default();
2689
2690 let mut tx = MockTransaction::eip1559();
2691 for _ in 0..pool.max_account_slots {
2692 tx = tx.next();
2693 pool.insert_tx(
2694 f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
2695 on_chain_balance,
2696 on_chain_nonce,
2697 )
2698 .unwrap();
2699 }
2700
2701 assert_eq!(
2702 pool.max_account_slots,
2703 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
2704 );
2705
2706 pool.insert_tx(
2707 f.validated_with_origin(TransactionOrigin::Local, tx.next()),
2708 on_chain_balance,
2709 on_chain_nonce,
2710 )
2711 .unwrap();
2712 }
2713
2714 #[test]
2715 fn reject_tx_over_gas_limit() {
2716 let on_chain_balance = U256::from(1_000);
2717 let on_chain_nonce = 0;
2718 let mut f = MockTransactionFactory::default();
2719 let mut pool = AllTransactions::default();
2720
2721 let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
2722
2723 assert!(matches!(
2724 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
2725 Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
2726 ));
2727 }
2728
2729 #[test]
2730 fn test_tx_equal_gas_limit() {
2731 let on_chain_balance = U256::from(1_000);
2732 let on_chain_nonce = 0;
2733 let mut f = MockTransactionFactory::default();
2734 let mut pool = AllTransactions::default();
2735
2736 let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
2737
2738 let InsertOk { state, .. } =
2739 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
2740 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
2741 }
2742
2743 #[test]
2744 fn update_basefee_subpools() {
2745 let mut f = MockTransactionFactory::default();
2746 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2747
2748 let tx = MockTransaction::eip1559().inc_price_by(10);
2749 let validated = f.validated(tx.clone());
2750 let id = *validated.id();
2751 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
2752
2753 assert_eq!(pool.pending_pool.len(), 1);
2754
2755 pool.update_basefee((tx.max_fee_per_gas() + 1) as u64);
2756
2757 assert!(pool.pending_pool.is_empty());
2758 assert_eq!(pool.basefee_pool.len(), 1);
2759
2760 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
2761 }
2762
2763 #[test]
2764 fn update_basefee_subpools_setting_block_info() {
2765 let mut f = MockTransactionFactory::default();
2766 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2767
2768 let tx = MockTransaction::eip1559().inc_price_by(10);
2769 let validated = f.validated(tx.clone());
2770 let id = *validated.id();
2771 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
2772
2773 assert_eq!(pool.pending_pool.len(), 1);
2774
2775 let mut block_info = pool.block_info();
2777 block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
2778 pool.set_block_info(block_info);
2779
2780 assert!(pool.pending_pool.is_empty());
2781 assert_eq!(pool.basefee_pool.len(), 1);
2782
2783 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
2784 }
2785
2786 #[test]
2787 fn get_highest_transaction_by_sender_and_nonce() {
2788 let mut f = MockTransactionFactory::default();
2790 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2791
2792 let tx = MockTransaction::eip1559();
2794 pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0).unwrap();
2795
2796 let tx1 = tx.inc_price().next();
2798
2799 let tx1_validated = f.validated(tx1.clone());
2801 pool.add_transaction(tx1_validated, U256::from(1_000), 0).unwrap();
2802
2803 assert_eq!(
2805 pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
2806 Some(1)
2807 );
2808
2809 let highest_tx = pool
2811 .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
2812 .expect("Failed to retrieve highest transaction");
2813
2814 assert_eq!(highest_tx.as_ref().transaction, tx1);
2816 }
2817
2818 #[test]
2819 fn get_highest_consecutive_transaction_by_sender() {
2820 let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
2822 let mut f = MockTransactionFactory::default();
2823
2824 let sender = Address::random();
2826 let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
2827 for nonce in txs {
2828 let mut mock_tx = MockTransaction::eip1559();
2829 mock_tx.set_sender(sender);
2830 mock_tx.set_nonce(nonce);
2831
2832 let validated_tx = f.validated(mock_tx);
2833 pool.add_transaction(validated_tx, U256::from(1000), 0).unwrap();
2834 }
2835
2836 let sender_id = f.ids.sender_id(&sender).unwrap();
2838 let next_tx =
2839 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
2840 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
2841
2842 let next_tx =
2843 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
2844 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
2845
2846 let next_tx =
2847 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
2848 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
2849
2850 let mut info = SenderInfo::default();
2852 info.update(8, U256::ZERO);
2853 pool.sender_info.insert(sender_id, info);
2854 let next_tx =
2855 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
2856 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
2857 }
2858
2859 #[test]
2860 fn discard_nonce_too_low() {
2861 let mut f = MockTransactionFactory::default();
2862 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2863
2864 let tx = MockTransaction::eip1559().inc_price_by(10);
2865 let validated = f.validated(tx.clone());
2866 let id = *validated.id();
2867 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
2868
2869 let next = tx.next();
2870 let validated = f.validated(next.clone());
2871 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
2872
2873 assert_eq!(pool.pending_pool.len(), 2);
2874
2875 let mut changed_senders = HashMap::default();
2876 changed_senders.insert(
2877 id.sender,
2878 SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
2879 );
2880 let outcome = pool.update_accounts(changed_senders);
2881 assert_eq!(outcome.discarded.len(), 1);
2882 assert_eq!(pool.pending_pool.len(), 1);
2883 }
2884
2885 #[test]
2886 fn discard_with_large_blob_txs() {
2887 reth_tracing::init_test_tracing();
2889
2890 let mut f = MockTransactionFactory::default();
2892 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2893 let default_limits = pool.config.blob_limit;
2894
2895 let a_sender = address!("0x000000000000000000000000000000000000000a");
2898
2899 let mut block_info = pool.block_info();
2901 block_info.pending_blob_fee = Some(100);
2902 block_info.pending_basefee = 100;
2903
2904 pool.set_block_info(block_info);
2906
2907 let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
2909 .into_iter()
2910 .map(|mut tx| {
2911 tx.set_size(default_limits.max_size / 2 + 1);
2912 tx.set_max_fee((block_info.pending_basefee - 1).into());
2913 tx
2914 })
2915 .collect::<Vec<_>>();
2916
2917 for tx in a_txs {
2919 pool.add_transaction(f.validated(tx), U256::from(1_000), 0).unwrap();
2920 }
2921
2922 let removed = pool.discard_worst();
2924 assert_eq!(removed.len(), 1);
2925 }
2926
2927 #[test]
2928 fn discard_with_parked_large_txs() {
2929 reth_tracing::init_test_tracing();
2931
2932 let mut f = MockTransactionFactory::default();
2934 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2935 let default_limits = pool.config.queued_limit;
2936
2937 let a_sender = address!("0x000000000000000000000000000000000000000a");
2940
2941 let pool_base_fee = 100;
2943 pool.update_basefee(pool_base_fee);
2944
2945 let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
2947 .into_iter()
2948 .map(|mut tx| {
2949 tx.set_size(default_limits.max_size / 2 + 1);
2950 tx.set_max_fee((pool_base_fee - 1).into());
2951 tx
2952 })
2953 .collect::<Vec<_>>();
2954
2955 for tx in a_txs {
2957 pool.add_transaction(f.validated(tx), U256::from(1_000), 0).unwrap();
2958 }
2959
2960 let removed = pool.discard_worst();
2962 assert_eq!(removed.len(), 1);
2963 }
2964
2965 #[test]
2966 fn discard_at_capacity() {
2967 let mut f = MockTransactionFactory::default();
2968 let queued_limit = SubPoolLimit::new(1000, usize::MAX);
2969 let mut pool =
2970 TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
2971
2972 for _ in 0..queued_limit.max_txs {
2974 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
2975 let validated = f.validated(tx.clone());
2976 let _id = *validated.id();
2977 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
2978 }
2979
2980 let size = pool.size();
2981 assert_eq!(size.queued, queued_limit.max_txs);
2982
2983 for _ in 0..queued_limit.max_txs {
2984 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
2985 let validated = f.validated(tx.clone());
2986 let _id = *validated.id();
2987 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
2988
2989 pool.discard_worst();
2990 pool.assert_invariants();
2991 assert!(pool.size().queued <= queued_limit.max_txs);
2992 }
2993 }
2994
2995 #[test]
2996 fn discard_blobs_at_capacity() {
2997 let mut f = MockTransactionFactory::default();
2998 let blob_limit = SubPoolLimit::new(1000, usize::MAX);
2999 let mut pool =
3000 TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
3001 pool.all_transactions.pending_fees.blob_fee = 10000;
3002 for _ in 0..blob_limit.max_txs {
3004 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3005 let validated = f.validated(tx.clone());
3006 let _id = *validated.id();
3007 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
3008 }
3009
3010 let size = pool.size();
3011 assert_eq!(size.blob, blob_limit.max_txs);
3012
3013 for _ in 0..blob_limit.max_txs {
3014 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3015 let validated = f.validated(tx.clone());
3016 let _id = *validated.id();
3017 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
3018
3019 pool.discard_worst();
3020 pool.assert_invariants();
3021 assert!(pool.size().blob <= blob_limit.max_txs);
3022 }
3023 }
3024
3025 #[test]
3026 fn account_updates_sender_balance() {
3027 let mut on_chain_balance = U256::from(100);
3028 let on_chain_nonce = 0;
3029 let mut f = MockTransactionFactory::default();
3030 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3031
3032 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3033 let tx_1 = tx_0.next();
3034 let tx_2 = tx_1.next();
3035
3036 let v0 = f.validated(tx_0);
3038 let v1 = f.validated(tx_1);
3039 let v2 = f.validated(tx_2);
3040
3041 let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap();
3042 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce).unwrap();
3043 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce).unwrap();
3044
3045 assert_eq!(1, pool.pending_transactions().len());
3047 assert_eq!(2, pool.queued_transactions().len());
3048
3049 let mut updated_accounts = HashMap::default();
3051 on_chain_balance = U256::from(300);
3052 updated_accounts.insert(
3053 v0.sender_id(),
3054 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3055 );
3056 pool.update_accounts(updated_accounts.clone());
3057
3058 assert_eq!(3, pool.pending_transactions().len());
3059 assert!(pool.queued_transactions().is_empty());
3060
3061 updated_accounts.entry(v0.sender_id()).and_modify(|v| v.balance = U256::from(1));
3063 pool.update_accounts(updated_accounts);
3064
3065 assert!(pool.pending_transactions().is_empty());
3066 assert_eq!(3, pool.queued_transactions().len());
3067 }
3068
3069 #[test]
3070 fn account_updates_nonce_gap() {
3071 let on_chain_balance = U256::from(10_000);
3072 let mut on_chain_nonce = 0;
3073 let mut f = MockTransactionFactory::default();
3074 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3075
3076 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3077 let tx_1 = tx_0.next();
3078 let tx_2 = tx_1.next();
3079
3080 let v0 = f.validated(tx_0);
3082 let v1 = f.validated(tx_1);
3083 let v2 = f.validated(tx_2);
3084
3085 let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap();
3087 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce).unwrap();
3088
3089 assert!(pool.queued_transactions().is_empty());
3090 assert_eq!(2, pool.pending_transactions().len());
3091
3092 pool.remove_transaction_by_hash(v0.hash());
3094
3095 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce).unwrap();
3097
3098 assert_eq!(2, pool.queued_transactions().len());
3100 assert!(pool.pending_transactions().is_empty());
3101
3102 let mut updated_accounts = HashMap::default();
3104 on_chain_nonce += 1;
3105 updated_accounts.insert(
3106 v0.sender_id(),
3107 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3108 );
3109 pool.update_accounts(updated_accounts);
3110
3111 assert!(pool.queued_transactions().is_empty());
3113 assert_eq!(2, pool.pending_transactions().len());
3114 }
3115 #[test]
3116 fn test_transaction_removal() {
3117 let on_chain_balance = U256::from(10_000);
3118 let on_chain_nonce = 0;
3119 let mut f = MockTransactionFactory::default();
3120 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3121
3122 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3123 let tx_1 = tx_0.next();
3124
3125 let v0 = f.validated(tx_0);
3127 let v1 = f.validated(tx_1);
3128
3129 let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap();
3131 let _res = pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce).unwrap();
3132
3133 assert_eq!(0, pool.queued_transactions().len());
3134 assert_eq!(2, pool.pending_transactions().len());
3135
3136 pool.remove_transaction(v0.id());
3138 let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
3140 assert_eq!(vec![v1.nonce()], pool_txs);
3141 }
3142 #[test]
3143 fn test_remove_transactions() {
3144 let on_chain_balance = U256::from(10_000);
3145 let on_chain_nonce = 0;
3146 let mut f = MockTransactionFactory::default();
3147 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3148
3149 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3150 let tx_1 = tx_0.next();
3151 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3152 let tx_3 = tx_2.next();
3153
3154 let v0 = f.validated(tx_0);
3156 let v1 = f.validated(tx_1);
3157 let v2 = f.validated(tx_2);
3158 let v3 = f.validated(tx_3);
3159
3160 let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap();
3162 let _res = pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce).unwrap();
3163 let _res = pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce).unwrap();
3164 let _res = pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce).unwrap();
3165
3166 assert_eq!(0, pool.queued_transactions().len());
3167 assert_eq!(4, pool.pending_transactions().len());
3168
3169 pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
3170
3171 assert_eq!(2, pool.queued_transactions().len());
3172 assert!(pool.pending_transactions().is_empty());
3173 assert!(pool.contains(v1.hash()));
3174 assert!(pool.contains(v3.hash()));
3175 }
3176
3177 #[test]
3178 fn test_remove_transactions_middle_pending_hash() {
3179 let on_chain_balance = U256::from(10_000);
3180 let on_chain_nonce = 0;
3181 let mut f = MockTransactionFactory::default();
3182 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3183
3184 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3185 let tx_1 = tx_0.next();
3186 let tx_2 = tx_1.next();
3187 let tx_3 = tx_2.next();
3188
3189 let v0 = f.validated(tx_0);
3191 let v1 = f.validated(tx_1);
3192 let v2 = f.validated(tx_2);
3193 let v3 = f.validated(tx_3);
3194
3195 let _res = pool.add_transaction(v0, on_chain_balance, on_chain_nonce).unwrap();
3197 let _res = pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce).unwrap();
3198 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce).unwrap();
3199 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce).unwrap();
3200
3201 assert_eq!(0, pool.queued_transactions().len());
3202 assert_eq!(4, pool.pending_transactions().len());
3203
3204 let mut removed_txs = pool.remove_transactions(vec![*v1.hash()]);
3205 assert_eq!(1, removed_txs.len());
3206
3207 assert_eq!(2, pool.queued_transactions().len());
3208 assert_eq!(1, pool.pending_transactions().len());
3209
3210 let removed_tx = removed_txs.pop().unwrap();
3212 let v1 = f.validated(removed_tx.transaction.clone());
3213 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce).unwrap();
3214 assert_eq!(0, pool.queued_transactions().len());
3215 assert_eq!(4, pool.pending_transactions().len());
3216 }
3217
3218 #[test]
3219 fn test_remove_transactions_and_descendants() {
3220 let on_chain_balance = U256::from(10_000);
3221 let on_chain_nonce = 0;
3222 let mut f = MockTransactionFactory::default();
3223 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3224
3225 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3226 let tx_1 = tx_0.next();
3227 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3228 let tx_3 = tx_2.next();
3229 let tx_4 = tx_3.next();
3230
3231 let v0 = f.validated(tx_0);
3233 let v1 = f.validated(tx_1);
3234 let v2 = f.validated(tx_2);
3235 let v3 = f.validated(tx_3);
3236 let v4 = f.validated(tx_4);
3237
3238 let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap();
3240 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce).unwrap();
3241 let _res = pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce).unwrap();
3242 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce).unwrap();
3243 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce).unwrap();
3244
3245 assert_eq!(0, pool.queued_transactions().len());
3246 assert_eq!(5, pool.pending_transactions().len());
3247
3248 pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
3249
3250 assert_eq!(0, pool.queued_transactions().len());
3251 assert_eq!(0, pool.pending_transactions().len());
3252 }
3253 #[test]
3254 fn test_remove_descendants() {
3255 let on_chain_balance = U256::from(10_000);
3256 let on_chain_nonce = 0;
3257 let mut f = MockTransactionFactory::default();
3258 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3259
3260 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3261 let tx_1 = tx_0.next();
3262 let tx_2 = tx_1.next();
3263 let tx_3 = tx_2.next();
3264
3265 let v0 = f.validated(tx_0);
3267 let v1 = f.validated(tx_1);
3268 let v2 = f.validated(tx_2);
3269 let v3 = f.validated(tx_3);
3270
3271 let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap();
3273 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce).unwrap();
3274 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce).unwrap();
3275 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce).unwrap();
3276
3277 assert_eq!(0, pool.queued_transactions().len());
3278 assert_eq!(4, pool.pending_transactions().len());
3279
3280 let mut removed = Vec::new();
3281 pool.remove_transaction(v0.id());
3282 pool.remove_descendants(v0.id(), &mut removed);
3283
3284 assert_eq!(0, pool.queued_transactions().len());
3285 assert_eq!(0, pool.pending_transactions().len());
3286 assert_eq!(3, removed.len());
3287 }
3288 #[test]
3289 fn test_remove_transactions_by_sender() {
3290 let on_chain_balance = U256::from(10_000);
3291 let on_chain_nonce = 0;
3292 let mut f = MockTransactionFactory::default();
3293 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3294
3295 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3296 let tx_1 = tx_0.next();
3297 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3298 let tx_3 = tx_2.next();
3299 let tx_4 = tx_3.next();
3300
3301 let v0 = f.validated(tx_0);
3303 let v1 = f.validated(tx_1);
3304 let v2 = f.validated(tx_2);
3305 let v3 = f.validated(tx_3);
3306 let v4 = f.validated(tx_4);
3307
3308 let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap();
3310 let _res = pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce).unwrap();
3311 let _res = pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce).unwrap();
3312 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce).unwrap();
3313 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce).unwrap();
3314
3315 assert_eq!(0, pool.queued_transactions().len());
3316 assert_eq!(5, pool.pending_transactions().len());
3317
3318 pool.remove_transactions_by_sender(v2.sender_id());
3319
3320 assert_eq!(0, pool.queued_transactions().len());
3321 assert_eq!(2, pool.pending_transactions().len());
3322 assert!(pool.contains(v0.hash()));
3323 assert!(pool.contains(v1.hash()));
3324 }
3325 #[test]
3326 fn wrong_best_order_of_transactions() {
3327 let on_chain_balance = U256::from(10_000);
3328 let mut on_chain_nonce = 0;
3329 let mut f = MockTransactionFactory::default();
3330 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3331
3332 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3333 let tx_1 = tx_0.next();
3334 let tx_2 = tx_1.next();
3335 let tx_3 = tx_2.next();
3336
3337 let v0 = f.validated(tx_0);
3339 let v1 = f.validated(tx_1);
3340 let v2 = f.validated(tx_2);
3341 let v3 = f.validated(tx_3);
3342
3343 let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap();
3345 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce).unwrap();
3346
3347 assert_eq!(0, pool.queued_transactions().len());
3348 assert_eq!(2, pool.pending_transactions().len());
3349
3350 pool.remove_transaction(v0.id());
3352
3353 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce).unwrap();
3355
3356 assert_eq!(1, pool.queued_transactions().len());
3358 assert_eq!(1, pool.pending_transactions().len());
3359
3360 let mut updated_accounts = HashMap::default();
3362 on_chain_nonce += 1;
3363 updated_accounts.insert(
3364 v0.sender_id(),
3365 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3366 );
3367 pool.update_accounts(updated_accounts);
3368
3369 assert_eq!(0, pool.queued_transactions().len());
3372 assert_eq!(2, pool.pending_transactions().len());
3373
3374 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce).unwrap();
3376 assert_eq!(0, pool.queued_transactions().len());
3377 assert_eq!(3, pool.pending_transactions().len());
3378
3379 assert_eq!(
3382 pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
3383 vec![1, 2, 3]
3384 );
3385 }
3386
3387 #[test]
3388 fn test_pending_ordering() {
3389 let mut f = MockTransactionFactory::default();
3390 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3391
3392 let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
3393 let tx_1 = tx_0.next();
3394
3395 let v0 = f.validated(tx_0);
3396 let v1 = f.validated(tx_1);
3397
3398 pool.add_transaction(v0.clone(), U256::MAX, 0).unwrap();
3400 assert_eq!(1, pool.queued_transactions().len());
3401
3402 pool.add_transaction(v1, U256::MAX, 1).unwrap();
3404
3405 assert_eq!(2, pool.pending_transactions().len());
3406 assert_eq!(0, pool.queued_transactions().len());
3407
3408 assert_eq!(
3409 pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
3410 v0.nonce()
3411 );
3412 }
3413
3414 #[test]
3416 fn one_sender_one_independent_transaction() {
3417 let mut on_chain_balance = U256::from(4_999); let mut on_chain_nonce = 40;
3419 let mut f = MockTransactionFactory::default();
3420 let mut pool = TxPool::mock();
3421 let mut submitted_txs = Vec::new();
3422
3423 let template =
3425 MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
3426
3427 for tx_nonce in 40..48 {
3430 let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
3431 submitted_txs.push(*tx.id());
3432 pool.add_transaction(tx, on_chain_balance, on_chain_nonce).unwrap();
3433 }
3434
3435 on_chain_balance = U256::from(999_999);
3438 on_chain_nonce = 42;
3439 pool.remove_transaction(&submitted_txs[0]);
3440 pool.remove_transaction(&submitted_txs[1]);
3441
3442 for tx_nonce in 48..52 {
3444 pool.add_transaction(
3445 f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
3446 on_chain_balance,
3447 on_chain_nonce,
3448 )
3449 .unwrap();
3450 }
3451
3452 let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
3453 assert_eq!(best_txs.len(), 10); assert_eq!(pool.pending_pool.independent().len(), 1);
3456 }
3457}