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},
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, MIN_PROTOCOL_BASE_FEE},
27 eip4844::BLOB_TX_MIN_BLOB_GASPRICE,
28};
29use alloy_primitives::{Address, TxHash, B256};
30use rustc_hash::FxHashMap;
31use smallvec::SmallVec;
32use std::{
33 cmp::Ordering,
34 collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet},
35 fmt,
36 ops::Bound::{Excluded, Unbounded},
37 sync::Arc,
38};
39use tracing::trace;
40
41#[cfg_attr(doc, aquamarine::aquamarine)]
42pub struct TxPool<T: TransactionOrdering> {
84 sender_info: FxHashMap<SenderId, SenderInfo>,
86 pending_pool: PendingPool<T>,
90 config: PoolConfig,
92 queued_pool: ParkedPool<QueuedOrd<T::Transaction>>,
99 basefee_pool: ParkedPool<BasefeeOrd<T::Transaction>>,
104 blob_pool: BlobTransactions<T::Transaction>,
111 all_transactions: AllTransactions<T::Transaction>,
113 metrics: TxPoolMetrics,
115 latest_update_kind: Option<PoolUpdateKind>,
117}
118
119impl<T: TransactionOrdering> TxPool<T> {
122 pub fn new(ordering: T, config: PoolConfig) -> Self {
124 Self {
125 sender_info: Default::default(),
126 pending_pool: PendingPool::new(ordering),
127 queued_pool: Default::default(),
128 basefee_pool: Default::default(),
129 blob_pool: Default::default(),
130 all_transactions: AllTransactions::new(&config),
131 config,
132 metrics: Default::default(),
133 latest_update_kind: None,
134 }
135 }
136
137 pub fn get_highest_nonce_by_sender(&self, sender: SenderId) -> Option<u64> {
139 self.all().txs_iter(sender).last().map(|(_, tx)| tx.transaction.nonce())
140 }
141
142 pub fn get_highest_transaction_by_sender(
145 &self,
146 sender: SenderId,
147 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
148 self.all().txs_iter(sender).last().map(|(_, tx)| Arc::clone(&tx.transaction))
149 }
150
151 pub(crate) fn get_highest_consecutive_transaction_by_sender(
158 &self,
159 mut on_chain: TransactionId,
160 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
161 let mut last_consecutive_tx = None;
162
163 if let Some(current) = self.sender_info.get(&on_chain.sender) {
165 on_chain.nonce = on_chain.nonce.max(current.state_nonce);
166 }
167
168 let mut next_expected_nonce = on_chain.nonce;
169 for (id, tx) in self.all().descendant_txs_inclusive(&on_chain) {
170 if next_expected_nonce != id.nonce {
171 break
172 }
173 next_expected_nonce = id.next_nonce();
174 last_consecutive_tx = Some(tx);
175 }
176
177 last_consecutive_tx.map(|tx| Arc::clone(&tx.transaction))
178 }
179
180 pub(crate) const fn all(&self) -> &AllTransactions<T::Transaction> {
182 &self.all_transactions
183 }
184
185 pub(crate) fn unique_senders(&self) -> HashSet<Address> {
187 self.all_transactions.txs.values().map(|tx| tx.transaction.sender()).collect()
188 }
189
190 pub fn size(&self) -> PoolSize {
192 PoolSize {
193 pending: self.pending_pool.len(),
194 pending_size: self.pending_pool.size(),
195 basefee: self.basefee_pool.len(),
196 basefee_size: self.basefee_pool.size(),
197 queued: self.queued_pool.len(),
198 queued_size: self.queued_pool.size(),
199 blob: self.blob_pool.len(),
200 blob_size: self.blob_pool.size(),
201 total: self.all_transactions.len(),
202 }
203 }
204
205 pub const fn block_info(&self) -> BlockInfo {
207 BlockInfo {
208 block_gas_limit: self.all_transactions.block_gas_limit,
209 last_seen_block_hash: self.all_transactions.last_seen_block_hash,
210 last_seen_block_number: self.all_transactions.last_seen_block_number,
211 pending_basefee: self.all_transactions.pending_fees.base_fee,
212 pending_blob_fee: Some(self.all_transactions.pending_fees.blob_fee),
213 }
214 }
215
216 fn update_blob_fee(&mut self, mut pending_blob_fee: u128, base_fee_update: Ordering) {
218 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut pending_blob_fee);
219 match (self.all_transactions.pending_fees.blob_fee.cmp(&pending_blob_fee), base_fee_update)
220 {
221 (Ordering::Equal, Ordering::Equal | Ordering::Greater) => {
222 }
224 (Ordering::Greater, Ordering::Equal | Ordering::Greater) => {
225 let removed =
227 self.pending_pool.update_blob_fee(self.all_transactions.pending_fees.blob_fee);
228 for tx in removed {
229 let to = {
230 let tx =
231 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
232
233 tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
235 tx.subpool = tx.state.into();
236 tx.subpool
237 };
238 self.add_transaction_to_subpool(to, tx);
239 }
240 }
241 (Ordering::Less, _) | (_, Ordering::Less) => {
242 let removed =
244 self.blob_pool.enforce_pending_fees(&self.all_transactions.pending_fees);
245 for tx in removed {
246 let to = {
247 let tx =
248 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
249 tx.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
250 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
251 tx.subpool = tx.state.into();
252 tx.subpool
253 };
254 self.add_transaction_to_subpool(to, tx);
255 }
256 }
257 }
258 }
259
260 fn update_basefee(&mut self, mut pending_basefee: u64) -> Ordering {
265 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut pending_basefee);
266 match self.all_transactions.pending_fees.base_fee.cmp(&pending_basefee) {
267 Ordering::Equal => {
268 Ordering::Equal
270 }
271 Ordering::Greater => {
272 let removed =
274 self.pending_pool.update_base_fee(self.all_transactions.pending_fees.base_fee);
275 for tx in removed {
276 let to = {
277 let tx =
278 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
279 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
280 tx.subpool = tx.state.into();
281 tx.subpool
282 };
283 self.add_transaction_to_subpool(to, tx);
284 }
285
286 Ordering::Greater
287 }
288 Ordering::Less => {
289 let removed =
291 self.basefee_pool.enforce_basefee(self.all_transactions.pending_fees.base_fee);
292 for tx in removed {
293 let to = {
294 let tx =
295 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
296 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
297 tx.subpool = tx.state.into();
298 tx.subpool
299 };
300 self.add_transaction_to_subpool(to, tx);
301 }
302
303 Ordering::Less
304 }
305 }
306 }
307
308 pub fn set_block_info(&mut self, info: BlockInfo) {
312 let BlockInfo {
313 block_gas_limit,
314 last_seen_block_hash,
315 last_seen_block_number,
316 pending_basefee,
317 pending_blob_fee,
318 } = info;
319 self.all_transactions.last_seen_block_hash = last_seen_block_hash;
320 self.all_transactions.last_seen_block_number = last_seen_block_number;
321 let basefee_ordering = self.update_basefee(pending_basefee);
322
323 self.all_transactions.block_gas_limit = block_gas_limit;
324
325 if let Some(blob_fee) = pending_blob_fee {
326 self.update_blob_fee(blob_fee, basefee_ordering)
327 }
328 }
329
330 pub(crate) fn best_transactions(&self) -> BestTransactions<T> {
333 self.pending_pool.best()
334 }
335
336 pub(crate) fn best_transactions_with_attributes(
343 &self,
344 best_transactions_attributes: BestTransactionsAttributes,
345 ) -> Box<dyn crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T::Transaction>>>>
346 {
347 match best_transactions_attributes.basefee.cmp(&self.all_transactions.pending_fees.base_fee)
350 {
351 Ordering::Equal => {
352 if best_transactions_attributes
356 .blob_fee
357 .is_some_and(|fee| fee < self.all_transactions.pending_fees.blob_fee as u64)
358 {
359 let unlocked_by_blob_fee =
360 self.blob_pool.satisfy_attributes(best_transactions_attributes);
361
362 Box::new(self.pending_pool.best_with_unlocked(
363 unlocked_by_blob_fee,
364 self.all_transactions.pending_fees.base_fee,
365 ))
366 } else {
367 Box::new(self.pending_pool.best())
368 }
369 }
370 Ordering::Greater => {
371 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
373 best_transactions_attributes.basefee,
374 best_transactions_attributes.blob_fee.unwrap_or_default(),
375 ))
376 }
377 Ordering::Less => {
378 let mut unlocked = self
381 .basefee_pool
382 .satisfy_base_fee_transactions(best_transactions_attributes.basefee);
383
384 unlocked.extend(self.blob_pool.satisfy_attributes(best_transactions_attributes));
386
387 Box::new(
388 self.pending_pool
389 .best_with_unlocked(unlocked, self.all_transactions.pending_fees.base_fee),
390 )
391 }
392 }
393 }
394
395 pub(crate) fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
397 self.pending_pool.all().collect()
398 }
399 pub(crate) fn pending_transactions_iter(
401 &self,
402 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
403 self.pending_pool.all()
404 }
405
406 pub(crate) fn pending_transactions_with_predicate(
408 &self,
409 mut predicate: impl FnMut(&ValidPoolTransaction<T::Transaction>) -> bool,
410 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
411 self.pending_transactions_iter().filter(|tx| predicate(tx)).collect()
412 }
413
414 pub(crate) fn pending_txs_by_sender(
416 &self,
417 sender: SenderId,
418 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
419 self.pending_transactions_iter().filter(|tx| tx.sender_id() == sender).collect()
420 }
421
422 pub(crate) fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
424 self.basefee_pool.all().chain(self.queued_pool.all()).collect()
425 }
426
427 pub(crate) fn queued_transactions_iter(
429 &self,
430 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
431 self.basefee_pool.all().chain(self.queued_pool.all())
432 }
433
434 pub fn queued_and_pending_txs_by_sender(
436 &self,
437 sender: SenderId,
438 ) -> (SmallVec<[TransactionId; TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER]>, Vec<TransactionId>) {
439 (self.queued_pool.get_txs_by_sender(sender), self.pending_pool.get_txs_by_sender(sender))
440 }
441
442 pub(crate) fn queued_txs_by_sender(
444 &self,
445 sender: SenderId,
446 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
447 self.queued_transactions_iter().filter(|tx| tx.sender_id() == sender).collect()
448 }
449
450 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
452 self.all_transactions.contains(tx_hash)
453 }
454
455 #[cfg(test)]
457 pub(crate) fn subpool_contains(&self, subpool: SubPool, id: &TransactionId) -> bool {
458 match subpool {
459 SubPool::Queued => self.queued_pool.contains(id),
460 SubPool::Pending => self.pending_pool.contains(id),
461 SubPool::BaseFee => self.basefee_pool.contains(id),
462 SubPool::Blob => self.blob_pool.contains(id),
463 }
464 }
465
466 #[inline]
468 pub(crate) fn is_exceeded(&self) -> bool {
469 self.config.is_exceeded(self.size())
470 }
471
472 pub(crate) fn get(
474 &self,
475 tx_hash: &TxHash,
476 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
477 self.all_transactions.by_hash.get(tx_hash).cloned()
478 }
479
480 pub(crate) fn get_all(
482 &self,
483 txs: Vec<TxHash>,
484 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
485 txs.into_iter().filter_map(|tx| self.get(&tx))
486 }
487
488 pub(crate) fn get_transactions_by_sender(
490 &self,
491 sender: SenderId,
492 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
493 self.all_transactions.txs_iter(sender).map(|(_, tx)| Arc::clone(&tx.transaction)).collect()
494 }
495
496 pub(crate) fn update_accounts(
498 &mut self,
499 changed_senders: FxHashMap<SenderId, SenderInfo>,
500 ) -> UpdateOutcome<T::Transaction> {
501 let updates = self.all_transactions.update(&changed_senders);
503
504 self.sender_info.extend(changed_senders);
506
507 let update = self.process_updates(updates);
509 self.update_size_metrics();
511 update
512 }
513
514 pub(crate) fn on_canonical_state_change(
519 &mut self,
520 block_info: BlockInfo,
521 mined_transactions: Vec<TxHash>,
522 changed_senders: FxHashMap<SenderId, SenderInfo>,
523 update_kind: PoolUpdateKind,
524 ) -> OnNewCanonicalStateOutcome<T::Transaction> {
525 let block_hash = block_info.last_seen_block_hash;
527 self.all_transactions.set_block_info(block_info);
528
529 let mut removed_txs_count = 0;
531 for tx_hash in &mined_transactions {
532 if self.prune_transaction_by_hash(tx_hash).is_some() {
533 removed_txs_count += 1;
534 }
535 }
536
537 self.metrics.removed_transactions.increment(removed_txs_count);
539
540 let UpdateOutcome { promoted, discarded } = self.update_accounts(changed_senders);
541
542 self.update_transaction_type_metrics();
543 self.metrics.performed_state_updates.increment(1);
544
545 self.latest_update_kind = Some(update_kind);
547
548 OnNewCanonicalStateOutcome { block_hash, mined: mined_transactions, promoted, discarded }
549 }
550
551 pub(crate) fn update_size_metrics(&self) {
553 let stats = self.size();
554 self.metrics.pending_pool_transactions.set(stats.pending as f64);
555 self.metrics.pending_pool_size_bytes.set(stats.pending_size as f64);
556 self.metrics.basefee_pool_transactions.set(stats.basefee as f64);
557 self.metrics.basefee_pool_size_bytes.set(stats.basefee_size as f64);
558 self.metrics.queued_pool_transactions.set(stats.queued as f64);
559 self.metrics.queued_pool_size_bytes.set(stats.queued_size as f64);
560 self.metrics.blob_pool_transactions.set(stats.blob as f64);
561 self.metrics.blob_pool_size_bytes.set(stats.blob_size as f64);
562 self.metrics.total_transactions.set(stats.total as f64);
563 }
564
565 pub(crate) fn update_transaction_type_metrics(&self) {
567 let mut legacy_count = 0;
568 let mut eip2930_count = 0;
569 let mut eip1559_count = 0;
570 let mut eip4844_count = 0;
571 let mut eip7702_count = 0;
572
573 for tx in self.all_transactions.transactions_iter() {
574 match tx.transaction.tx_type() {
575 LEGACY_TX_TYPE_ID => legacy_count += 1,
576 EIP2930_TX_TYPE_ID => eip2930_count += 1,
577 EIP1559_TX_TYPE_ID => eip1559_count += 1,
578 EIP4844_TX_TYPE_ID => eip4844_count += 1,
579 EIP7702_TX_TYPE_ID => eip7702_count += 1,
580 _ => {} }
582 }
583
584 self.metrics.total_legacy_transactions.set(legacy_count as f64);
585 self.metrics.total_eip2930_transactions.set(eip2930_count as f64);
586 self.metrics.total_eip1559_transactions.set(eip1559_count as f64);
587 self.metrics.total_eip4844_transactions.set(eip4844_count as f64);
588 self.metrics.total_eip7702_transactions.set(eip7702_count as f64);
589 }
590
591 pub(crate) fn add_transaction(
617 &mut self,
618 tx: ValidPoolTransaction<T::Transaction>,
619 on_chain_balance: U256,
620 on_chain_nonce: u64,
621 ) -> PoolResult<AddedTransaction<T::Transaction>> {
622 if self.contains(tx.hash()) {
623 return Err(PoolError::new(*tx.hash(), PoolErrorKind::AlreadyImported))
624 }
625
626 self.sender_info
628 .entry(tx.sender_id())
629 .or_default()
630 .update(on_chain_nonce, on_chain_balance);
631
632 match self.all_transactions.insert_tx(tx, on_chain_balance, on_chain_nonce) {
633 Ok(InsertOk { transaction, move_to, replaced_tx, updates, .. }) => {
634 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
636 self.metrics.inserted_transactions.increment(1);
638 let UpdateOutcome { promoted, discarded } = self.process_updates(updates);
639
640 let replaced = replaced_tx.map(|(tx, _)| tx);
641
642 let res = if move_to.is_pending() {
644 AddedTransaction::Pending(AddedPendingTransaction {
645 transaction,
646 promoted,
647 discarded,
648 replaced,
649 })
650 } else {
651 AddedTransaction::Parked { transaction, subpool: move_to, replaced }
652 };
653
654 self.update_size_metrics();
656
657 Ok(res)
658 }
659 Err(err) => {
660 self.metrics.invalid_transactions.increment(1);
662 match err {
663 InsertErr::Underpriced { existing: _, transaction } => Err(PoolError::new(
664 *transaction.hash(),
665 PoolErrorKind::ReplacementUnderpriced,
666 )),
667 InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap } => {
668 Err(PoolError::new(
669 *transaction.hash(),
670 PoolErrorKind::FeeCapBelowMinimumProtocolFeeCap(fee_cap),
671 ))
672 }
673 InsertErr::ExceededSenderTransactionsCapacity { transaction } => {
674 Err(PoolError::new(
675 *transaction.hash(),
676 PoolErrorKind::SpammerExceededCapacity(transaction.sender()),
677 ))
678 }
679 InsertErr::TxGasLimitMoreThanAvailableBlockGas {
680 transaction,
681 block_gas_limit,
682 tx_gas_limit,
683 } => Err(PoolError::new(
684 *transaction.hash(),
685 PoolErrorKind::InvalidTransaction(
686 InvalidPoolTransactionError::ExceedsGasLimit(
687 tx_gas_limit,
688 block_gas_limit,
689 ),
690 ),
691 )),
692 InsertErr::BlobTxHasNonceGap { transaction } => Err(PoolError::new(
693 *transaction.hash(),
694 PoolErrorKind::InvalidTransaction(
695 Eip4844PoolTransactionError::Eip4844NonceGap.into(),
696 ),
697 )),
698 InsertErr::Overdraft { transaction } => Err(PoolError::new(
699 *transaction.hash(),
700 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Overdraft {
701 cost: *transaction.cost(),
702 balance: on_chain_balance,
703 }),
704 )),
705 InsertErr::TxTypeConflict { transaction } => Err(PoolError::new(
706 *transaction.hash(),
707 PoolErrorKind::ExistingConflictingTransactionType(
708 transaction.sender(),
709 transaction.tx_type(),
710 ),
711 )),
712 }
713 }
714 }
715 }
716
717 fn process_updates(&mut self, updates: Vec<PoolUpdate>) -> UpdateOutcome<T::Transaction> {
721 let mut outcome = UpdateOutcome::default();
722 for PoolUpdate { id, hash, current, destination } in updates {
723 match destination {
724 Destination::Discard => {
725 if let Some(tx) = self.prune_transaction_by_hash(&hash) {
727 outcome.discarded.push(tx);
728 }
729 self.metrics.removed_transactions.increment(1);
730 }
731 Destination::Pool(move_to) => {
732 debug_assert_ne!(&move_to, ¤t, "destination must be different");
733 let moved = self.move_transaction(current, move_to, &id);
734 if matches!(move_to, SubPool::Pending) {
735 if let Some(tx) = moved {
736 trace!(target: "txpool", hash=%tx.transaction.hash(), "Promoted transaction to pending");
737 outcome.promoted.push(tx);
738 }
739 }
740 }
741 }
742 }
743 outcome
744 }
745
746 fn move_transaction(
751 &mut self,
752 from: SubPool,
753 to: SubPool,
754 id: &TransactionId,
755 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
756 let tx = self.remove_from_subpool(from, id)?;
757 self.add_transaction_to_subpool(to, tx.clone());
758 Some(tx)
759 }
760
761 pub(crate) fn remove_transactions(
766 &mut self,
767 hashes: Vec<TxHash>,
768 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
769 let txs =
770 hashes.into_iter().filter_map(|hash| self.remove_transaction_by_hash(&hash)).collect();
771 self.update_size_metrics();
772 txs
773 }
774
775 pub(crate) fn remove_transactions_and_descendants(
777 &mut self,
778 hashes: Vec<TxHash>,
779 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
780 let mut removed = Vec::new();
781 for hash in hashes {
782 if let Some(tx) = self.remove_transaction_by_hash(&hash) {
783 removed.push(tx.clone());
784 self.remove_descendants(tx.id(), &mut removed);
785 }
786 }
787 self.update_size_metrics();
788 removed
789 }
790
791 pub(crate) fn remove_transactions_by_sender(
793 &mut self,
794 sender_id: SenderId,
795 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
796 let mut removed = Vec::new();
797 let txs = self.get_transactions_by_sender(sender_id);
798 for tx in txs {
799 if let Some(tx) = self.remove_transaction(tx.id()) {
800 removed.push(tx);
801 }
802 }
803 self.update_size_metrics();
804 removed
805 }
806
807 fn remove_transaction(
811 &mut self,
812 id: &TransactionId,
813 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
814 let (tx, pool) = self.all_transactions.remove_transaction(id)?;
815 self.remove_from_subpool(pool, tx.id())
816 }
817
818 fn remove_transaction_by_hash(
822 &mut self,
823 tx_hash: &B256,
824 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
825 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
826 self.remove_from_subpool(pool, tx.id())
827 }
828
829 fn prune_transaction_by_hash(
835 &mut self,
836 tx_hash: &B256,
837 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
838 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
839 self.prune_from_subpool(pool, tx.id())
840 }
841
842 fn remove_from_subpool(
846 &mut self,
847 pool: SubPool,
848 tx: &TransactionId,
849 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
850 let tx = match pool {
851 SubPool::Queued => self.queued_pool.remove_transaction(tx),
852 SubPool::Pending => self.pending_pool.remove_transaction(tx),
853 SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
854 SubPool::Blob => self.blob_pool.remove_transaction(tx),
855 };
856
857 if let Some(ref tx) = tx {
858 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Removed transaction from a subpool");
862 }
863
864 tx
865 }
866
867 fn prune_from_subpool(
870 &mut self,
871 pool: SubPool,
872 tx: &TransactionId,
873 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
874 let tx = match pool {
875 SubPool::Pending => self.pending_pool.remove_transaction(tx),
876 SubPool::Queued => self.queued_pool.remove_transaction(tx),
877 SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
878 SubPool::Blob => self.blob_pool.remove_transaction(tx),
879 };
880
881 if let Some(ref tx) = tx {
882 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Pruned transaction from a subpool");
886 }
887
888 tx
889 }
890
891 fn remove_descendants(
895 &mut self,
896 tx: &TransactionId,
897 removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
898 ) {
899 let mut id = *tx;
900
901 loop {
903 let descendant =
904 self.all_transactions.descendant_txs_exclusive(&id).map(|(id, _)| *id).next();
905 if let Some(descendant) = descendant {
906 if let Some(tx) = self.remove_transaction(&descendant) {
907 removed.push(tx)
908 }
909 id = descendant;
910 } else {
911 return
912 }
913 }
914 }
915
916 fn add_transaction_to_subpool(
918 &mut self,
919 pool: SubPool,
920 tx: Arc<ValidPoolTransaction<T::Transaction>>,
921 ) {
922 match pool {
926 SubPool::Queued => self.queued_pool.add_transaction(tx),
927 SubPool::Pending => {
928 self.pending_pool.add_transaction(tx, self.all_transactions.pending_fees.base_fee);
929 }
930 SubPool::BaseFee => self.basefee_pool.add_transaction(tx),
931 SubPool::Blob => self.blob_pool.add_transaction(tx),
932 }
933 }
934
935 fn add_new_transaction(
938 &mut self,
939 transaction: Arc<ValidPoolTransaction<T::Transaction>>,
940 replaced: Option<(Arc<ValidPoolTransaction<T::Transaction>>, SubPool)>,
941 pool: SubPool,
942 ) {
943 if let Some((replaced, replaced_pool)) = replaced {
944 self.remove_from_subpool(replaced_pool, replaced.id());
946 }
947
948 self.add_transaction_to_subpool(pool, transaction)
949 }
950
951 pub(crate) fn discard_worst(&mut self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
958 let mut removed = Vec::new();
959
960 macro_rules! discard_worst {
962 ($this:ident, $removed:ident, [$($limit:ident => $pool:ident),* $(,)*]) => {
963 $ (
964 while $this.$pool.exceeds(&$this.config.$limit)
965 {
966 trace!(
967 target: "txpool",
968 "discarding transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
969 stringify!($pool),
970 $this.config.$limit,
971 $this.$pool.size(),
972 $this.$pool.len(),
973 );
974
975 let removed_from_subpool = $this.$pool.truncate_pool($this.config.$limit.clone());
977
978 trace!(
979 target: "txpool",
980 "removed {} transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
981 removed_from_subpool.len(),
982 stringify!($pool),
983 $this.config.$limit,
984 $this.$pool.size(),
985 $this.$pool.len()
986 );
987
988 for tx in removed_from_subpool {
990 $this.all_transactions.remove_transaction(tx.id());
991
992 let id = *tx.id();
993
994 removed.push(tx);
996
997 $this.remove_descendants(&id, &mut $removed);
999 }
1000 }
1001
1002 )*
1003 };
1004 }
1005
1006 discard_worst!(
1007 self, removed, [
1008 pending_limit => pending_pool,
1009 basefee_limit => basefee_pool,
1010 blob_limit => blob_pool,
1011 queued_limit => queued_pool,
1012 ]
1013 );
1014
1015 removed
1016 }
1017
1018 pub(crate) fn len(&self) -> usize {
1020 self.all_transactions.len()
1021 }
1022
1023 pub(crate) fn is_empty(&self) -> bool {
1025 self.all_transactions.is_empty()
1026 }
1027
1028 #[cfg(any(test, feature = "test-utils"))]
1036 pub fn assert_invariants(&self) {
1037 let size = self.size();
1038 let actual = size.basefee + size.pending + size.queued + size.blob;
1039 assert_eq!(size.total, actual, "total size must be equal to the sum of all sub-pools, basefee:{}, pending:{}, queued:{}, blob:{}", size.basefee, size.pending, size.queued, size.blob);
1040 self.all_transactions.assert_invariants();
1041 self.pending_pool.assert_invariants();
1042 self.basefee_pool.assert_invariants();
1043 self.queued_pool.assert_invariants();
1044 self.blob_pool.assert_invariants();
1045 }
1046}
1047
1048#[cfg(any(test, feature = "test-utils"))]
1049impl TxPool<crate::test_utils::MockOrdering> {
1050 pub fn mock() -> Self {
1052 Self::new(crate::test_utils::MockOrdering::default(), PoolConfig::default())
1053 }
1054}
1055
1056#[cfg(test)]
1057impl<T: TransactionOrdering> Drop for TxPool<T> {
1058 fn drop(&mut self) {
1059 self.assert_invariants();
1060 }
1061}
1062
1063#[cfg(any(test, feature = "test-utils"))]
1065#[allow(dead_code)]
1066impl<T: TransactionOrdering> TxPool<T> {
1067 pub(crate) const fn pending(&self) -> &PendingPool<T> {
1068 &self.pending_pool
1069 }
1070
1071 pub(crate) const fn base_fee(&self) -> &ParkedPool<BasefeeOrd<T::Transaction>> {
1072 &self.basefee_pool
1073 }
1074
1075 pub(crate) const fn queued(&self) -> &ParkedPool<QueuedOrd<T::Transaction>> {
1076 &self.queued_pool
1077 }
1078}
1079
1080impl<T: TransactionOrdering> fmt::Debug for TxPool<T> {
1081 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1082 f.debug_struct("TxPool").field("config", &self.config).finish_non_exhaustive()
1083 }
1084}
1085
1086pub(crate) struct AllTransactions<T: PoolTransaction> {
1091 minimal_protocol_basefee: u64,
1095 block_gas_limit: u64,
1097 max_account_slots: usize,
1099 by_hash: HashMap<TxHash, Arc<ValidPoolTransaction<T>>>,
1101 txs: BTreeMap<TransactionId, PoolInternalTransaction<T>>,
1103 tx_counter: FxHashMap<SenderId, usize>,
1105 last_seen_block_number: u64,
1107 last_seen_block_hash: B256,
1109 pending_fees: PendingFees,
1111 price_bumps: PriceBumpConfig,
1113 local_transactions_config: LocalTransactionConfig,
1115 metrics: AllTransactionsMetrics,
1117}
1118
1119impl<T: PoolTransaction> AllTransactions<T> {
1120 fn new(config: &PoolConfig) -> Self {
1122 Self {
1123 max_account_slots: config.max_account_slots,
1124 price_bumps: config.price_bumps,
1125 local_transactions_config: config.local_transactions_config.clone(),
1126 minimal_protocol_basefee: config.minimal_protocol_basefee,
1127 block_gas_limit: config.gas_limit,
1128 ..Default::default()
1129 }
1130 }
1131
1132 #[allow(dead_code)]
1134 pub(crate) fn hashes_iter(&self) -> impl Iterator<Item = TxHash> + '_ {
1135 self.by_hash.keys().copied()
1136 }
1137
1138 pub(crate) fn transactions_iter(
1140 &self,
1141 ) -> impl Iterator<Item = &Arc<ValidPoolTransaction<T>>> + '_ {
1142 self.by_hash.values()
1143 }
1144
1145 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
1147 self.by_hash.contains_key(tx_hash)
1148 }
1149
1150 pub(crate) fn get(&self, id: &TransactionId) -> Option<&PoolInternalTransaction<T>> {
1152 self.txs.get(id)
1153 }
1154
1155 pub(crate) fn tx_inc(&mut self, sender: SenderId) {
1157 let count = self.tx_counter.entry(sender).or_default();
1158 *count += 1;
1159 self.metrics.all_transactions_by_all_senders.increment(1.0);
1160 }
1161
1162 pub(crate) fn tx_decr(&mut self, sender: SenderId) {
1164 if let hash_map::Entry::Occupied(mut entry) = self.tx_counter.entry(sender) {
1165 let count = entry.get_mut();
1166 if *count == 1 {
1167 entry.remove();
1168 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1169 return
1170 }
1171 *count -= 1;
1172 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1173 }
1174 }
1175
1176 fn set_block_info(&mut self, block_info: BlockInfo) {
1178 let BlockInfo {
1179 block_gas_limit,
1180 last_seen_block_hash,
1181 last_seen_block_number,
1182 pending_basefee,
1183 pending_blob_fee,
1184 } = block_info;
1185 self.last_seen_block_number = last_seen_block_number;
1186 self.last_seen_block_hash = last_seen_block_hash;
1187
1188 self.pending_fees.base_fee = pending_basefee;
1189 self.metrics.base_fee.set(pending_basefee as f64);
1190
1191 self.block_gas_limit = block_gas_limit;
1192
1193 if let Some(pending_blob_fee) = pending_blob_fee {
1194 self.pending_fees.blob_fee = pending_blob_fee;
1195 self.metrics.blob_base_fee.set(pending_blob_fee as f64);
1196 }
1197 }
1198
1199 pub(crate) fn update_size_metrics(&self) {
1201 self.metrics.all_transactions_by_hash.set(self.by_hash.len() as f64);
1202 self.metrics.all_transactions_by_id.set(self.txs.len() as f64);
1203 }
1204
1205 pub(crate) fn update(
1222 &mut self,
1223 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1224 ) -> Vec<PoolUpdate> {
1225 let mut updates = Vec::with_capacity(64);
1227
1228 let mut iter = self.txs.iter_mut().peekable();
1229
1230 'transactions: while let Some((id, tx)) = iter.next() {
1240 macro_rules! next_sender {
1241 ($iter:ident) => {
1242 'this: while let Some((peek, _)) = iter.peek() {
1243 if peek.sender != id.sender {
1244 break 'this
1245 }
1246 iter.next();
1247 }
1248 };
1249 }
1250 let mut changed_balance = None;
1252
1253 if let Some(info) = changed_accounts.get(&id.sender) {
1255 if id.nonce < info.state_nonce {
1257 updates.push(PoolUpdate {
1258 id: *tx.transaction.id(),
1259 hash: *tx.transaction.hash(),
1260 current: tx.subpool,
1261 destination: Destination::Discard,
1262 });
1263 continue 'transactions
1264 }
1265
1266 let ancestor = TransactionId::ancestor(id.nonce, info.state_nonce, id.sender);
1267 if ancestor.is_none() {
1269 tx.state.insert(TxState::NO_NONCE_GAPS);
1270 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1271 tx.cumulative_cost = U256::ZERO;
1272 if tx.transaction.cost() > &info.balance {
1273 tx.state.remove(TxState::ENOUGH_BALANCE);
1275 } else {
1276 tx.state.insert(TxState::ENOUGH_BALANCE);
1277 }
1278 }
1279
1280 changed_balance = Some(&info.balance);
1281 }
1282
1283 if tx.state.has_nonce_gap() {
1285 next_sender!(iter);
1286 continue 'transactions
1287 }
1288
1289 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1291
1292 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1294 Self::record_subpool_update(&mut updates, tx);
1296
1297 let mut has_parked_ancestor = !tx.state.is_pending();
1299
1300 let mut cumulative_cost = tx.next_cumulative_cost();
1301
1302 let mut next_nonce_in_line = tx.transaction.nonce().saturating_add(1);
1304
1305 while let Some((peek, ref mut tx)) = iter.peek_mut() {
1307 if peek.sender != id.sender {
1308 continue 'transactions
1310 }
1311
1312 if tx.transaction.nonce() == next_nonce_in_line {
1313 tx.state.insert(TxState::NO_NONCE_GAPS);
1315 } else {
1316 next_sender!(iter);
1318 continue 'transactions
1319 }
1320
1321 next_nonce_in_line = next_nonce_in_line.saturating_add(1);
1323
1324 tx.cumulative_cost = cumulative_cost;
1326 cumulative_cost = tx.next_cumulative_cost();
1328
1329 if let Some(changed_balance) = changed_balance {
1331 if &cumulative_cost > changed_balance {
1332 tx.state.remove(TxState::ENOUGH_BALANCE);
1334 } else {
1335 tx.state.insert(TxState::ENOUGH_BALANCE);
1336 }
1337 }
1338
1339 if has_parked_ancestor {
1341 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1342 } else {
1343 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1344 }
1345 has_parked_ancestor = !tx.state.is_pending();
1346
1347 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1349 Self::record_subpool_update(&mut updates, tx);
1350
1351 iter.next();
1353 }
1354 }
1355
1356 updates
1357 }
1358
1359 fn record_subpool_update(updates: &mut Vec<PoolUpdate>, tx: &mut PoolInternalTransaction<T>) {
1364 let current_pool = tx.subpool;
1365 tx.subpool = tx.state.into();
1366 if current_pool != tx.subpool {
1367 updates.push(PoolUpdate {
1368 id: *tx.transaction.id(),
1369 hash: *tx.transaction.hash(),
1370 current: current_pool,
1371 destination: tx.subpool.into(),
1372 })
1373 }
1374 }
1375
1376 fn update_tx_base_fee(pending_block_base_fee: u64, tx: &mut PoolInternalTransaction<T>) {
1378 match tx.transaction.max_fee_per_gas().cmp(&(pending_block_base_fee as u128)) {
1380 Ordering::Greater | Ordering::Equal => {
1381 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1382 }
1383 Ordering::Less => {
1384 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
1385 }
1386 }
1387 }
1388
1389 pub(crate) fn txs_iter(
1392 &self,
1393 sender: SenderId,
1394 ) -> impl Iterator<Item = (&TransactionId, &PoolInternalTransaction<T>)> + '_ {
1395 self.txs
1396 .range((sender.start_bound(), Unbounded))
1397 .take_while(move |(other, _)| sender == other.sender)
1398 }
1399
1400 #[cfg(test)]
1403 #[allow(dead_code)]
1404 pub(crate) fn txs_iter_mut(
1405 &mut self,
1406 sender: SenderId,
1407 ) -> impl Iterator<Item = (&TransactionId, &mut PoolInternalTransaction<T>)> + '_ {
1408 self.txs
1409 .range_mut((sender.start_bound(), Unbounded))
1410 .take_while(move |(other, _)| sender == other.sender)
1411 }
1412
1413 pub(crate) fn descendant_txs_exclusive<'a, 'b: 'a>(
1417 &'a self,
1418 id: &'b TransactionId,
1419 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1420 self.txs.range((Excluded(id), Unbounded)).take_while(|(other, _)| id.sender == other.sender)
1421 }
1422
1423 pub(crate) fn descendant_txs_inclusive<'a, 'b: 'a>(
1428 &'a self,
1429 id: &'b TransactionId,
1430 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1431 self.txs.range(id..).take_while(|(other, _)| id.sender == other.sender)
1432 }
1433
1434 pub(crate) fn descendant_txs_mut<'a, 'b: 'a>(
1439 &'a mut self,
1440 id: &'b TransactionId,
1441 ) -> impl Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)> + 'a {
1442 self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender)
1443 }
1444
1445 pub(crate) fn remove_transaction_by_hash(
1447 &mut self,
1448 tx_hash: &B256,
1449 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1450 let tx = self.by_hash.remove(tx_hash)?;
1451 let internal = self.txs.remove(&tx.transaction_id)?;
1452 self.tx_decr(tx.sender_id());
1454 self.update_size_metrics();
1455 Some((tx, internal.subpool))
1456 }
1457
1458 pub(crate) fn remove_transaction(
1464 &mut self,
1465 id: &TransactionId,
1466 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1467 let internal = self.txs.remove(id)?;
1468
1469 self.tx_decr(internal.transaction.sender_id());
1471
1472 let result =
1473 self.by_hash.remove(internal.transaction.hash()).map(|tx| (tx, internal.subpool));
1474
1475 self.update_size_metrics();
1476
1477 result
1478 }
1479
1480 #[inline]
1486 fn contains_conflicting_transaction(&self, tx: &ValidPoolTransaction<T>) -> bool {
1487 self.txs_iter(tx.transaction_id.sender)
1488 .next()
1489 .is_some_and(|(_, existing)| tx.tx_type_conflicts_with(&existing.transaction))
1490 }
1491
1492 fn ensure_valid(
1501 &self,
1502 transaction: ValidPoolTransaction<T>,
1503 on_chain_nonce: u64,
1504 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1505 if !self.local_transactions_config.is_local(transaction.origin, transaction.sender_ref()) {
1506 let current_txs =
1507 self.tx_counter.get(&transaction.sender_id()).copied().unwrap_or_default();
1508
1509 if current_txs >= self.max_account_slots && transaction.nonce() > on_chain_nonce {
1512 return Err(InsertErr::ExceededSenderTransactionsCapacity {
1513 transaction: Arc::new(transaction),
1514 })
1515 }
1516 }
1517 if transaction.gas_limit() > self.block_gas_limit {
1518 return Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas {
1519 block_gas_limit: self.block_gas_limit,
1520 tx_gas_limit: transaction.gas_limit(),
1521 transaction: Arc::new(transaction),
1522 })
1523 }
1524
1525 if self.contains_conflicting_transaction(&transaction) {
1526 return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) })
1528 }
1529
1530 Ok(transaction)
1531 }
1532
1533 fn ensure_valid_blob_transaction(
1539 &self,
1540 new_blob_tx: ValidPoolTransaction<T>,
1541 on_chain_balance: U256,
1542 ancestor: Option<TransactionId>,
1543 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1544 if let Some(ancestor) = ancestor {
1545 let Some(ancestor_tx) = self.txs.get(&ancestor) else {
1546 self.metrics.blob_transactions_nonce_gaps.increment(1);
1548 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1549 };
1550 if ancestor_tx.state.has_nonce_gap() {
1551 self.metrics.blob_transactions_nonce_gaps.increment(1);
1554 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1555 }
1556
1557 let mut cumulative_cost = ancestor_tx.next_cumulative_cost() + new_blob_tx.cost();
1559
1560 if cumulative_cost > on_chain_balance {
1562 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1564 }
1565
1566 let id = new_blob_tx.transaction_id;
1569 let mut descendants = self.descendant_txs_inclusive(&id).peekable();
1570 if let Some((maybe_replacement, _)) = descendants.peek() {
1571 if **maybe_replacement == new_blob_tx.transaction_id {
1572 descendants.next();
1574
1575 for (_, tx) in descendants {
1577 cumulative_cost += tx.transaction.cost();
1578 if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance {
1579 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1581 }
1582 }
1583 }
1584 }
1585 } else if new_blob_tx.cost() > &on_chain_balance {
1586 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1588 }
1589
1590 Ok(new_blob_tx)
1591 }
1592
1593 pub(crate) fn insert_tx(
1625 &mut self,
1626 transaction: ValidPoolTransaction<T>,
1627 on_chain_balance: U256,
1628 on_chain_nonce: u64,
1629 ) -> InsertResult<T> {
1630 assert!(on_chain_nonce <= transaction.nonce(), "Invalid transaction");
1631
1632 let mut transaction = self.ensure_valid(transaction, on_chain_nonce)?;
1633
1634 let inserted_tx_id = *transaction.id();
1635 let mut state = TxState::default();
1636 let mut cumulative_cost = U256::ZERO;
1637 let mut updates = Vec::new();
1638
1639 state.insert(TxState::NOT_TOO_MUCH_GAS);
1641
1642 let ancestor = TransactionId::ancestor(
1645 transaction.transaction.nonce(),
1646 on_chain_nonce,
1647 inserted_tx_id.sender,
1648 );
1649
1650 if transaction.is_eip4844() {
1653 state.insert(TxState::BLOB_TRANSACTION);
1654
1655 transaction =
1656 self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
1657 let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();
1658 if blob_fee_cap >= self.pending_fees.blob_fee {
1659 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1660 }
1661 } else {
1662 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1664 }
1665
1666 let transaction = Arc::new(transaction);
1667
1668 if ancestor.is_none() {
1670 state.insert(TxState::NO_NONCE_GAPS);
1671 state.insert(TxState::NO_PARKED_ANCESTORS);
1672 }
1673
1674 let fee_cap = transaction.max_fee_per_gas();
1676
1677 if fee_cap < self.minimal_protocol_basefee as u128 {
1678 return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap })
1679 }
1680 if fee_cap >= self.pending_fees.base_fee as u128 {
1681 state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1682 }
1683
1684 let mut replaced_tx = None;
1686
1687 let pool_tx = PoolInternalTransaction {
1688 transaction: Arc::clone(&transaction),
1689 subpool: state.into(),
1690 state,
1691 cumulative_cost,
1692 };
1693
1694 match self.txs.entry(*transaction.id()) {
1696 Entry::Vacant(entry) => {
1697 self.by_hash.insert(*pool_tx.transaction.hash(), pool_tx.transaction.clone());
1699 entry.insert(pool_tx);
1700 }
1701 Entry::Occupied(mut entry) => {
1702 let existing_transaction = entry.get().transaction.as_ref();
1704 let maybe_replacement = transaction.as_ref();
1705
1706 if existing_transaction.is_underpriced(maybe_replacement, &self.price_bumps) {
1708 return Err(InsertErr::Underpriced {
1709 transaction: pool_tx.transaction,
1710 existing: *entry.get().transaction.hash(),
1711 })
1712 }
1713 let new_hash = *pool_tx.transaction.hash();
1714 let new_transaction = pool_tx.transaction.clone();
1715 let replaced = entry.insert(pool_tx);
1716 self.by_hash.remove(replaced.transaction.hash());
1717 self.by_hash.insert(new_hash, new_transaction);
1718 replaced_tx = Some((replaced.transaction, replaced.subpool));
1720 }
1721 }
1722
1723 let on_chain_id = TransactionId::new(transaction.sender_id(), on_chain_nonce);
1725 {
1726 let mut next_nonce = on_chain_id.nonce;
1728
1729 let mut has_parked_ancestor = false;
1733
1734 for (id, tx) in self.descendant_txs_mut(&on_chain_id) {
1737 let current_pool = tx.subpool;
1738
1739 if next_nonce != id.nonce {
1741 break
1742 }
1743
1744 tx.state.insert(TxState::NO_NONCE_GAPS);
1746
1747 tx.cumulative_cost = cumulative_cost;
1749
1750 cumulative_cost = tx.next_cumulative_cost();
1752
1753 if cumulative_cost > on_chain_balance {
1754 tx.state.remove(TxState::ENOUGH_BALANCE);
1756 } else {
1757 tx.state.insert(TxState::ENOUGH_BALANCE);
1758 }
1759
1760 if has_parked_ancestor {
1762 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1763 } else {
1764 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1765 }
1766 has_parked_ancestor = !tx.state.is_pending();
1767
1768 tx.subpool = tx.state.into();
1770
1771 if inserted_tx_id.eq(id) {
1772 state = tx.state;
1774 } else {
1775 if current_pool != tx.subpool {
1777 updates.push(PoolUpdate {
1778 id: *id,
1779 hash: *tx.transaction.hash(),
1780 current: current_pool,
1781 destination: tx.subpool.into(),
1782 })
1783 }
1784 }
1785
1786 next_nonce = id.next_nonce();
1788 }
1789 }
1790
1791 if replaced_tx.is_none() {
1793 self.tx_inc(inserted_tx_id.sender);
1794 }
1795
1796 self.update_size_metrics();
1797
1798 let res = Ok(InsertOk { transaction, move_to: state.into(), state, replaced_tx, updates });
1799 res
1800 }
1801
1802 pub(crate) fn len(&self) -> usize {
1804 self.txs.len()
1805 }
1806
1807 pub(crate) fn is_empty(&self) -> bool {
1809 self.txs.is_empty()
1810 }
1811
1812 #[cfg(any(test, feature = "test-utils"))]
1814 pub(crate) fn assert_invariants(&self) {
1815 assert_eq!(self.by_hash.len(), self.txs.len(), "by_hash.len() != txs.len()");
1816 }
1817}
1818
1819#[cfg(test)]
1820impl<T: PoolTransaction> AllTransactions<T> {
1821 pub(crate) fn tx_count(&self, sender: SenderId) -> usize {
1825 self.tx_counter.get(&sender).copied().unwrap_or_default()
1826 }
1827}
1828
1829impl<T: PoolTransaction> Default for AllTransactions<T> {
1830 fn default() -> Self {
1831 Self {
1832 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
1833 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
1834 block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT,
1835 by_hash: Default::default(),
1836 txs: Default::default(),
1837 tx_counter: Default::default(),
1838 last_seen_block_number: Default::default(),
1839 last_seen_block_hash: Default::default(),
1840 pending_fees: Default::default(),
1841 price_bumps: Default::default(),
1842 local_transactions_config: Default::default(),
1843 metrics: Default::default(),
1844 }
1845 }
1846}
1847
1848#[derive(Debug, Clone)]
1850pub(crate) struct PendingFees {
1851 pub(crate) base_fee: u64,
1853 pub(crate) blob_fee: u128,
1855}
1856
1857impl Default for PendingFees {
1858 fn default() -> Self {
1859 Self { base_fee: Default::default(), blob_fee: BLOB_TX_MIN_BLOB_GASPRICE }
1860 }
1861}
1862
1863pub(crate) type InsertResult<T> = Result<InsertOk<T>, InsertErr<T>>;
1865
1866#[derive(Debug)]
1868pub(crate) enum InsertErr<T: PoolTransaction> {
1869 Underpriced {
1871 transaction: Arc<ValidPoolTransaction<T>>,
1872 #[allow(dead_code)]
1873 existing: TxHash,
1874 },
1875 BlobTxHasNonceGap { transaction: Arc<ValidPoolTransaction<T>> },
1877 Overdraft { transaction: Arc<ValidPoolTransaction<T>> },
1880 FeeCapBelowMinimumProtocolFeeCap { transaction: Arc<ValidPoolTransaction<T>>, fee_cap: u128 },
1884 ExceededSenderTransactionsCapacity { transaction: Arc<ValidPoolTransaction<T>> },
1888 TxGasLimitMoreThanAvailableBlockGas {
1890 transaction: Arc<ValidPoolTransaction<T>>,
1891 block_gas_limit: u64,
1892 tx_gas_limit: u64,
1893 },
1894 TxTypeConflict { transaction: Arc<ValidPoolTransaction<T>> },
1896}
1897
1898#[derive(Debug)]
1900pub(crate) struct InsertOk<T: PoolTransaction> {
1901 transaction: Arc<ValidPoolTransaction<T>>,
1903 move_to: SubPool,
1905 #[allow(dead_code)]
1907 state: TxState,
1908 replaced_tx: Option<(Arc<ValidPoolTransaction<T>>, SubPool)>,
1910 updates: Vec<PoolUpdate>,
1912}
1913
1914#[derive(Debug)]
1917pub(crate) struct PoolInternalTransaction<T: PoolTransaction> {
1918 pub(crate) transaction: Arc<ValidPoolTransaction<T>>,
1920 pub(crate) subpool: SubPool,
1922 pub(crate) state: TxState,
1925 pub(crate) cumulative_cost: U256,
1930}
1931
1932impl<T: PoolTransaction> PoolInternalTransaction<T> {
1935 fn next_cumulative_cost(&self) -> U256 {
1936 self.cumulative_cost + self.transaction.cost()
1937 }
1938}
1939
1940#[derive(Debug)]
1942pub(crate) struct UpdateOutcome<T: PoolTransaction> {
1943 pub(crate) promoted: Vec<Arc<ValidPoolTransaction<T>>>,
1945 pub(crate) discarded: Vec<Arc<ValidPoolTransaction<T>>>,
1947}
1948
1949impl<T: PoolTransaction> Default for UpdateOutcome<T> {
1950 fn default() -> Self {
1951 Self { promoted: vec![], discarded: vec![] }
1952 }
1953}
1954
1955#[derive(Debug, Clone, Default)]
1957pub(crate) struct SenderInfo {
1958 pub(crate) state_nonce: u64,
1960 pub(crate) balance: U256,
1962}
1963
1964impl SenderInfo {
1967 fn update(&mut self, state_nonce: u64, balance: U256) {
1969 *self = Self { state_nonce, balance };
1970 }
1971}
1972
1973#[cfg(test)]
1974mod tests {
1975 use super::*;
1976 use crate::{
1977 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
1978 traits::TransactionOrigin,
1979 SubPoolLimit,
1980 };
1981 use alloy_primitives::address;
1982 use reth_primitives::TxType;
1983
1984 #[test]
1985 fn test_insert_blob() {
1986 let on_chain_balance = U256::MAX;
1987 let on_chain_nonce = 0;
1988 let mut f = MockTransactionFactory::default();
1989 let mut pool = AllTransactions::default();
1990 let tx = MockTransaction::eip4844().inc_price().inc_limit();
1991 let valid_tx = f.validated(tx);
1992 let InsertOk { updates, replaced_tx, move_to, state, .. } =
1993 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
1994 assert!(updates.is_empty());
1995 assert!(replaced_tx.is_none());
1996 assert!(state.contains(TxState::NO_NONCE_GAPS));
1997 assert!(state.contains(TxState::ENOUGH_BALANCE));
1998 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
1999 assert_eq!(move_to, SubPool::Pending);
2000
2001 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2002 assert_eq!(inserted.subpool, SubPool::Pending);
2003 }
2004
2005 #[test]
2006 fn test_insert_blob_not_enough_blob_fee() {
2007 let on_chain_balance = U256::MAX;
2008 let on_chain_nonce = 0;
2009 let mut f = MockTransactionFactory::default();
2010 let mut pool = AllTransactions {
2011 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2012 ..Default::default()
2013 };
2014 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2015 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2016 let valid_tx = f.validated(tx);
2017 let InsertOk { state, .. } =
2018 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2019 assert!(state.contains(TxState::NO_NONCE_GAPS));
2020 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2021
2022 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2023 }
2024
2025 #[test]
2026 fn test_valid_tx_with_decreasing_blob_fee() {
2027 let on_chain_balance = U256::MAX;
2028 let on_chain_nonce = 0;
2029 let mut f = MockTransactionFactory::default();
2030 let mut pool = AllTransactions {
2031 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2032 ..Default::default()
2033 };
2034 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2035
2036 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2037 let valid_tx = f.validated(tx.clone());
2038 let InsertOk { state, .. } =
2039 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2040 assert!(state.contains(TxState::NO_NONCE_GAPS));
2041 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2042
2043 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2044 pool.remove_transaction(&valid_tx.transaction_id);
2045
2046 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
2047 let InsertOk { state, .. } =
2048 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2049 assert!(state.contains(TxState::NO_NONCE_GAPS));
2050 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2051 }
2052
2053 #[test]
2054 fn test_demote_valid_tx_with_increasing_blob_fee() {
2055 let on_chain_balance = U256::MAX;
2056 let on_chain_nonce = 0;
2057 let mut f = MockTransactionFactory::default();
2058 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2059 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2060
2061 let mut block_info = pool.block_info();
2063 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2064 pool.set_block_info(block_info);
2065
2066 let validated = f.validated(tx.clone());
2067 let id = *validated.id();
2068 pool.add_transaction(validated, on_chain_balance, on_chain_nonce).unwrap();
2069
2070 assert!(pool.blob_pool.is_empty());
2072 assert_eq!(pool.pending_pool.len(), 1);
2073
2074 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2076 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2077 assert_eq!(internal_tx.subpool, SubPool::Pending);
2078
2079 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2081 pool.set_block_info(block_info);
2082
2083 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2085 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2086 assert_eq!(internal_tx.subpool, SubPool::Blob);
2087
2088 assert_eq!(pool.blob_pool.len(), 1);
2090 assert!(pool.pending_pool.is_empty());
2091 }
2092
2093 #[test]
2094 fn test_promote_valid_tx_with_decreasing_blob_fee() {
2095 let on_chain_balance = U256::MAX;
2096 let on_chain_nonce = 0;
2097 let mut f = MockTransactionFactory::default();
2098 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2099 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2100
2101 let mut block_info = pool.block_info();
2103 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2104 pool.set_block_info(block_info);
2105
2106 let validated = f.validated(tx.clone());
2107 let id = *validated.id();
2108 pool.add_transaction(validated, on_chain_balance, on_chain_nonce).unwrap();
2109
2110 assert!(pool.pending_pool.is_empty());
2112 assert_eq!(pool.blob_pool.len(), 1);
2113
2114 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2116 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2117 assert_eq!(internal_tx.subpool, SubPool::Blob);
2118
2119 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2121 pool.set_block_info(block_info);
2122
2123 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2125 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2126 assert_eq!(internal_tx.subpool, SubPool::Pending);
2127
2128 assert_eq!(pool.pending_pool.len(), 1);
2130 assert!(pool.blob_pool.is_empty());
2131 }
2132
2133 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
2135 struct PromotionTest {
2136 basefee: u64,
2138 blobfee: u128,
2140 subpool: SubPool,
2142 basefee_update: u64,
2144 blobfee_update: u128,
2146 new_subpool: SubPool,
2148 }
2149
2150 impl PromotionTest {
2151 const fn opposite(&self) -> Self {
2153 Self {
2154 basefee: self.basefee_update,
2155 blobfee: self.blobfee_update,
2156 subpool: self.new_subpool,
2157 blobfee_update: self.blobfee,
2158 basefee_update: self.basefee,
2159 new_subpool: self.subpool,
2160 }
2161 }
2162
2163 fn assert_subpool_lengths<T: TransactionOrdering>(
2164 &self,
2165 pool: &TxPool<T>,
2166 failure_message: String,
2167 check_subpool: SubPool,
2168 ) {
2169 match check_subpool {
2170 SubPool::Blob => {
2171 assert_eq!(pool.blob_pool.len(), 1, "{failure_message}");
2172 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2173 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2174 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2175 }
2176 SubPool::Pending => {
2177 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2178 assert_eq!(pool.pending_pool.len(), 1, "{failure_message}");
2179 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2180 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2181 }
2182 SubPool::BaseFee => {
2183 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2184 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2185 assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}");
2186 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2187 }
2188 SubPool::Queued => {
2189 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2190 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2191 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2192 assert_eq!(pool.queued_pool.len(), 1, "{failure_message}");
2193 }
2194 }
2195 }
2196
2197 fn assert_single_tx_starting_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2201 self.assert_subpool_lengths(
2202 pool,
2203 format!("pool length check failed at start of test: {self:?}"),
2204 self.subpool,
2205 );
2206 }
2207
2208 fn assert_single_tx_ending_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2212 self.assert_subpool_lengths(
2213 pool,
2214 format!("pool length check failed at end of test: {self:?}"),
2215 self.new_subpool,
2216 );
2217 }
2218 }
2219
2220 #[test]
2221 fn test_promote_blob_tx_with_both_pending_fee_updates() {
2222 let on_chain_balance = U256::MAX;
2225 let on_chain_nonce = 0;
2226 let mut f = MockTransactionFactory::default();
2227 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2228
2229 let max_fee_per_blob_gas = tx.max_fee_per_blob_gas().unwrap();
2230 let max_fee_per_gas = tx.max_fee_per_gas() as u64;
2231
2232 let mut expected_promotions = vec![
2234 PromotionTest {
2235 blobfee: max_fee_per_blob_gas + 1,
2236 basefee: max_fee_per_gas + 1,
2237 subpool: SubPool::Blob,
2238 blobfee_update: max_fee_per_blob_gas + 1,
2239 basefee_update: max_fee_per_gas + 1,
2240 new_subpool: SubPool::Blob,
2241 },
2242 PromotionTest {
2243 blobfee: max_fee_per_blob_gas + 1,
2244 basefee: max_fee_per_gas + 1,
2245 subpool: SubPool::Blob,
2246 blobfee_update: max_fee_per_blob_gas,
2247 basefee_update: max_fee_per_gas + 1,
2248 new_subpool: SubPool::Blob,
2249 },
2250 PromotionTest {
2251 blobfee: max_fee_per_blob_gas + 1,
2252 basefee: max_fee_per_gas + 1,
2253 subpool: SubPool::Blob,
2254 blobfee_update: max_fee_per_blob_gas + 1,
2255 basefee_update: max_fee_per_gas,
2256 new_subpool: SubPool::Blob,
2257 },
2258 PromotionTest {
2259 blobfee: max_fee_per_blob_gas + 1,
2260 basefee: max_fee_per_gas + 1,
2261 subpool: SubPool::Blob,
2262 blobfee_update: max_fee_per_blob_gas,
2263 basefee_update: max_fee_per_gas,
2264 new_subpool: SubPool::Pending,
2265 },
2266 PromotionTest {
2267 blobfee: max_fee_per_blob_gas,
2268 basefee: max_fee_per_gas + 1,
2269 subpool: SubPool::Blob,
2270 blobfee_update: max_fee_per_blob_gas,
2271 basefee_update: max_fee_per_gas,
2272 new_subpool: SubPool::Pending,
2273 },
2274 PromotionTest {
2275 blobfee: max_fee_per_blob_gas + 1,
2276 basefee: max_fee_per_gas,
2277 subpool: SubPool::Blob,
2278 blobfee_update: max_fee_per_blob_gas,
2279 basefee_update: max_fee_per_gas,
2280 new_subpool: SubPool::Pending,
2281 },
2282 PromotionTest {
2283 blobfee: max_fee_per_blob_gas,
2284 basefee: max_fee_per_gas,
2285 subpool: SubPool::Pending,
2286 blobfee_update: max_fee_per_blob_gas,
2287 basefee_update: max_fee_per_gas,
2288 new_subpool: SubPool::Pending,
2289 },
2290 ];
2291
2292 let reversed = expected_promotions.iter().map(|test| test.opposite()).collect::<Vec<_>>();
2294 expected_promotions.extend(reversed);
2295
2296 let expected_promotions = expected_promotions.into_iter().collect::<HashSet<_>>();
2298
2299 for promotion_test in &expected_promotions {
2300 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2301
2302 let mut block_info = pool.block_info();
2304
2305 block_info.pending_blob_fee = Some(promotion_test.blobfee);
2306 block_info.pending_basefee = promotion_test.basefee;
2307 pool.set_block_info(block_info);
2308
2309 let validated = f.validated(tx.clone());
2310 let id = *validated.id();
2311 pool.add_transaction(validated, on_chain_balance, on_chain_nonce).unwrap();
2312
2313 promotion_test.assert_single_tx_starting_subpool(&pool);
2315
2316 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2318 assert_eq!(
2319 internal_tx.subpool, promotion_test.subpool,
2320 "Subpools do not match at start of test: {promotion_test:?}"
2321 );
2322
2323 block_info.pending_basefee = promotion_test.basefee_update;
2325 block_info.pending_blob_fee = Some(promotion_test.blobfee_update);
2326 pool.set_block_info(block_info);
2327
2328 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2330 assert_eq!(
2331 internal_tx.subpool, promotion_test.new_subpool,
2332 "Subpools do not match at end of test: {promotion_test:?}"
2333 );
2334
2335 promotion_test.assert_single_tx_ending_subpool(&pool);
2337 }
2338 }
2339
2340 #[test]
2341 fn test_insert_pending() {
2342 let on_chain_balance = U256::MAX;
2343 let on_chain_nonce = 0;
2344 let mut f = MockTransactionFactory::default();
2345 let mut pool = AllTransactions::default();
2346 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2347 let valid_tx = f.validated(tx);
2348 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2349 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2350 assert!(updates.is_empty());
2351 assert!(replaced_tx.is_none());
2352 assert!(state.contains(TxState::NO_NONCE_GAPS));
2353 assert!(state.contains(TxState::ENOUGH_BALANCE));
2354 assert_eq!(move_to, SubPool::Pending);
2355
2356 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2357 assert_eq!(inserted.subpool, SubPool::Pending);
2358 }
2359
2360 #[test]
2361 fn test_simple_insert() {
2362 let on_chain_balance = U256::ZERO;
2363 let on_chain_nonce = 0;
2364 let mut f = MockTransactionFactory::default();
2365 let mut pool = AllTransactions::default();
2366 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2367 tx.set_priority_fee(100);
2368 tx.set_max_fee(100);
2369 let valid_tx = f.validated(tx.clone());
2370 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2371 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2372 assert!(updates.is_empty());
2373 assert!(replaced_tx.is_none());
2374 assert!(state.contains(TxState::NO_NONCE_GAPS));
2375 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2376 assert_eq!(move_to, SubPool::Queued);
2377
2378 assert_eq!(pool.len(), 1);
2379 assert!(pool.contains(valid_tx.hash()));
2380 let expected_state = TxState::ENOUGH_FEE_CAP_BLOCK | TxState::NO_NONCE_GAPS;
2381 let inserted = pool.get(valid_tx.id()).unwrap();
2382 assert!(inserted.state.intersects(expected_state));
2383
2384 let res = pool.insert_tx(valid_tx, on_chain_balance, on_chain_nonce);
2386 res.unwrap_err();
2387 assert_eq!(pool.len(), 1);
2388
2389 let valid_tx = f.validated(tx.next());
2390 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2391 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2392
2393 assert!(updates.is_empty());
2394 assert!(replaced_tx.is_none());
2395 assert!(state.contains(TxState::NO_NONCE_GAPS));
2396 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2397 assert_eq!(move_to, SubPool::Queued);
2398
2399 assert!(pool.contains(valid_tx.hash()));
2400 assert_eq!(pool.len(), 2);
2401 let inserted = pool.get(valid_tx.id()).unwrap();
2402 assert!(inserted.state.intersects(expected_state));
2403 }
2404
2405 #[test]
2406 fn insert_already_imported() {
2407 let on_chain_balance = U256::ZERO;
2408 let on_chain_nonce = 0;
2409 let mut f = MockTransactionFactory::default();
2410 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2411 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2412 let tx = f.validated(tx);
2413 pool.add_transaction(tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2414 match pool.add_transaction(tx, on_chain_balance, on_chain_nonce).unwrap_err().kind {
2415 PoolErrorKind::AlreadyImported => {}
2416 _ => unreachable!(),
2417 }
2418 }
2419
2420 #[test]
2421 fn insert_replace() {
2422 let on_chain_balance = U256::ZERO;
2423 let on_chain_nonce = 0;
2424 let mut f = MockTransactionFactory::default();
2425 let mut pool = AllTransactions::default();
2426 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2427 let first = f.validated(tx.clone());
2428 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
2429 let replacement = f.validated(tx.rng_hash().inc_price());
2430 let InsertOk { updates, replaced_tx, .. } =
2431 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
2432 assert!(updates.is_empty());
2433 let replaced = replaced_tx.unwrap();
2434 assert_eq!(replaced.0.hash(), first.hash());
2435
2436 assert!(!pool.contains(first.hash()));
2438 assert!(pool.contains(replacement.hash()));
2439 assert_eq!(pool.len(), 1);
2440 }
2441
2442 #[test]
2443 fn insert_replace_txpool() {
2444 let on_chain_balance = U256::ZERO;
2445 let on_chain_nonce = 0;
2446 let mut f = MockTransactionFactory::default();
2447 let mut pool = TxPool::mock();
2448
2449 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2450 let first = f.validated(tx.clone());
2451 let first_added = pool.add_transaction(first, on_chain_balance, on_chain_nonce).unwrap();
2452 let replacement = f.validated(tx.rng_hash().inc_price());
2453 let replacement_added =
2454 pool.add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
2455
2456 assert!(!pool.contains(first_added.hash()));
2458 assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));
2460
2461 assert!(pool.contains(replacement.hash()));
2462 let size = pool.size();
2463 assert_eq!(size.total, 1);
2464 size.assert_invariants();
2465 }
2466
2467 #[test]
2468 fn insert_replace_underpriced() {
2469 let on_chain_balance = U256::ZERO;
2470 let on_chain_nonce = 0;
2471 let mut f = MockTransactionFactory::default();
2472 let mut pool = AllTransactions::default();
2473 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2474 let first = f.validated(tx.clone());
2475 let _res = pool.insert_tx(first, on_chain_balance, on_chain_nonce);
2476 let mut replacement = f.validated(tx.rng_hash());
2477 replacement.transaction = replacement.transaction.decr_price();
2478 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
2479 assert!(matches!(err, InsertErr::Underpriced { .. }));
2480 }
2481
2482 #[test]
2483 fn insert_replace_underpriced_not_enough_bump() {
2484 let on_chain_balance = U256::ZERO;
2485 let on_chain_nonce = 0;
2486 let mut f = MockTransactionFactory::default();
2487 let mut pool = AllTransactions::default();
2488 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2489 tx.set_priority_fee(100);
2490 tx.set_max_fee(100);
2491 let first = f.validated(tx.clone());
2492 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
2493 let mut replacement = f.validated(tx.rng_hash().inc_price());
2494
2495 replacement.transaction.set_priority_fee(109);
2497 replacement.transaction.set_max_fee(109);
2498 let err =
2499 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
2500 assert!(matches!(err, InsertErr::Underpriced { .. }));
2501 assert!(pool.contains(first.hash()));
2503 assert_eq!(pool.len(), 1);
2504
2505 replacement.transaction.set_priority_fee(110);
2507 replacement.transaction.set_max_fee(109);
2508 let err =
2509 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
2510 assert!(matches!(err, InsertErr::Underpriced { .. }));
2511 assert!(pool.contains(first.hash()));
2512 assert_eq!(pool.len(), 1);
2513
2514 replacement.transaction.set_priority_fee(109);
2516 replacement.transaction.set_max_fee(110);
2517 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
2518 assert!(matches!(err, InsertErr::Underpriced { .. }));
2519 assert!(pool.contains(first.hash()));
2520 assert_eq!(pool.len(), 1);
2521 }
2522
2523 #[test]
2524 fn insert_conflicting_type_normal_to_blob() {
2525 let on_chain_balance = U256::from(10_000);
2526 let on_chain_nonce = 0;
2527 let mut f = MockTransactionFactory::default();
2528 let mut pool = AllTransactions::default();
2529 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2530 let first = f.validated(tx.clone());
2531 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
2532 let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
2533 let blob = f.validated(tx);
2534 let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
2535 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
2536 }
2537
2538 #[test]
2539 fn insert_conflicting_type_blob_to_normal() {
2540 let on_chain_balance = U256::from(10_000);
2541 let on_chain_nonce = 0;
2542 let mut f = MockTransactionFactory::default();
2543 let mut pool = AllTransactions::default();
2544 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2545 let first = f.validated(tx.clone());
2546 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
2547 let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
2548 let tx = f.validated(tx);
2549 let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
2550 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
2551 }
2552
2553 #[test]
2555 fn insert_previous() {
2556 let on_chain_balance = U256::ZERO;
2557 let on_chain_nonce = 0;
2558 let mut f = MockTransactionFactory::default();
2559 let mut pool = AllTransactions::default();
2560 let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
2561 let first = f.validated(tx.clone());
2562 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
2563
2564 let first_in_pool = pool.get(first.id()).unwrap();
2565
2566 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2568
2569 let prev = f.validated(tx.prev());
2570 let InsertOk { updates, replaced_tx, state, move_to, .. } =
2571 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
2572
2573 assert!(updates.is_empty());
2575 assert!(replaced_tx.is_none());
2576 assert!(state.contains(TxState::NO_NONCE_GAPS));
2577 assert_eq!(move_to, SubPool::Queued);
2578
2579 let first_in_pool = pool.get(first.id()).unwrap();
2580 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2582 }
2583
2584 #[test]
2586 fn insert_with_updates() {
2587 let on_chain_balance = U256::from(10_000);
2588 let on_chain_nonce = 0;
2589 let mut f = MockTransactionFactory::default();
2590 let mut pool = AllTransactions::default();
2591 let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
2592 let first = f.validated(tx.clone());
2593 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
2594
2595 let first_in_pool = pool.get(first.id()).unwrap();
2596 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2598 assert_eq!(SubPool::Queued, first_in_pool.subpool);
2599
2600 let prev = f.validated(tx.prev());
2601 let InsertOk { updates, replaced_tx, state, move_to, .. } =
2602 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
2603
2604 assert_eq!(updates.len(), 1);
2606 assert!(replaced_tx.is_none());
2607 assert!(state.contains(TxState::NO_NONCE_GAPS));
2608 assert_eq!(move_to, SubPool::Pending);
2609
2610 let first_in_pool = pool.get(first.id()).unwrap();
2611 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2613 assert_eq!(SubPool::Pending, first_in_pool.subpool);
2614 }
2615
2616 #[test]
2617 fn insert_previous_blocking() {
2618 let on_chain_balance = U256::from(1_000);
2619 let on_chain_nonce = 0;
2620 let mut f = MockTransactionFactory::default();
2621 let mut pool = AllTransactions::default();
2622 pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
2623 let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
2624 let first = f.validated(tx.clone());
2625
2626 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
2627
2628 let first_in_pool = pool.get(first.id()).unwrap();
2629
2630 assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
2631 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2633
2634 let prev = f.validated(tx.prev());
2635 let InsertOk { updates, replaced_tx, state, move_to, .. } =
2636 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
2637
2638 assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
2639 assert!(updates.is_empty());
2641 assert!(replaced_tx.is_none());
2642 assert!(state.contains(TxState::NO_NONCE_GAPS));
2643 assert_eq!(move_to, SubPool::BaseFee);
2644
2645 let first_in_pool = pool.get(first.id()).unwrap();
2646 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
2648 }
2649
2650 #[test]
2651 fn rejects_spammer() {
2652 let on_chain_balance = U256::from(1_000);
2653 let on_chain_nonce = 0;
2654 let mut f = MockTransactionFactory::default();
2655 let mut pool = AllTransactions::default();
2656
2657 let mut tx = MockTransaction::eip1559();
2658 let unblocked_tx = tx.clone();
2659 for _ in 0..pool.max_account_slots {
2660 tx = tx.next();
2661 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
2662 }
2663
2664 assert_eq!(
2665 pool.max_account_slots,
2666 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
2667 );
2668
2669 let err =
2670 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
2671 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
2672
2673 assert!(pool
2674 .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
2675 .is_ok());
2676 }
2677
2678 #[test]
2679 fn allow_local_spamming() {
2680 let on_chain_balance = U256::from(1_000);
2681 let on_chain_nonce = 0;
2682 let mut f = MockTransactionFactory::default();
2683 let mut pool = AllTransactions::default();
2684
2685 let mut tx = MockTransaction::eip1559();
2686 for _ in 0..pool.max_account_slots {
2687 tx = tx.next();
2688 pool.insert_tx(
2689 f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
2690 on_chain_balance,
2691 on_chain_nonce,
2692 )
2693 .unwrap();
2694 }
2695
2696 assert_eq!(
2697 pool.max_account_slots,
2698 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
2699 );
2700
2701 pool.insert_tx(
2702 f.validated_with_origin(TransactionOrigin::Local, tx.next()),
2703 on_chain_balance,
2704 on_chain_nonce,
2705 )
2706 .unwrap();
2707 }
2708
2709 #[test]
2710 fn reject_tx_over_gas_limit() {
2711 let on_chain_balance = U256::from(1_000);
2712 let on_chain_nonce = 0;
2713 let mut f = MockTransactionFactory::default();
2714 let mut pool = AllTransactions::default();
2715
2716 let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
2717
2718 assert!(matches!(
2719 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
2720 Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
2721 ));
2722 }
2723
2724 #[test]
2725 fn test_tx_equal_gas_limit() {
2726 let on_chain_balance = U256::from(1_000);
2727 let on_chain_nonce = 0;
2728 let mut f = MockTransactionFactory::default();
2729 let mut pool = AllTransactions::default();
2730
2731 let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
2732
2733 let InsertOk { state, .. } =
2734 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
2735 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
2736 }
2737
2738 #[test]
2739 fn update_basefee_subpools() {
2740 let mut f = MockTransactionFactory::default();
2741 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2742
2743 let tx = MockTransaction::eip1559().inc_price_by(10);
2744 let validated = f.validated(tx.clone());
2745 let id = *validated.id();
2746 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
2747
2748 assert_eq!(pool.pending_pool.len(), 1);
2749
2750 pool.update_basefee((tx.max_fee_per_gas() + 1) as u64);
2751
2752 assert!(pool.pending_pool.is_empty());
2753 assert_eq!(pool.basefee_pool.len(), 1);
2754
2755 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
2756 }
2757
2758 #[test]
2759 fn update_basefee_subpools_setting_block_info() {
2760 let mut f = MockTransactionFactory::default();
2761 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2762
2763 let tx = MockTransaction::eip1559().inc_price_by(10);
2764 let validated = f.validated(tx.clone());
2765 let id = *validated.id();
2766 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
2767
2768 assert_eq!(pool.pending_pool.len(), 1);
2769
2770 let mut block_info = pool.block_info();
2772 block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
2773 pool.set_block_info(block_info);
2774
2775 assert!(pool.pending_pool.is_empty());
2776 assert_eq!(pool.basefee_pool.len(), 1);
2777
2778 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
2779 }
2780
2781 #[test]
2782 fn get_highest_transaction_by_sender_and_nonce() {
2783 let mut f = MockTransactionFactory::default();
2785 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2786
2787 let tx = MockTransaction::eip1559();
2789 pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0).unwrap();
2790
2791 let tx1 = tx.inc_price().next();
2793
2794 let tx1_validated = f.validated(tx1.clone());
2796 pool.add_transaction(tx1_validated, U256::from(1_000), 0).unwrap();
2797
2798 assert_eq!(
2800 pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
2801 Some(1)
2802 );
2803
2804 let highest_tx = pool
2806 .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
2807 .expect("Failed to retrieve highest transaction");
2808
2809 assert_eq!(highest_tx.as_ref().transaction, tx1);
2811 }
2812
2813 #[test]
2814 fn get_highest_consecutive_transaction_by_sender() {
2815 let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
2817 let mut f = MockTransactionFactory::default();
2818
2819 let sender = Address::random();
2821 let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
2822 for nonce in txs {
2823 let mut mock_tx = MockTransaction::eip1559();
2824 mock_tx.set_sender(sender);
2825 mock_tx.set_nonce(nonce);
2826
2827 let validated_tx = f.validated(mock_tx);
2828 pool.add_transaction(validated_tx, U256::from(1000), 0).unwrap();
2829 }
2830
2831 let sender_id = f.ids.sender_id(&sender).unwrap();
2833 let next_tx =
2834 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
2835 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
2836
2837 let next_tx =
2838 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
2839 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
2840
2841 let next_tx =
2842 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
2843 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
2844
2845 let mut info = SenderInfo::default();
2847 info.update(8, U256::ZERO);
2848 pool.sender_info.insert(sender_id, info);
2849 let next_tx =
2850 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
2851 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
2852 }
2853
2854 #[test]
2855 fn discard_nonce_too_low() {
2856 let mut f = MockTransactionFactory::default();
2857 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2858
2859 let tx = MockTransaction::eip1559().inc_price_by(10);
2860 let validated = f.validated(tx.clone());
2861 let id = *validated.id();
2862 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
2863
2864 let next = tx.next();
2865 let validated = f.validated(next.clone());
2866 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
2867
2868 assert_eq!(pool.pending_pool.len(), 2);
2869
2870 let mut changed_senders = HashMap::default();
2871 changed_senders.insert(
2872 id.sender,
2873 SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
2874 );
2875 let outcome = pool.update_accounts(changed_senders);
2876 assert_eq!(outcome.discarded.len(), 1);
2877 assert_eq!(pool.pending_pool.len(), 1);
2878 }
2879
2880 #[test]
2881 fn discard_with_large_blob_txs() {
2882 reth_tracing::init_test_tracing();
2884
2885 let mut f = MockTransactionFactory::default();
2887 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2888 let default_limits = pool.config.blob_limit;
2889
2890 let a_sender = address!("000000000000000000000000000000000000000a");
2893
2894 let mut block_info = pool.block_info();
2896 block_info.pending_blob_fee = Some(100);
2897 block_info.pending_basefee = 100;
2898
2899 pool.set_block_info(block_info);
2901
2902 let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
2904 .into_iter()
2905 .map(|mut tx| {
2906 tx.set_size(default_limits.max_size / 2 + 1);
2907 tx.set_max_fee((block_info.pending_basefee - 1).into());
2908 tx
2909 })
2910 .collect::<Vec<_>>();
2911
2912 for tx in a_txs {
2914 pool.add_transaction(f.validated(tx), U256::from(1_000), 0).unwrap();
2915 }
2916
2917 let removed = pool.discard_worst();
2919 assert_eq!(removed.len(), 1);
2920 }
2921
2922 #[test]
2923 fn discard_with_parked_large_txs() {
2924 reth_tracing::init_test_tracing();
2926
2927 let mut f = MockTransactionFactory::default();
2929 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2930 let default_limits = pool.config.queued_limit;
2931
2932 let a_sender = address!("000000000000000000000000000000000000000a");
2935
2936 let pool_base_fee = 100;
2938 pool.update_basefee(pool_base_fee);
2939
2940 let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
2942 .into_iter()
2943 .map(|mut tx| {
2944 tx.set_size(default_limits.max_size / 2 + 1);
2945 tx.set_max_fee((pool_base_fee - 1).into());
2946 tx
2947 })
2948 .collect::<Vec<_>>();
2949
2950 for tx in a_txs {
2952 pool.add_transaction(f.validated(tx), U256::from(1_000), 0).unwrap();
2953 }
2954
2955 let removed = pool.discard_worst();
2957 assert_eq!(removed.len(), 1);
2958 }
2959
2960 #[test]
2961 fn discard_at_capacity() {
2962 let mut f = MockTransactionFactory::default();
2963 let queued_limit = SubPoolLimit::new(1000, usize::MAX);
2964 let mut pool =
2965 TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
2966
2967 for _ in 0..queued_limit.max_txs {
2969 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
2970 let validated = f.validated(tx.clone());
2971 let _id = *validated.id();
2972 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
2973 }
2974
2975 let size = pool.size();
2976 assert_eq!(size.queued, queued_limit.max_txs);
2977
2978 for _ in 0..queued_limit.max_txs {
2979 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
2980 let validated = f.validated(tx.clone());
2981 let _id = *validated.id();
2982 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
2983
2984 pool.discard_worst();
2985 pool.assert_invariants();
2986 assert!(pool.size().queued <= queued_limit.max_txs);
2987 }
2988 }
2989
2990 #[test]
2991 fn discard_blobs_at_capacity() {
2992 let mut f = MockTransactionFactory::default();
2993 let blob_limit = SubPoolLimit::new(1000, usize::MAX);
2994 let mut pool =
2995 TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
2996 pool.all_transactions.pending_fees.blob_fee = 10000;
2997 for _ in 0..blob_limit.max_txs {
2999 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3000 let validated = f.validated(tx.clone());
3001 let _id = *validated.id();
3002 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
3003 }
3004
3005 let size = pool.size();
3006 assert_eq!(size.blob, blob_limit.max_txs);
3007
3008 for _ in 0..blob_limit.max_txs {
3009 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3010 let validated = f.validated(tx.clone());
3011 let _id = *validated.id();
3012 pool.add_transaction(validated, U256::from(1_000), 0).unwrap();
3013
3014 pool.discard_worst();
3015 pool.assert_invariants();
3016 assert!(pool.size().blob <= blob_limit.max_txs);
3017 }
3018 }
3019
3020 #[test]
3021 fn account_updates_nonce_gap() {
3022 let on_chain_balance = U256::from(10_000);
3023 let mut on_chain_nonce = 0;
3024 let mut f = MockTransactionFactory::default();
3025 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3026
3027 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3028 let tx_1 = tx_0.next();
3029 let tx_2 = tx_1.next();
3030
3031 let v0 = f.validated(tx_0);
3033 let v1 = f.validated(tx_1);
3034 let v2 = f.validated(tx_2);
3035
3036 let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap();
3038 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce).unwrap();
3039
3040 assert!(pool.queued_transactions().is_empty());
3041 assert_eq!(2, pool.pending_transactions().len());
3042
3043 pool.prune_transaction_by_hash(v0.hash());
3045
3046 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce).unwrap();
3048
3049 assert_eq!(1, pool.queued_transactions().len());
3051 assert_eq!(1, pool.pending_transactions().len());
3052
3053 let mut updated_accounts = HashMap::default();
3055 on_chain_nonce += 1;
3056 updated_accounts.insert(
3057 v0.sender_id(),
3058 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3059 );
3060 pool.update_accounts(updated_accounts);
3061
3062 assert!(pool.queued_transactions().is_empty());
3064 assert_eq!(2, pool.pending_transactions().len());
3065 }
3066 #[test]
3067 fn test_transaction_removal() {
3068 let on_chain_balance = U256::from(10_000);
3069 let on_chain_nonce = 0;
3070 let mut f = MockTransactionFactory::default();
3071 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3072
3073 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3074 let tx_1 = tx_0.next();
3075
3076 let v0 = f.validated(tx_0);
3078 let v1 = f.validated(tx_1);
3079
3080 let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap();
3082 let _res = pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce).unwrap();
3083
3084 assert_eq!(0, pool.queued_transactions().len());
3085 assert_eq!(2, pool.pending_transactions().len());
3086
3087 pool.remove_transaction(v0.id());
3089 let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
3091 assert_eq!(vec![v1.nonce()], pool_txs);
3092 }
3093 #[test]
3094 fn test_remove_transactions() {
3095 let on_chain_balance = U256::from(10_000);
3096 let on_chain_nonce = 0;
3097 let mut f = MockTransactionFactory::default();
3098 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3099
3100 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3101 let tx_1 = tx_0.next();
3102 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3103 let tx_3 = tx_2.next();
3104
3105 let v0 = f.validated(tx_0);
3107 let v1 = f.validated(tx_1);
3108 let v2 = f.validated(tx_2);
3109 let v3 = f.validated(tx_3);
3110
3111 let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap();
3113 let _res = pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce).unwrap();
3114 let _res = pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce).unwrap();
3115 let _res = pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce).unwrap();
3116
3117 assert_eq!(0, pool.queued_transactions().len());
3118 assert_eq!(4, pool.pending_transactions().len());
3119
3120 pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
3121
3122 assert_eq!(0, pool.queued_transactions().len());
3123 assert_eq!(2, pool.pending_transactions().len());
3124 assert!(pool.contains(v1.hash()));
3125 assert!(pool.contains(v3.hash()));
3126 }
3127
3128 #[test]
3129 fn test_remove_transactions_and_descendants() {
3130 let on_chain_balance = U256::from(10_000);
3131 let on_chain_nonce = 0;
3132 let mut f = MockTransactionFactory::default();
3133 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3134
3135 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3136 let tx_1 = tx_0.next();
3137 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3138 let tx_3 = tx_2.next();
3139 let tx_4 = tx_3.next();
3140
3141 let v0 = f.validated(tx_0);
3143 let v1 = f.validated(tx_1);
3144 let v2 = f.validated(tx_2);
3145 let v3 = f.validated(tx_3);
3146 let v4 = f.validated(tx_4);
3147
3148 let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap();
3150 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce).unwrap();
3151 let _res = pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce).unwrap();
3152 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce).unwrap();
3153 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce).unwrap();
3154
3155 assert_eq!(0, pool.queued_transactions().len());
3156 assert_eq!(5, pool.pending_transactions().len());
3157
3158 pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
3159
3160 assert_eq!(0, pool.queued_transactions().len());
3161 assert_eq!(0, pool.pending_transactions().len());
3162 }
3163 #[test]
3164 fn test_remove_descendants() {
3165 let on_chain_balance = U256::from(10_000);
3166 let on_chain_nonce = 0;
3167 let mut f = MockTransactionFactory::default();
3168 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3169
3170 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3171 let tx_1 = tx_0.next();
3172 let tx_2 = tx_1.next();
3173 let tx_3 = tx_2.next();
3174
3175 let v0 = f.validated(tx_0);
3177 let v1 = f.validated(tx_1);
3178 let v2 = f.validated(tx_2);
3179 let v3 = f.validated(tx_3);
3180
3181 let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap();
3183 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce).unwrap();
3184 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce).unwrap();
3185 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce).unwrap();
3186
3187 assert_eq!(0, pool.queued_transactions().len());
3188 assert_eq!(4, pool.pending_transactions().len());
3189
3190 let mut removed = Vec::new();
3191 pool.remove_transaction(v0.id());
3192 pool.remove_descendants(v0.id(), &mut removed);
3193
3194 assert_eq!(0, pool.queued_transactions().len());
3195 assert_eq!(0, pool.pending_transactions().len());
3196 assert_eq!(3, removed.len());
3197 }
3198 #[test]
3199 fn test_remove_transactions_by_sender() {
3200 let on_chain_balance = U256::from(10_000);
3201 let on_chain_nonce = 0;
3202 let mut f = MockTransactionFactory::default();
3203 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3204
3205 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3206 let tx_1 = tx_0.next();
3207 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3208 let tx_3 = tx_2.next();
3209 let tx_4 = tx_3.next();
3210
3211 let v0 = f.validated(tx_0);
3213 let v1 = f.validated(tx_1);
3214 let v2 = f.validated(tx_2);
3215 let v3 = f.validated(tx_3);
3216 let v4 = f.validated(tx_4);
3217
3218 let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap();
3220 let _res = pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce).unwrap();
3221 let _res = pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce).unwrap();
3222 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce).unwrap();
3223 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce).unwrap();
3224
3225 assert_eq!(0, pool.queued_transactions().len());
3226 assert_eq!(5, pool.pending_transactions().len());
3227
3228 pool.remove_transactions_by_sender(v2.sender_id());
3229
3230 assert_eq!(0, pool.queued_transactions().len());
3231 assert_eq!(2, pool.pending_transactions().len());
3232 assert!(pool.contains(v0.hash()));
3233 assert!(pool.contains(v1.hash()));
3234 }
3235 #[test]
3236 fn wrong_best_order_of_transactions() {
3237 let on_chain_balance = U256::from(10_000);
3238 let mut on_chain_nonce = 0;
3239 let mut f = MockTransactionFactory::default();
3240 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3241
3242 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3243 let tx_1 = tx_0.next();
3244 let tx_2 = tx_1.next();
3245 let tx_3 = tx_2.next();
3246
3247 let v0 = f.validated(tx_0);
3249 let v1 = f.validated(tx_1);
3250 let v2 = f.validated(tx_2);
3251 let v3 = f.validated(tx_3);
3252
3253 let _res = pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce).unwrap();
3255 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce).unwrap();
3256
3257 assert_eq!(0, pool.queued_transactions().len());
3258 assert_eq!(2, pool.pending_transactions().len());
3259
3260 pool.remove_transaction(v0.id());
3262
3263 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce).unwrap();
3265
3266 assert_eq!(1, pool.queued_transactions().len());
3268 assert_eq!(1, pool.pending_transactions().len());
3269
3270 let mut updated_accounts = HashMap::default();
3272 on_chain_nonce += 1;
3273 updated_accounts.insert(
3274 v0.sender_id(),
3275 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3276 );
3277 pool.update_accounts(updated_accounts);
3278
3279 assert_eq!(0, pool.queued_transactions().len());
3282 assert_eq!(2, pool.pending_transactions().len());
3283
3284 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce).unwrap();
3286 assert_eq!(0, pool.queued_transactions().len());
3287 assert_eq!(3, pool.pending_transactions().len());
3288
3289 assert_eq!(
3292 pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
3293 vec![1, 2, 3]
3294 );
3295 }
3296
3297 #[test]
3298 fn test_pending_ordering() {
3299 let mut f = MockTransactionFactory::default();
3300 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3301
3302 let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
3303 let tx_1 = tx_0.next();
3304
3305 let v0 = f.validated(tx_0);
3306 let v1 = f.validated(tx_1);
3307
3308 pool.add_transaction(v0.clone(), U256::MAX, 0).unwrap();
3310 assert_eq!(1, pool.queued_transactions().len());
3311
3312 pool.add_transaction(v1, U256::MAX, 1).unwrap();
3314
3315 assert_eq!(2, pool.pending_transactions().len());
3316 assert_eq!(0, pool.queued_transactions().len());
3317
3318 assert_eq!(
3319 pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
3320 v0.nonce()
3321 );
3322 }
3323
3324 #[test]
3326 fn one_sender_one_independent_transaction() {
3327 let mut on_chain_balance = U256::from(4_999); let mut on_chain_nonce = 40;
3329 let mut f = MockTransactionFactory::default();
3330 let mut pool = TxPool::mock();
3331 let mut submitted_txs = Vec::new();
3332
3333 let template =
3335 MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
3336
3337 for tx_nonce in 40..48 {
3340 let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
3341 submitted_txs.push(*tx.id());
3342 pool.add_transaction(tx, on_chain_balance, on_chain_nonce).unwrap();
3343 }
3344
3345 on_chain_balance = U256::from(999_999);
3348 on_chain_nonce = 42;
3349 pool.remove_transaction(&submitted_txs[0]);
3350 pool.remove_transaction(&submitted_txs[1]);
3351
3352 for tx_nonce in 48..52 {
3354 pool.add_transaction(
3355 f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
3356 on_chain_balance,
3357 on_chain_nonce,
3358 )
3359 .unwrap();
3360 }
3361
3362 let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
3363 assert_eq!(best_txs.len(), 10); assert_eq!(pool.pending_pool.independent().len(), 1);
3366 }
3367}