1use crate::{
4 config::{LocalTransactionConfig, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER},
5 error::{
6 Eip4844PoolTransactionError, Eip7702PoolTransactionError, InvalidPoolTransactionError,
7 PoolError, PoolErrorKind,
8 },
9 identifier::{SenderId, TransactionId},
10 metrics::{AllTransactionsMetrics, TxPoolMetrics},
11 pool::{
12 best::BestTransactions,
13 blob::BlobTransactions,
14 parked::{BasefeeOrd, ParkedPool, QueuedOrd},
15 pending::PendingPool,
16 state::{SubPool, TxState},
17 update::{Destination, PoolUpdate, UpdateOutcome},
18 AddedPendingTransaction, AddedTransaction, OnNewCanonicalStateOutcome,
19 },
20 traits::{BestTransactionsAttributes, BlockInfo, PoolSize},
21 PoolConfig, PoolResult, PoolTransaction, PoolUpdateKind, PriceBumpConfig, TransactionOrdering,
22 ValidPoolTransaction, U256,
23};
24use alloy_consensus::constants::{
25 EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID, KECCAK_EMPTY,
26 LEGACY_TX_TYPE_ID,
27};
28use alloy_eips::{
29 eip1559::{ETHEREUM_BLOCK_GAS_LIMIT_30M, MIN_PROTOCOL_BASE_FEE},
30 eip4844::BLOB_TX_MIN_BLOB_GASPRICE,
31 Typed2718,
32};
33use alloy_primitives::{Address, TxHash, B256};
34use rustc_hash::FxHashMap;
35use smallvec::SmallVec;
36use std::{
37 cmp::Ordering,
38 collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet},
39 fmt,
40 ops::Bound::{Excluded, Unbounded},
41 sync::Arc,
42};
43use tracing::{trace, warn};
44
45#[cfg_attr(doc, aquamarine::aquamarine)]
46pub struct TxPool<T: TransactionOrdering> {
88 sender_info: FxHashMap<SenderId, SenderInfo>,
90 pending_pool: PendingPool<T>,
94 config: PoolConfig,
96 queued_pool: ParkedPool<QueuedOrd<T::Transaction>>,
103 basefee_pool: ParkedPool<BasefeeOrd<T::Transaction>>,
108 blob_pool: BlobTransactions<T::Transaction>,
115 all_transactions: AllTransactions<T::Transaction>,
117 metrics: TxPoolMetrics,
119 latest_update_kind: Option<PoolUpdateKind>,
121}
122
123impl<T: TransactionOrdering> TxPool<T> {
126 pub fn new(ordering: T, config: PoolConfig) -> Self {
128 Self {
129 sender_info: Default::default(),
130 pending_pool: PendingPool::with_buffer(
131 ordering,
132 config.max_new_pending_txs_notifications,
133 ),
134 queued_pool: Default::default(),
135 basefee_pool: Default::default(),
136 blob_pool: Default::default(),
137 all_transactions: AllTransactions::new(&config),
138 config,
139 metrics: Default::default(),
140 latest_update_kind: None,
141 }
142 }
143
144 pub fn get_highest_nonce_by_sender(&self, sender: SenderId) -> Option<u64> {
146 self.all().txs_iter(sender).last().map(|(_, tx)| tx.transaction.nonce())
147 }
148
149 pub fn get_highest_transaction_by_sender(
152 &self,
153 sender: SenderId,
154 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
155 self.all().txs_iter(sender).last().map(|(_, tx)| Arc::clone(&tx.transaction))
156 }
157
158 pub(crate) fn get_highest_consecutive_transaction_by_sender(
165 &self,
166 mut on_chain: TransactionId,
167 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
168 let mut last_consecutive_tx = None;
169
170 if let Some(current) = self.sender_info.get(&on_chain.sender) {
172 on_chain.nonce = on_chain.nonce.max(current.state_nonce);
173 }
174
175 let mut next_expected_nonce = on_chain.nonce;
176 for (id, tx) in self.all().descendant_txs_inclusive(&on_chain) {
177 if next_expected_nonce != id.nonce {
178 break
179 }
180 next_expected_nonce = id.next_nonce();
181 last_consecutive_tx = Some(tx);
182 }
183
184 last_consecutive_tx.map(|tx| Arc::clone(&tx.transaction))
185 }
186
187 pub(crate) const fn all(&self) -> &AllTransactions<T::Transaction> {
189 &self.all_transactions
190 }
191
192 pub(crate) fn unique_senders(&self) -> HashSet<Address> {
194 self.all_transactions.txs.values().map(|tx| tx.transaction.sender()).collect()
195 }
196
197 pub fn size(&self) -> PoolSize {
199 PoolSize {
200 pending: self.pending_pool.len(),
201 pending_size: self.pending_pool.size(),
202 basefee: self.basefee_pool.len(),
203 basefee_size: self.basefee_pool.size(),
204 queued: self.queued_pool.len(),
205 queued_size: self.queued_pool.size(),
206 blob: self.blob_pool.len(),
207 blob_size: self.blob_pool.size(),
208 total: self.all_transactions.len(),
209 }
210 }
211
212 pub const fn block_info(&self) -> BlockInfo {
214 BlockInfo {
215 block_gas_limit: self.all_transactions.block_gas_limit,
216 last_seen_block_hash: self.all_transactions.last_seen_block_hash,
217 last_seen_block_number: self.all_transactions.last_seen_block_number,
218 pending_basefee: self.all_transactions.pending_fees.base_fee,
219 pending_blob_fee: Some(self.all_transactions.pending_fees.blob_fee),
220 }
221 }
222
223 fn update_blob_fee<F>(
225 &mut self,
226 mut pending_blob_fee: u128,
227 base_fee_update: Ordering,
228 mut on_promoted: F,
229 ) where
230 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
231 {
232 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut pending_blob_fee);
233 match (self.all_transactions.pending_fees.blob_fee.cmp(&pending_blob_fee), base_fee_update)
234 {
235 (Ordering::Equal, Ordering::Equal | Ordering::Greater) => {
236 }
238 (Ordering::Greater, Ordering::Equal | Ordering::Greater) => {
239 let removed =
241 self.pending_pool.update_blob_fee(self.all_transactions.pending_fees.blob_fee);
242 for tx in removed {
243 let to = {
244 let tx =
245 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
246
247 tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
249 tx.subpool = tx.state.into();
250 tx.subpool
251 };
252 self.add_transaction_to_subpool(to, tx);
253 }
254 }
255 (Ordering::Less, _) | (_, Ordering::Less) => {
256 let removed =
258 self.blob_pool.enforce_pending_fees(&self.all_transactions.pending_fees);
259 for tx in removed {
260 let subpool = {
261 let tx_meta =
262 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
263 tx_meta.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
264 tx_meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
265 tx_meta.subpool = tx_meta.state.into();
266 tx_meta.subpool
267 };
268
269 if subpool == SubPool::Pending {
270 on_promoted(&tx);
271 }
272
273 self.add_transaction_to_subpool(subpool, tx);
274 }
275 }
276 }
277 }
278
279 fn update_basefee<F>(&mut self, mut pending_basefee: u64, mut on_promoted: F) -> Ordering
284 where
285 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
286 {
287 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut pending_basefee);
288 match self.all_transactions.pending_fees.base_fee.cmp(&pending_basefee) {
289 Ordering::Equal => {
290 Ordering::Equal
292 }
293 Ordering::Greater => {
294 let removed =
296 self.pending_pool.update_base_fee(self.all_transactions.pending_fees.base_fee);
297 for tx in removed {
298 let to = {
299 let tx =
300 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
301 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
302 tx.subpool = tx.state.into();
303 tx.subpool
304 };
305 self.add_transaction_to_subpool(to, tx);
306 }
307
308 Ordering::Greater
309 }
310 Ordering::Less => {
311 let current_base_fee = self.all_transactions.pending_fees.base_fee;
320 self.basefee_pool.enforce_basefee_with(current_base_fee, |tx| {
321 let subpool = {
323 let meta =
324 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
325 meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
326 meta.subpool = meta.state.into();
327 meta.subpool
328 };
329
330 if subpool == SubPool::Pending {
331 on_promoted(&tx);
332 }
333
334 trace!(target: "txpool", hash=%tx.transaction.hash(), pool=?subpool, "Adding transaction to a subpool");
335 match subpool {
336 SubPool::Queued => self.queued_pool.add_transaction(tx),
337 SubPool::Pending => {
338 self.pending_pool.add_transaction(tx, current_base_fee);
339 }
340 SubPool::Blob => {
341 self.blob_pool.add_transaction(tx);
342 }
343 SubPool::BaseFee => {
344 warn!(target: "txpool", "BaseFee transactions should become Pending after basefee decrease");
347 }
348 }
349 });
350
351 Ordering::Less
352 }
353 }
354 }
355
356 pub fn set_block_info(&mut self, info: BlockInfo) {
360 let basefee_ordering = self.update_basefee(info.pending_basefee, |_| {});
362 if let Some(blob_fee) = info.pending_blob_fee {
363 self.update_blob_fee(blob_fee, basefee_ordering, |_| {})
364 }
365 self.all_transactions.set_block_info(info);
367 }
368
369 pub(crate) fn best_transactions(&self) -> BestTransactions<T> {
372 self.pending_pool.best()
373 }
374
375 pub(crate) fn best_transactions_with_attributes(
382 &self,
383 best_transactions_attributes: BestTransactionsAttributes,
384 ) -> Box<dyn crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T::Transaction>>>>
385 {
386 match best_transactions_attributes.basefee.cmp(&self.all_transactions.pending_fees.base_fee)
389 {
390 Ordering::Equal => {
391 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
395 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
396 Ordering::Less => {
397 let unlocked =
399 self.blob_pool.satisfy_attributes(best_transactions_attributes);
400 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
401 unlocked,
402 best_transactions_attributes.basefee,
403 new_blob_fee,
404 ))
405 }
406 Ordering::Equal => Box::new(self.pending_pool.best()),
407 Ordering::Greater => {
408 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
410 best_transactions_attributes.basefee,
411 best_transactions_attributes.blob_fee.unwrap_or_default(),
412 ))
413 }
414 }
415 }
416 Ordering::Greater => {
417 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
419 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
420 Ordering::Less => {
421 let unlocked =
423 self.blob_pool.satisfy_attributes(best_transactions_attributes);
424 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
425 unlocked,
426 best_transactions_attributes.basefee,
427 new_blob_fee,
428 ))
429 }
430 Ordering::Equal | Ordering::Greater => {
431 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
433 best_transactions_attributes.basefee,
434 new_blob_fee,
435 ))
436 }
437 }
438 }
439 Ordering::Less => {
440 let mut unlocked = self
443 .basefee_pool
444 .satisfy_base_fee_transactions(best_transactions_attributes.basefee);
445
446 unlocked.extend(self.blob_pool.satisfy_attributes(best_transactions_attributes));
448
449 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
450 unlocked,
451 best_transactions_attributes.basefee,
452 best_transactions_attributes.blob_fee.unwrap_or_default(),
453 ))
454 }
455 }
456 }
457
458 pub(crate) fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
460 self.pending_pool.all().collect()
461 }
462 pub(crate) fn pending_transactions_iter(
464 &self,
465 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
466 self.pending_pool.all()
467 }
468
469 pub(crate) fn pending_transactions_count(&self) -> usize {
471 self.pending_pool.len()
472 }
473
474 pub(crate) fn pending_transactions_with_predicate(
476 &self,
477 mut predicate: impl FnMut(&ValidPoolTransaction<T::Transaction>) -> bool,
478 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
479 self.pending_transactions_iter().filter(|tx| predicate(tx)).collect()
480 }
481
482 pub(crate) fn pending_txs_by_sender(
484 &self,
485 sender: SenderId,
486 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
487 self.pending_transactions_iter().filter(|tx| tx.sender_id() == sender).collect()
488 }
489
490 pub(crate) fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
492 self.basefee_pool.all().chain(self.queued_pool.all()).collect()
493 }
494
495 pub(crate) fn queued_transactions_iter(
497 &self,
498 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
499 self.basefee_pool.all().chain(self.queued_pool.all())
500 }
501
502 pub(crate) fn queued_transactions_count(&self) -> usize {
504 self.basefee_pool.len() + self.queued_pool.len()
505 }
506
507 pub fn queued_and_pending_txs_by_sender(
509 &self,
510 sender: SenderId,
511 ) -> (SmallVec<[TransactionId; TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER]>, Vec<TransactionId>) {
512 (self.queued_pool.get_txs_by_sender(sender), self.pending_pool.get_txs_by_sender(sender))
513 }
514
515 pub(crate) fn queued_txs_by_sender(
517 &self,
518 sender: SenderId,
519 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
520 self.queued_transactions_iter().filter(|tx| tx.sender_id() == sender).collect()
521 }
522
523 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
525 self.all_transactions.contains(tx_hash)
526 }
527
528 #[cfg(test)]
530 pub(crate) fn subpool_contains(&self, subpool: SubPool, id: &TransactionId) -> bool {
531 match subpool {
532 SubPool::Queued => self.queued_pool.contains(id),
533 SubPool::Pending => self.pending_pool.contains(id),
534 SubPool::BaseFee => self.basefee_pool.contains(id),
535 SubPool::Blob => self.blob_pool.contains(id),
536 }
537 }
538
539 #[inline]
541 pub(crate) fn is_exceeded(&self) -> bool {
542 self.config.is_exceeded(self.size())
543 }
544
545 pub(crate) fn get(
547 &self,
548 tx_hash: &TxHash,
549 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
550 self.all_transactions.by_hash.get(tx_hash).cloned()
551 }
552
553 pub(crate) fn get_all(
555 &self,
556 txs: Vec<TxHash>,
557 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
558 txs.into_iter().filter_map(|tx| self.get(&tx))
559 }
560
561 pub(crate) fn get_transactions_by_sender(
563 &self,
564 sender: SenderId,
565 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
566 self.all_transactions.txs_iter(sender).map(|(_, tx)| Arc::clone(&tx.transaction)).collect()
567 }
568
569 const fn update_pending_fees_only(
572 &mut self,
573 mut new_base_fee: u64,
574 new_blob_fee: Option<u128>,
575 ) -> (u64, u128) {
576 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut new_base_fee);
577
578 let prev_blob_fee = if let Some(mut blob_fee) = new_blob_fee {
579 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut blob_fee);
580 blob_fee
581 } else {
582 self.all_transactions.pending_fees.blob_fee
583 };
584
585 (new_base_fee, prev_blob_fee)
586 }
587
588 fn apply_fee_updates(
595 &mut self,
596 prev_base_fee: u64,
597 prev_blob_fee: u128,
598 outcome: &mut UpdateOutcome<T::Transaction>,
599 ) {
600 let new_base_fee = self.all_transactions.pending_fees.base_fee;
601 let new_blob_fee = self.all_transactions.pending_fees.blob_fee;
602
603 if new_base_fee == prev_base_fee && new_blob_fee == prev_blob_fee {
604 return;
606 }
607
608 self.all_transactions.pending_fees.base_fee = prev_base_fee;
611 self.all_transactions.pending_fees.blob_fee = prev_blob_fee;
612
613 let base_fee_ordering = self.update_basefee(new_base_fee, |tx| {
614 outcome.promoted.push(tx.clone());
615 });
616
617 self.update_blob_fee(new_blob_fee, base_fee_ordering, |tx| {
618 outcome.promoted.push(tx.clone());
619 });
620 }
621
622 pub(crate) fn update_accounts(
624 &mut self,
625 changed_senders: FxHashMap<SenderId, SenderInfo>,
626 ) -> UpdateOutcome<T::Transaction> {
627 let updates = self.all_transactions.update(&changed_senders);
629
630 self.sender_info.extend(changed_senders);
632
633 let update = self.process_updates(updates);
635 self.update_size_metrics();
637 update
638 }
639
640 pub(crate) fn on_canonical_state_change(
645 &mut self,
646 block_info: BlockInfo,
647 mined_transactions: Vec<TxHash>,
648 changed_senders: FxHashMap<SenderId, SenderInfo>,
649 update_kind: PoolUpdateKind,
650 ) -> OnNewCanonicalStateOutcome<T::Transaction> {
651 let block_hash = block_info.last_seen_block_hash;
653
654 let mut removed_txs_count = 0;
656 for tx_hash in &mined_transactions {
657 if self.prune_transaction_by_hash(tx_hash).is_some() {
658 removed_txs_count += 1;
659 }
660 }
661
662 self.metrics.removed_transactions.increment(removed_txs_count);
664
665 let (prev_base_fee, prev_blob_fee) =
670 self.update_pending_fees_only(block_info.pending_basefee, block_info.pending_blob_fee);
671
672 let mut outcome = self.update_accounts(changed_senders);
674
675 self.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
678
679 self.all_transactions.set_block_info(block_info);
681
682 self.update_transaction_type_metrics();
683 self.metrics.performed_state_updates.increment(1);
684
685 self.latest_update_kind = Some(update_kind);
687
688 OnNewCanonicalStateOutcome {
689 block_hash,
690 mined: mined_transactions,
691 promoted: outcome.promoted,
692 discarded: outcome.discarded,
693 }
694 }
695
696 pub(crate) fn update_size_metrics(&self) {
698 let stats = self.size();
699 self.metrics.pending_pool_transactions.set(stats.pending as f64);
700 self.metrics.pending_pool_size_bytes.set(stats.pending_size as f64);
701 self.metrics.basefee_pool_transactions.set(stats.basefee as f64);
702 self.metrics.basefee_pool_size_bytes.set(stats.basefee_size as f64);
703 self.metrics.queued_pool_transactions.set(stats.queued as f64);
704 self.metrics.queued_pool_size_bytes.set(stats.queued_size as f64);
705 self.metrics.blob_pool_transactions.set(stats.blob as f64);
706 self.metrics.blob_pool_size_bytes.set(stats.blob_size as f64);
707 self.metrics.total_transactions.set(stats.total as f64);
708 }
709
710 pub(crate) fn update_transaction_type_metrics(&self) {
712 let mut legacy_count = 0;
713 let mut eip2930_count = 0;
714 let mut eip1559_count = 0;
715 let mut eip4844_count = 0;
716 let mut eip7702_count = 0;
717
718 for tx in self.all_transactions.transactions_iter() {
719 match tx.transaction.ty() {
720 LEGACY_TX_TYPE_ID => legacy_count += 1,
721 EIP2930_TX_TYPE_ID => eip2930_count += 1,
722 EIP1559_TX_TYPE_ID => eip1559_count += 1,
723 EIP4844_TX_TYPE_ID => eip4844_count += 1,
724 EIP7702_TX_TYPE_ID => eip7702_count += 1,
725 _ => {} }
727 }
728
729 self.metrics.total_legacy_transactions.set(legacy_count as f64);
730 self.metrics.total_eip2930_transactions.set(eip2930_count as f64);
731 self.metrics.total_eip1559_transactions.set(eip1559_count as f64);
732 self.metrics.total_eip4844_transactions.set(eip4844_count as f64);
733 self.metrics.total_eip7702_transactions.set(eip7702_count as f64);
734 }
735
736 pub(crate) fn add_transaction(
737 &mut self,
738 tx: ValidPoolTransaction<T::Transaction>,
739 on_chain_balance: U256,
740 on_chain_nonce: u64,
741 on_chain_code_hash: Option<B256>,
742 ) -> PoolResult<AddedTransaction<T::Transaction>> {
743 if self.contains(tx.hash()) {
744 return Err(PoolError::new(*tx.hash(), PoolErrorKind::AlreadyImported))
745 }
746
747 self.validate_auth(&tx, on_chain_nonce, on_chain_code_hash)?;
748
749 self.sender_info
751 .entry(tx.sender_id())
752 .or_default()
753 .update(on_chain_nonce, on_chain_balance);
754
755 match self.all_transactions.insert_tx(tx, on_chain_balance, on_chain_nonce) {
756 Ok(InsertOk { transaction, move_to, replaced_tx, updates, state }) => {
757 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
759 self.metrics.inserted_transactions.increment(1);
761 let UpdateOutcome { promoted, discarded } = self.process_updates(updates);
762
763 let replaced = replaced_tx.map(|(tx, _)| tx);
764
765 let res = if move_to.is_pending() {
767 AddedTransaction::Pending(AddedPendingTransaction {
768 transaction,
769 promoted,
770 discarded,
771 replaced,
772 })
773 } else {
774 let queued_reason = state.determine_queued_reason(move_to);
776 AddedTransaction::Parked {
777 transaction,
778 subpool: move_to,
779 replaced,
780 queued_reason,
781 }
782 };
783
784 self.update_size_metrics();
786
787 Ok(res)
788 }
789 Err(err) => {
790 self.metrics.invalid_transactions.increment(1);
792 match err {
793 InsertErr::Underpriced { existing: _, transaction } => Err(PoolError::new(
794 *transaction.hash(),
795 PoolErrorKind::ReplacementUnderpriced,
796 )),
797 InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap } => {
798 Err(PoolError::new(
799 *transaction.hash(),
800 PoolErrorKind::FeeCapBelowMinimumProtocolFeeCap(fee_cap),
801 ))
802 }
803 InsertErr::ExceededSenderTransactionsCapacity { transaction } => {
804 Err(PoolError::new(
805 *transaction.hash(),
806 PoolErrorKind::SpammerExceededCapacity(transaction.sender()),
807 ))
808 }
809 InsertErr::TxGasLimitMoreThanAvailableBlockGas {
810 transaction,
811 block_gas_limit,
812 tx_gas_limit,
813 } => Err(PoolError::new(
814 *transaction.hash(),
815 PoolErrorKind::InvalidTransaction(
816 InvalidPoolTransactionError::ExceedsGasLimit(
817 tx_gas_limit,
818 block_gas_limit,
819 ),
820 ),
821 )),
822 InsertErr::BlobTxHasNonceGap { transaction } => Err(PoolError::new(
823 *transaction.hash(),
824 PoolErrorKind::InvalidTransaction(
825 Eip4844PoolTransactionError::Eip4844NonceGap.into(),
826 ),
827 )),
828 InsertErr::Overdraft { transaction } => Err(PoolError::new(
829 *transaction.hash(),
830 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Overdraft {
831 cost: *transaction.cost(),
832 balance: on_chain_balance,
833 }),
834 )),
835 InsertErr::TxTypeConflict { transaction } => Err(PoolError::new(
836 *transaction.hash(),
837 PoolErrorKind::ExistingConflictingTransactionType(
838 transaction.sender(),
839 transaction.tx_type(),
840 ),
841 )),
842 }
843 }
844 }
845 }
846
847 fn check_delegation_limit(
851 &self,
852 transaction: &ValidPoolTransaction<T::Transaction>,
853 on_chain_nonce: u64,
854 on_chain_code_hash: Option<B256>,
855 ) -> Result<(), PoolError> {
856 if (on_chain_code_hash.is_none() || on_chain_code_hash == Some(KECCAK_EMPTY)) &&
858 !self.all_transactions.auths.contains_key(&transaction.sender_id())
859 {
860 return Ok(())
861 }
862
863 let mut txs_by_sender =
864 self.pending_pool.iter_txs_by_sender(transaction.sender_id()).peekable();
865
866 if txs_by_sender.peek().is_none() {
867 let nonce_gap_distance = transaction.nonce().saturating_sub(on_chain_nonce);
872 if nonce_gap_distance >= self.config.max_inflight_delegated_slot_limit as u64 {
873 return Err(PoolError::new(
874 *transaction.hash(),
875 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
876 Eip7702PoolTransactionError::OutOfOrderTxFromDelegated,
877 )),
878 ))
879 }
880 return Ok(())
881 }
882
883 let mut count = 0;
884 for id in txs_by_sender {
885 if id == &transaction.transaction_id {
886 return Ok(())
888 }
889 count += 1;
890 }
891
892 if count < self.config.max_inflight_delegated_slot_limit {
893 return Ok(())
895 }
896
897 Err(PoolError::new(
898 *transaction.hash(),
899 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
900 Eip7702PoolTransactionError::InflightTxLimitReached,
901 )),
902 ))
903 }
904
905 fn validate_auth(
915 &self,
916 transaction: &ValidPoolTransaction<T::Transaction>,
917 on_chain_nonce: u64,
918 on_chain_code_hash: Option<B256>,
919 ) -> Result<(), PoolError> {
920 self.check_delegation_limit(transaction, on_chain_nonce, on_chain_code_hash)?;
922
923 if let Some(authority_list) = &transaction.authority_ids {
924 for sender_id in authority_list {
925 if self.all_transactions.txs_iter(*sender_id).nth(1).is_some() {
927 return Err(PoolError::new(
928 *transaction.hash(),
929 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
930 Eip7702PoolTransactionError::AuthorityReserved,
931 )),
932 ))
933 }
934 }
935 }
936
937 Ok(())
938 }
939
940 fn process_updates(&mut self, updates: Vec<PoolUpdate>) -> UpdateOutcome<T::Transaction> {
944 let mut outcome = UpdateOutcome::default();
945 for PoolUpdate { id, current, destination } in updates {
946 match destination {
947 Destination::Discard => {
948 if let Some(tx) = self.prune_transaction_by_id(&id) {
950 outcome.discarded.push(tx);
951 }
952 self.metrics.removed_transactions.increment(1);
953 }
954 Destination::Pool(move_to) => {
955 debug_assert_ne!(&move_to, ¤t, "destination must be different");
956 let moved = self.move_transaction(current, move_to, &id);
957 if matches!(move_to, SubPool::Pending) {
958 if let Some(tx) = moved {
959 trace!(target: "txpool", hash=%tx.transaction.hash(), "Promoted transaction to pending");
960 outcome.promoted.push(tx);
961 }
962 }
963 }
964 }
965 }
966
967 outcome
968 }
969
970 fn move_transaction(
975 &mut self,
976 from: SubPool,
977 to: SubPool,
978 id: &TransactionId,
979 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
980 let tx = self.remove_from_subpool(from, id)?;
981 self.add_transaction_to_subpool(to, tx.clone());
982 Some(tx)
983 }
984
985 pub(crate) fn remove_transactions(
990 &mut self,
991 hashes: Vec<TxHash>,
992 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
993 let txs =
994 hashes.into_iter().filter_map(|hash| self.remove_transaction_by_hash(&hash)).collect();
995 self.update_size_metrics();
996 txs
997 }
998
999 pub(crate) fn remove_transactions_and_descendants(
1001 &mut self,
1002 hashes: Vec<TxHash>,
1003 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1004 let mut removed = Vec::new();
1005 for hash in hashes {
1006 if let Some(tx) = self.remove_transaction_by_hash(&hash) {
1007 removed.push(tx.clone());
1008 self.remove_descendants(tx.id(), &mut removed);
1009 }
1010 }
1011 self.update_size_metrics();
1012 removed
1013 }
1014
1015 pub(crate) fn remove_transactions_by_sender(
1017 &mut self,
1018 sender_id: SenderId,
1019 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1020 let mut removed = Vec::new();
1021 let txs = self.get_transactions_by_sender(sender_id);
1022 for tx in txs {
1023 if let Some(tx) = self.remove_transaction(tx.id()) {
1024 removed.push(tx);
1025 }
1026 }
1027 self.update_size_metrics();
1028 removed
1029 }
1030
1031 fn remove_transaction(
1035 &mut self,
1036 id: &TransactionId,
1037 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1038 let (tx, pool) = self.all_transactions.remove_transaction(id)?;
1039 self.remove_from_subpool(pool, tx.id())
1040 }
1041
1042 fn remove_transaction_by_hash(
1048 &mut self,
1049 tx_hash: &B256,
1050 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1051 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1052
1053 let updates = self.all_transactions.park_descendant_transactions(tx.id());
1055 self.process_updates(updates);
1056 self.remove_from_subpool(pool, tx.id())
1057 }
1058
1059 fn prune_transaction_by_hash(
1066 &mut self,
1067 tx_hash: &B256,
1068 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1069 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1070 self.remove_from_subpool(pool, tx.id())
1071 }
1072 fn prune_transaction_by_id(
1077 &mut self,
1078 tx_id: &TransactionId,
1079 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1080 let (tx, pool) = self.all_transactions.remove_transaction_by_id(tx_id)?;
1081 self.remove_from_subpool(pool, tx.id())
1082 }
1083
1084 fn remove_from_subpool(
1088 &mut self,
1089 pool: SubPool,
1090 tx: &TransactionId,
1091 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1092 let tx = match pool {
1093 SubPool::Queued => self.queued_pool.remove_transaction(tx),
1094 SubPool::Pending => self.pending_pool.remove_transaction(tx),
1095 SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
1096 SubPool::Blob => self.blob_pool.remove_transaction(tx),
1097 };
1098
1099 if let Some(ref tx) = tx {
1100 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Removed transaction from a subpool");
1104 }
1105
1106 tx
1107 }
1108
1109 fn remove_descendants(
1113 &mut self,
1114 tx: &TransactionId,
1115 removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
1116 ) {
1117 let mut id = *tx;
1118
1119 loop {
1121 let descendant =
1122 self.all_transactions.descendant_txs_exclusive(&id).map(|(id, _)| *id).next();
1123 if let Some(descendant) = descendant {
1124 if let Some(tx) = self.remove_transaction(&descendant) {
1125 removed.push(tx)
1126 }
1127 id = descendant;
1128 } else {
1129 return
1130 }
1131 }
1132 }
1133
1134 fn add_transaction_to_subpool(
1136 &mut self,
1137 pool: SubPool,
1138 tx: Arc<ValidPoolTransaction<T::Transaction>>,
1139 ) {
1140 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Adding transaction to a subpool");
1144 match pool {
1145 SubPool::Queued => self.queued_pool.add_transaction(tx),
1146 SubPool::Pending => {
1147 self.pending_pool.add_transaction(tx, self.all_transactions.pending_fees.base_fee);
1148 }
1149 SubPool::BaseFee => {
1150 self.basefee_pool.add_transaction(tx);
1151 }
1152 SubPool::Blob => {
1153 self.blob_pool.add_transaction(tx);
1154 }
1155 }
1156 }
1157
1158 fn add_new_transaction(
1161 &mut self,
1162 transaction: Arc<ValidPoolTransaction<T::Transaction>>,
1163 replaced: Option<(Arc<ValidPoolTransaction<T::Transaction>>, SubPool)>,
1164 pool: SubPool,
1165 ) {
1166 if let Some((replaced, replaced_pool)) = replaced {
1167 self.remove_from_subpool(replaced_pool, replaced.id());
1169 }
1170
1171 self.add_transaction_to_subpool(pool, transaction)
1172 }
1173
1174 pub(crate) fn discard_worst(&mut self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1181 let mut removed = Vec::new();
1182
1183 macro_rules! discard_worst {
1185 ($this:ident, $removed:ident, [$($limit:ident => ($pool:ident, $metric:ident)),* $(,)*]) => {
1186 $ (
1187 while $this.$pool.exceeds(&$this.config.$limit)
1188 {
1189 trace!(
1190 target: "txpool",
1191 "discarding transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1192 stringify!($pool),
1193 $this.config.$limit,
1194 $this.$pool.size(),
1195 $this.$pool.len(),
1196 );
1197
1198 let removed_from_subpool = $this.$pool.truncate_pool($this.config.$limit.clone());
1200
1201 trace!(
1202 target: "txpool",
1203 "removed {} transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1204 removed_from_subpool.len(),
1205 stringify!($pool),
1206 $this.config.$limit,
1207 $this.$pool.size(),
1208 $this.$pool.len()
1209 );
1210 $this.metrics.$metric.increment(removed_from_subpool.len() as u64);
1211
1212 for tx in removed_from_subpool {
1214 $this.all_transactions.remove_transaction(tx.id());
1215
1216 let id = *tx.id();
1217
1218 removed.push(tx);
1220
1221 $this.remove_descendants(&id, &mut $removed);
1223 }
1224 }
1225
1226 )*
1227 };
1228 }
1229
1230 discard_worst!(
1231 self, removed, [
1232 pending_limit => (pending_pool, pending_transactions_evicted),
1233 basefee_limit => (basefee_pool, basefee_transactions_evicted),
1234 blob_limit => (blob_pool, blob_transactions_evicted),
1235 queued_limit => (queued_pool, queued_transactions_evicted),
1236 ]
1237 );
1238
1239 removed
1240 }
1241
1242 pub(crate) fn len(&self) -> usize {
1244 self.all_transactions.len()
1245 }
1246
1247 pub(crate) fn is_empty(&self) -> bool {
1249 self.all_transactions.is_empty()
1250 }
1251
1252 #[cfg(any(test, feature = "test-utils"))]
1260 pub fn assert_invariants(&self) {
1261 let size = self.size();
1262 let actual = size.basefee + size.pending + size.queued + size.blob;
1263 assert_eq!(
1264 size.total, actual,
1265 "total size must be equal to the sum of all sub-pools, basefee:{}, pending:{}, queued:{}, blob:{}",
1266 size.basefee, size.pending, size.queued, size.blob
1267 );
1268 self.all_transactions.assert_invariants();
1269 self.pending_pool.assert_invariants();
1270 self.basefee_pool.assert_invariants();
1271 self.queued_pool.assert_invariants();
1272 self.blob_pool.assert_invariants();
1273 }
1274}
1275
1276#[cfg(any(test, feature = "test-utils"))]
1277impl TxPool<crate::test_utils::MockOrdering> {
1278 pub fn mock() -> Self {
1280 Self::new(crate::test_utils::MockOrdering::default(), PoolConfig::default())
1281 }
1282}
1283
1284#[cfg(test)]
1285impl<T: TransactionOrdering> Drop for TxPool<T> {
1286 fn drop(&mut self) {
1287 self.assert_invariants();
1288 }
1289}
1290
1291impl<T: TransactionOrdering> TxPool<T> {
1292 pub const fn pending(&self) -> &PendingPool<T> {
1294 &self.pending_pool
1295 }
1296
1297 pub const fn base_fee(&self) -> &ParkedPool<BasefeeOrd<T::Transaction>> {
1299 &self.basefee_pool
1300 }
1301
1302 pub const fn queued(&self) -> &ParkedPool<QueuedOrd<T::Transaction>> {
1304 &self.queued_pool
1305 }
1306}
1307
1308impl<T: TransactionOrdering> fmt::Debug for TxPool<T> {
1309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1310 f.debug_struct("TxPool").field("config", &self.config).finish_non_exhaustive()
1311 }
1312}
1313
1314pub(crate) struct AllTransactions<T: PoolTransaction> {
1319 minimal_protocol_basefee: u64,
1323 block_gas_limit: u64,
1325 max_account_slots: usize,
1327 by_hash: HashMap<TxHash, Arc<ValidPoolTransaction<T>>>,
1329 txs: BTreeMap<TransactionId, PoolInternalTransaction<T>>,
1331 tx_counter: FxHashMap<SenderId, usize>,
1333 last_seen_block_number: u64,
1335 last_seen_block_hash: B256,
1337 pending_fees: PendingFees,
1339 price_bumps: PriceBumpConfig,
1341 local_transactions_config: LocalTransactionConfig,
1343 auths: FxHashMap<SenderId, HashSet<TxHash>>,
1345 metrics: AllTransactionsMetrics,
1347}
1348
1349impl<T: PoolTransaction> AllTransactions<T> {
1350 fn new(config: &PoolConfig) -> Self {
1352 Self {
1353 max_account_slots: config.max_account_slots,
1354 price_bumps: config.price_bumps,
1355 local_transactions_config: config.local_transactions_config.clone(),
1356 minimal_protocol_basefee: config.minimal_protocol_basefee,
1357 block_gas_limit: config.gas_limit,
1358 ..Default::default()
1359 }
1360 }
1361
1362 #[expect(dead_code)]
1364 pub(crate) fn hashes_iter(&self) -> impl Iterator<Item = TxHash> + '_ {
1365 self.by_hash.keys().copied()
1366 }
1367
1368 pub(crate) fn transactions_iter(
1370 &self,
1371 ) -> impl Iterator<Item = &Arc<ValidPoolTransaction<T>>> + '_ {
1372 self.by_hash.values()
1373 }
1374
1375 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
1377 self.by_hash.contains_key(tx_hash)
1378 }
1379
1380 pub(crate) fn get(&self, id: &TransactionId) -> Option<&PoolInternalTransaction<T>> {
1382 self.txs.get(id)
1383 }
1384
1385 pub(crate) fn tx_inc(&mut self, sender: SenderId) {
1387 let count = self.tx_counter.entry(sender).or_default();
1388 *count += 1;
1389 self.metrics.all_transactions_by_all_senders.increment(1.0);
1390 }
1391
1392 pub(crate) fn tx_decr(&mut self, sender: SenderId) {
1394 if let hash_map::Entry::Occupied(mut entry) = self.tx_counter.entry(sender) {
1395 let count = entry.get_mut();
1396 if *count == 1 {
1397 entry.remove();
1398 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1399 return
1400 }
1401 *count -= 1;
1402 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1403 }
1404 }
1405
1406 fn set_block_info(&mut self, block_info: BlockInfo) {
1408 let BlockInfo {
1409 block_gas_limit,
1410 last_seen_block_hash,
1411 last_seen_block_number,
1412 pending_basefee,
1413 pending_blob_fee,
1414 } = block_info;
1415 self.last_seen_block_number = last_seen_block_number;
1416 self.last_seen_block_hash = last_seen_block_hash;
1417
1418 self.pending_fees.base_fee = pending_basefee;
1419 self.metrics.base_fee.set(pending_basefee as f64);
1420
1421 self.block_gas_limit = block_gas_limit;
1422
1423 if let Some(pending_blob_fee) = pending_blob_fee {
1424 self.pending_fees.blob_fee = pending_blob_fee;
1425 self.metrics.blob_base_fee.set(pending_blob_fee as f64);
1426 }
1427 }
1428
1429 pub(crate) fn update_size_metrics(&self) {
1431 self.metrics.all_transactions_by_hash.set(self.by_hash.len() as f64);
1432 self.metrics.all_transactions_by_id.set(self.txs.len() as f64);
1433 }
1434
1435 pub(crate) fn update(
1452 &mut self,
1453 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1454 ) -> Vec<PoolUpdate> {
1455 let mut updates = Vec::with_capacity(64);
1457
1458 let mut iter = self.txs.iter_mut().peekable();
1459
1460 'transactions: while let Some((id, tx)) = iter.next() {
1470 macro_rules! next_sender {
1471 ($iter:ident) => {
1472 'this: while let Some((peek, _)) = iter.peek() {
1473 if peek.sender != id.sender {
1474 break 'this
1475 }
1476 iter.next();
1477 }
1478 };
1479 }
1480
1481 let changed_balance = if let Some(info) = changed_accounts.get(&id.sender) {
1484 if id.nonce < info.state_nonce {
1486 updates.push(PoolUpdate {
1487 id: *tx.transaction.id(),
1488 current: tx.subpool,
1489 destination: Destination::Discard,
1490 });
1491 continue 'transactions
1492 }
1493
1494 let ancestor = TransactionId::ancestor(id.nonce, info.state_nonce, id.sender);
1495 if ancestor.is_none() {
1497 tx.state.insert(TxState::NO_NONCE_GAPS);
1498 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1499 tx.cumulative_cost = U256::ZERO;
1500 if tx.transaction.cost() > &info.balance {
1501 tx.state.remove(TxState::ENOUGH_BALANCE);
1503 } else {
1504 tx.state.insert(TxState::ENOUGH_BALANCE);
1505 }
1506 }
1507
1508 Some(&info.balance)
1509 } else {
1510 None
1511 };
1512
1513 if tx.state.has_nonce_gap() {
1515 next_sender!(iter);
1516 continue 'transactions
1517 }
1518
1519 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1521
1522 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1524 Self::record_subpool_update(&mut updates, tx);
1526
1527 let mut has_parked_ancestor = !tx.state.is_pending();
1529
1530 let mut cumulative_cost = tx.next_cumulative_cost();
1531
1532 let mut next_nonce_in_line = tx.transaction.nonce().saturating_add(1);
1534
1535 while let Some((peek, tx)) = iter.peek_mut() {
1537 if peek.sender != id.sender {
1538 continue 'transactions
1540 }
1541
1542 if tx.transaction.nonce() == next_nonce_in_line {
1543 tx.state.insert(TxState::NO_NONCE_GAPS);
1545 } else {
1546 next_sender!(iter);
1548 continue 'transactions
1549 }
1550
1551 next_nonce_in_line = next_nonce_in_line.saturating_add(1);
1553
1554 tx.cumulative_cost = cumulative_cost;
1556 cumulative_cost = tx.next_cumulative_cost();
1558
1559 if let Some(changed_balance) = changed_balance {
1561 if &cumulative_cost > changed_balance {
1562 tx.state.remove(TxState::ENOUGH_BALANCE);
1564 } else {
1565 tx.state.insert(TxState::ENOUGH_BALANCE);
1566 }
1567 }
1568
1569 if has_parked_ancestor {
1571 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1572 } else {
1573 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1574 }
1575 has_parked_ancestor = !tx.state.is_pending();
1576
1577 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1579 Self::record_subpool_update(&mut updates, tx);
1580
1581 iter.next();
1583 }
1584 }
1585
1586 updates
1587 }
1588
1589 fn record_subpool_update(updates: &mut Vec<PoolUpdate>, tx: &mut PoolInternalTransaction<T>) {
1594 let current_pool = tx.subpool;
1595 tx.subpool = tx.state.into();
1596 if current_pool != tx.subpool {
1597 updates.push(PoolUpdate {
1598 id: *tx.transaction.id(),
1599 current: current_pool,
1600 destination: tx.subpool.into(),
1601 })
1602 }
1603 }
1604
1605 fn update_tx_base_fee(pending_block_base_fee: u64, tx: &mut PoolInternalTransaction<T>) {
1607 match tx.transaction.max_fee_per_gas().cmp(&(pending_block_base_fee as u128)) {
1609 Ordering::Greater | Ordering::Equal => {
1610 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1611 }
1612 Ordering::Less => {
1613 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
1614 }
1615 }
1616 }
1617
1618 pub(crate) fn txs_iter(
1621 &self,
1622 sender: SenderId,
1623 ) -> impl Iterator<Item = (&TransactionId, &PoolInternalTransaction<T>)> + '_ {
1624 self.txs
1625 .range((sender.start_bound(), Unbounded))
1626 .take_while(move |(other, _)| sender == other.sender)
1627 }
1628
1629 #[cfg(test)]
1632 #[expect(dead_code)]
1633 pub(crate) fn txs_iter_mut(
1634 &mut self,
1635 sender: SenderId,
1636 ) -> impl Iterator<Item = (&TransactionId, &mut PoolInternalTransaction<T>)> + '_ {
1637 self.txs
1638 .range_mut((sender.start_bound(), Unbounded))
1639 .take_while(move |(other, _)| sender == other.sender)
1640 }
1641
1642 pub(crate) fn descendant_txs_exclusive<'a, 'b: 'a>(
1646 &'a self,
1647 id: &'b TransactionId,
1648 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1649 self.txs.range((Excluded(id), Unbounded)).take_while(|(other, _)| id.sender == other.sender)
1650 }
1651
1652 pub(crate) fn descendant_txs_inclusive<'a, 'b: 'a>(
1657 &'a self,
1658 id: &'b TransactionId,
1659 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1660 self.txs.range(id..).take_while(|(other, _)| id.sender == other.sender)
1661 }
1662
1663 pub(crate) fn descendant_txs_mut<'a, 'b: 'a>(
1668 &'a mut self,
1669 id: &'b TransactionId,
1670 ) -> impl Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)> + 'a {
1671 self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender)
1672 }
1673
1674 pub(crate) fn remove_transaction_by_hash(
1676 &mut self,
1677 tx_hash: &B256,
1678 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1679 let tx = self.by_hash.remove(tx_hash)?;
1680 let internal = self.txs.remove(&tx.transaction_id)?;
1681 self.remove_auths(&internal);
1682 self.tx_decr(tx.sender_id());
1684 Some((tx, internal.subpool))
1685 }
1686
1687 pub(crate) fn remove_transaction_by_id(
1691 &mut self,
1692 tx_id: &TransactionId,
1693 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1694 let internal = self.txs.remove(tx_id)?;
1695 let tx = self.by_hash.remove(internal.transaction.hash())?;
1696 self.remove_auths(&internal);
1697 self.tx_decr(tx.sender_id());
1699 Some((tx, internal.subpool))
1700 }
1701
1702 pub(crate) fn park_descendant_transactions(
1704 &mut self,
1705 tx_id: &TransactionId,
1706 ) -> Vec<PoolUpdate> {
1707 let mut updates = Vec::new();
1708
1709 for (id, tx) in self.descendant_txs_mut(tx_id) {
1710 let current_pool = tx.subpool;
1711
1712 tx.state.remove(TxState::NO_NONCE_GAPS);
1713
1714 tx.subpool = tx.state.into();
1716
1717 if current_pool != tx.subpool {
1719 updates.push(PoolUpdate {
1720 id: *id,
1721 current: current_pool,
1722 destination: tx.subpool.into(),
1723 })
1724 }
1725 }
1726
1727 updates
1728 }
1729
1730 pub(crate) fn remove_transaction(
1736 &mut self,
1737 id: &TransactionId,
1738 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1739 let internal = self.txs.remove(id)?;
1740
1741 self.tx_decr(internal.transaction.sender_id());
1743
1744 let result =
1745 self.by_hash.remove(internal.transaction.hash()).map(|tx| (tx, internal.subpool));
1746
1747 self.remove_auths(&internal);
1748
1749 result
1750 }
1751
1752 fn remove_auths(&mut self, tx: &PoolInternalTransaction<T>) {
1756 let Some(auths) = &tx.transaction.authority_ids else { return };
1757
1758 let tx_hash = tx.transaction.hash();
1759 for auth in auths {
1760 if let Some(list) = self.auths.get_mut(auth) {
1761 list.remove(tx_hash);
1762 if list.is_empty() {
1763 self.auths.remove(auth);
1764 }
1765 }
1766 }
1767 }
1768
1769 #[inline]
1775 fn contains_conflicting_transaction(&self, tx: &ValidPoolTransaction<T>) -> bool {
1776 self.txs_iter(tx.transaction_id.sender)
1777 .next()
1778 .is_some_and(|(_, existing)| tx.tx_type_conflicts_with(&existing.transaction))
1779 }
1780
1781 fn ensure_valid(
1790 &self,
1791 transaction: ValidPoolTransaction<T>,
1792 on_chain_nonce: u64,
1793 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1794 if !self.local_transactions_config.is_local(transaction.origin, transaction.sender_ref()) {
1795 let current_txs =
1796 self.tx_counter.get(&transaction.sender_id()).copied().unwrap_or_default();
1797
1798 if current_txs >= self.max_account_slots && transaction.nonce() > on_chain_nonce {
1801 return Err(InsertErr::ExceededSenderTransactionsCapacity {
1802 transaction: Arc::new(transaction),
1803 })
1804 }
1805 }
1806 if transaction.gas_limit() > self.block_gas_limit {
1807 return Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas {
1808 block_gas_limit: self.block_gas_limit,
1809 tx_gas_limit: transaction.gas_limit(),
1810 transaction: Arc::new(transaction),
1811 })
1812 }
1813
1814 if self.contains_conflicting_transaction(&transaction) {
1815 return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) })
1817 }
1818
1819 Ok(transaction)
1820 }
1821
1822 fn ensure_valid_blob_transaction(
1828 &self,
1829 new_blob_tx: ValidPoolTransaction<T>,
1830 on_chain_balance: U256,
1831 ancestor: Option<TransactionId>,
1832 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1833 if let Some(ancestor) = ancestor {
1834 let Some(ancestor_tx) = self.txs.get(&ancestor) else {
1835 self.metrics.blob_transactions_nonce_gaps.increment(1);
1837 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1838 };
1839 if ancestor_tx.state.has_nonce_gap() {
1840 self.metrics.blob_transactions_nonce_gaps.increment(1);
1843 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1844 }
1845
1846 let mut cumulative_cost = ancestor_tx.next_cumulative_cost() + new_blob_tx.cost();
1848
1849 if cumulative_cost > on_chain_balance {
1851 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1853 }
1854
1855 let id = new_blob_tx.transaction_id;
1858 let mut descendants = self.descendant_txs_inclusive(&id).peekable();
1859 if let Some((maybe_replacement, _)) = descendants.peek() {
1860 if **maybe_replacement == new_blob_tx.transaction_id {
1861 descendants.next();
1863
1864 for (_, tx) in descendants {
1866 cumulative_cost += tx.transaction.cost();
1867 if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance {
1868 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1870 }
1871 }
1872 }
1873 }
1874 } else if new_blob_tx.cost() > &on_chain_balance {
1875 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1877 }
1878
1879 Ok(new_blob_tx)
1880 }
1881
1882 pub(crate) fn insert_tx(
1914 &mut self,
1915 transaction: ValidPoolTransaction<T>,
1916 on_chain_balance: U256,
1917 on_chain_nonce: u64,
1918 ) -> InsertResult<T> {
1919 assert!(on_chain_nonce <= transaction.nonce(), "Invalid transaction");
1920
1921 let mut transaction = self.ensure_valid(transaction, on_chain_nonce)?;
1922
1923 let inserted_tx_id = *transaction.id();
1924 let mut state = TxState::default();
1925 let mut cumulative_cost = U256::ZERO;
1926 let mut updates = Vec::new();
1927
1928 state.insert(TxState::NOT_TOO_MUCH_GAS);
1930
1931 let ancestor = TransactionId::ancestor(
1934 transaction.transaction.nonce(),
1935 on_chain_nonce,
1936 inserted_tx_id.sender,
1937 );
1938
1939 if transaction.is_eip4844() {
1942 state.insert(TxState::BLOB_TRANSACTION);
1943
1944 transaction =
1945 self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
1946 let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();
1947 if blob_fee_cap >= self.pending_fees.blob_fee {
1948 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1949 }
1950 } else {
1951 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1953 }
1954
1955 let transaction = Arc::new(transaction);
1956
1957 if ancestor.is_none() {
1959 state.insert(TxState::NO_NONCE_GAPS);
1960 state.insert(TxState::NO_PARKED_ANCESTORS);
1961 }
1962
1963 let fee_cap = transaction.max_fee_per_gas();
1965
1966 if fee_cap < self.minimal_protocol_basefee as u128 {
1967 return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap })
1968 }
1969 if fee_cap >= self.pending_fees.base_fee as u128 {
1970 state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1971 }
1972
1973 let mut replaced_tx = None;
1975
1976 let pool_tx = PoolInternalTransaction {
1977 transaction: Arc::clone(&transaction),
1978 subpool: state.into(),
1979 state,
1980 cumulative_cost,
1981 };
1982
1983 match self.txs.entry(*transaction.id()) {
1985 Entry::Vacant(entry) => {
1986 self.by_hash.insert(*pool_tx.transaction.hash(), pool_tx.transaction.clone());
1988 entry.insert(pool_tx);
1989 }
1990 Entry::Occupied(mut entry) => {
1991 let existing_transaction = entry.get().transaction.as_ref();
1993 let maybe_replacement = transaction.as_ref();
1994
1995 if existing_transaction.is_underpriced(maybe_replacement, &self.price_bumps) {
1997 return Err(InsertErr::Underpriced {
1998 transaction: pool_tx.transaction,
1999 existing: *entry.get().transaction.hash(),
2000 })
2001 }
2002 let new_hash = *pool_tx.transaction.hash();
2003 let new_transaction = pool_tx.transaction.clone();
2004 let replaced = entry.insert(pool_tx);
2005 self.by_hash.remove(replaced.transaction.hash());
2006 self.by_hash.insert(new_hash, new_transaction);
2007
2008 self.remove_auths(&replaced);
2009
2010 replaced_tx = Some((replaced.transaction, replaced.subpool));
2012 }
2013 }
2014
2015 if let Some(auths) = &transaction.authority_ids {
2016 let tx_hash = transaction.hash();
2017 for auth in auths {
2018 self.auths.entry(*auth).or_default().insert(*tx_hash);
2019 }
2020 }
2021
2022 let on_chain_id = TransactionId::new(transaction.sender_id(), on_chain_nonce);
2024 {
2025 let mut next_nonce = on_chain_id.nonce;
2027
2028 let mut has_parked_ancestor = false;
2032
2033 for (id, tx) in self.descendant_txs_mut(&on_chain_id) {
2036 let current_pool = tx.subpool;
2037
2038 if next_nonce != id.nonce {
2040 break
2041 }
2042
2043 tx.state.insert(TxState::NO_NONCE_GAPS);
2045
2046 tx.cumulative_cost = cumulative_cost;
2048
2049 cumulative_cost = tx.next_cumulative_cost();
2051
2052 if cumulative_cost > on_chain_balance {
2053 tx.state.remove(TxState::ENOUGH_BALANCE);
2055 } else {
2056 tx.state.insert(TxState::ENOUGH_BALANCE);
2057 }
2058
2059 if has_parked_ancestor {
2061 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
2062 } else {
2063 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
2064 }
2065 has_parked_ancestor = !tx.state.is_pending();
2066
2067 tx.subpool = tx.state.into();
2069
2070 if inserted_tx_id.eq(id) {
2071 state = tx.state;
2073 } else {
2074 if current_pool != tx.subpool {
2076 updates.push(PoolUpdate {
2077 id: *id,
2078 current: current_pool,
2079 destination: tx.subpool.into(),
2080 })
2081 }
2082 }
2083
2084 next_nonce = id.next_nonce();
2086 }
2087 }
2088
2089 if replaced_tx.is_none() {
2091 self.tx_inc(inserted_tx_id.sender);
2092 }
2093
2094 self.update_size_metrics();
2095
2096 Ok(InsertOk { transaction, move_to: state.into(), state, replaced_tx, updates })
2097 }
2098
2099 pub(crate) fn len(&self) -> usize {
2101 self.txs.len()
2102 }
2103
2104 pub(crate) fn is_empty(&self) -> bool {
2106 self.txs.is_empty()
2107 }
2108
2109 #[cfg(any(test, feature = "test-utils"))]
2111 pub(crate) fn assert_invariants(&self) {
2112 assert_eq!(self.by_hash.len(), self.txs.len(), "by_hash.len() != txs.len()");
2113 assert!(self.auths.len() <= self.txs.len(), "auths > txs.len()");
2114 }
2115}
2116
2117#[cfg(test)]
2118impl<T: PoolTransaction> AllTransactions<T> {
2119 pub(crate) fn tx_count(&self, sender: SenderId) -> usize {
2123 self.tx_counter.get(&sender).copied().unwrap_or_default()
2124 }
2125}
2126
2127impl<T: PoolTransaction> Default for AllTransactions<T> {
2128 fn default() -> Self {
2129 Self {
2130 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
2131 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
2132 block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
2133 by_hash: Default::default(),
2134 txs: Default::default(),
2135 tx_counter: Default::default(),
2136 last_seen_block_number: Default::default(),
2137 last_seen_block_hash: Default::default(),
2138 pending_fees: Default::default(),
2139 price_bumps: Default::default(),
2140 local_transactions_config: Default::default(),
2141 auths: Default::default(),
2142 metrics: Default::default(),
2143 }
2144 }
2145}
2146
2147#[derive(Debug, Clone)]
2149pub(crate) struct PendingFees {
2150 pub(crate) base_fee: u64,
2152 pub(crate) blob_fee: u128,
2154}
2155
2156impl Default for PendingFees {
2157 fn default() -> Self {
2158 Self { base_fee: Default::default(), blob_fee: BLOB_TX_MIN_BLOB_GASPRICE }
2159 }
2160}
2161
2162pub(crate) type InsertResult<T> = Result<InsertOk<T>, InsertErr<T>>;
2164
2165#[derive(Debug)]
2167pub(crate) enum InsertErr<T: PoolTransaction> {
2168 Underpriced {
2170 transaction: Arc<ValidPoolTransaction<T>>,
2171 #[expect(dead_code)]
2172 existing: TxHash,
2173 },
2174 BlobTxHasNonceGap { transaction: Arc<ValidPoolTransaction<T>> },
2176 Overdraft { transaction: Arc<ValidPoolTransaction<T>> },
2179 FeeCapBelowMinimumProtocolFeeCap { transaction: Arc<ValidPoolTransaction<T>>, fee_cap: u128 },
2183 ExceededSenderTransactionsCapacity { transaction: Arc<ValidPoolTransaction<T>> },
2187 TxGasLimitMoreThanAvailableBlockGas {
2189 transaction: Arc<ValidPoolTransaction<T>>,
2190 block_gas_limit: u64,
2191 tx_gas_limit: u64,
2192 },
2193 TxTypeConflict { transaction: Arc<ValidPoolTransaction<T>> },
2195}
2196
2197#[derive(Debug)]
2199pub(crate) struct InsertOk<T: PoolTransaction> {
2200 transaction: Arc<ValidPoolTransaction<T>>,
2202 move_to: SubPool,
2204 state: TxState,
2206 replaced_tx: Option<(Arc<ValidPoolTransaction<T>>, SubPool)>,
2208 updates: Vec<PoolUpdate>,
2210}
2211
2212#[derive(Debug)]
2215pub(crate) struct PoolInternalTransaction<T: PoolTransaction> {
2216 pub(crate) transaction: Arc<ValidPoolTransaction<T>>,
2218 pub(crate) subpool: SubPool,
2220 pub(crate) state: TxState,
2223 pub(crate) cumulative_cost: U256,
2228}
2229
2230impl<T: PoolTransaction> PoolInternalTransaction<T> {
2233 fn next_cumulative_cost(&self) -> U256 {
2234 self.cumulative_cost + self.transaction.cost()
2235 }
2236}
2237
2238#[derive(Debug, Clone, Default)]
2240pub(crate) struct SenderInfo {
2241 pub(crate) state_nonce: u64,
2243 pub(crate) balance: U256,
2245}
2246
2247impl SenderInfo {
2250 const fn update(&mut self, state_nonce: u64, balance: U256) {
2252 *self = Self { state_nonce, balance };
2253 }
2254}
2255
2256#[cfg(test)]
2257mod tests {
2258 use super::*;
2259 use crate::{
2260 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
2261 traits::TransactionOrigin,
2262 SubPoolLimit,
2263 };
2264 use alloy_consensus::{Transaction, TxType};
2265 use alloy_primitives::address;
2266
2267 #[test]
2268 fn test_insert_blob() {
2269 let on_chain_balance = U256::MAX;
2270 let on_chain_nonce = 0;
2271 let mut f = MockTransactionFactory::default();
2272 let mut pool = AllTransactions::default();
2273 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2274 let valid_tx = f.validated(tx);
2275 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2276 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2277 assert!(updates.is_empty());
2278 assert!(replaced_tx.is_none());
2279 assert!(state.contains(TxState::NO_NONCE_GAPS));
2280 assert!(state.contains(TxState::ENOUGH_BALANCE));
2281 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2282 assert_eq!(move_to, SubPool::Pending);
2283
2284 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2285 assert_eq!(inserted.subpool, SubPool::Pending);
2286 }
2287
2288 #[test]
2289 fn test_insert_blob_not_enough_blob_fee() {
2290 let on_chain_balance = U256::MAX;
2291 let on_chain_nonce = 0;
2292 let mut f = MockTransactionFactory::default();
2293 let mut pool = AllTransactions {
2294 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2295 ..Default::default()
2296 };
2297 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2298 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2299 let valid_tx = f.validated(tx);
2300 let InsertOk { state, .. } =
2301 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2302 assert!(state.contains(TxState::NO_NONCE_GAPS));
2303 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2304
2305 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2306 }
2307
2308 #[test]
2309 fn test_valid_tx_with_decreasing_blob_fee() {
2310 let on_chain_balance = U256::MAX;
2311 let on_chain_nonce = 0;
2312 let mut f = MockTransactionFactory::default();
2313 let mut pool = AllTransactions {
2314 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2315 ..Default::default()
2316 };
2317 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2318
2319 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2320 let valid_tx = f.validated(tx.clone());
2321 let InsertOk { state, .. } =
2322 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2323 assert!(state.contains(TxState::NO_NONCE_GAPS));
2324 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2325
2326 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2327 pool.remove_transaction(&valid_tx.transaction_id);
2328
2329 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
2330 let InsertOk { state, .. } =
2331 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2332 assert!(state.contains(TxState::NO_NONCE_GAPS));
2333 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2334 }
2335
2336 #[test]
2337 fn test_demote_valid_tx_with_increasing_blob_fee() {
2338 let on_chain_balance = U256::MAX;
2339 let on_chain_nonce = 0;
2340 let mut f = MockTransactionFactory::default();
2341 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2342 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2343
2344 let mut block_info = pool.block_info();
2346 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2347 pool.set_block_info(block_info);
2348
2349 let validated = f.validated(tx.clone());
2350 let id = *validated.id();
2351 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2352
2353 assert!(pool.blob_pool.is_empty());
2355 assert_eq!(pool.pending_pool.len(), 1);
2356
2357 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2359 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2360 assert_eq!(internal_tx.subpool, SubPool::Pending);
2361
2362 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2364 pool.set_block_info(block_info);
2365
2366 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2368 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2369 assert_eq!(internal_tx.subpool, SubPool::Blob);
2370
2371 assert_eq!(pool.blob_pool.len(), 1);
2373 assert!(pool.pending_pool.is_empty());
2374 }
2375
2376 #[test]
2377 fn test_promote_valid_tx_with_decreasing_blob_fee() {
2378 let on_chain_balance = U256::MAX;
2379 let on_chain_nonce = 0;
2380 let mut f = MockTransactionFactory::default();
2381 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2382 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2383
2384 let mut block_info = pool.block_info();
2386 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2387 pool.set_block_info(block_info);
2388
2389 let validated = f.validated(tx.clone());
2390 let id = *validated.id();
2391 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2392
2393 assert!(pool.pending_pool.is_empty());
2395 assert_eq!(pool.blob_pool.len(), 1);
2396
2397 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2399 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2400 assert_eq!(internal_tx.subpool, SubPool::Blob);
2401
2402 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2404 pool.set_block_info(block_info);
2405
2406 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2408 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2409 assert_eq!(internal_tx.subpool, SubPool::Pending);
2410
2411 assert_eq!(pool.pending_pool.len(), 1);
2413 assert!(pool.blob_pool.is_empty());
2414 }
2415
2416 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
2418 struct PromotionTest {
2419 basefee: u64,
2421 blobfee: u128,
2423 subpool: SubPool,
2425 basefee_update: u64,
2427 blobfee_update: u128,
2429 new_subpool: SubPool,
2431 }
2432
2433 impl PromotionTest {
2434 const fn opposite(&self) -> Self {
2436 Self {
2437 basefee: self.basefee_update,
2438 blobfee: self.blobfee_update,
2439 subpool: self.new_subpool,
2440 blobfee_update: self.blobfee,
2441 basefee_update: self.basefee,
2442 new_subpool: self.subpool,
2443 }
2444 }
2445
2446 fn assert_subpool_lengths<T: TransactionOrdering>(
2447 &self,
2448 pool: &TxPool<T>,
2449 failure_message: String,
2450 check_subpool: SubPool,
2451 ) {
2452 match check_subpool {
2453 SubPool::Blob => {
2454 assert_eq!(pool.blob_pool.len(), 1, "{failure_message}");
2455 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2456 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2457 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2458 }
2459 SubPool::Pending => {
2460 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2461 assert_eq!(pool.pending_pool.len(), 1, "{failure_message}");
2462 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2463 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2464 }
2465 SubPool::BaseFee => {
2466 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2467 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2468 assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}");
2469 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2470 }
2471 SubPool::Queued => {
2472 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2473 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2474 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2475 assert_eq!(pool.queued_pool.len(), 1, "{failure_message}");
2476 }
2477 }
2478 }
2479
2480 fn assert_single_tx_starting_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2484 self.assert_subpool_lengths(
2485 pool,
2486 format!("pool length check failed at start of test: {self:?}"),
2487 self.subpool,
2488 );
2489 }
2490
2491 fn assert_single_tx_ending_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2495 self.assert_subpool_lengths(
2496 pool,
2497 format!("pool length check failed at end of test: {self:?}"),
2498 self.new_subpool,
2499 );
2500 }
2501 }
2502
2503 #[test]
2504 fn test_promote_blob_tx_with_both_pending_fee_updates() {
2505 let on_chain_balance = U256::MAX;
2508 let on_chain_nonce = 0;
2509 let mut f = MockTransactionFactory::default();
2510 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2511
2512 let max_fee_per_blob_gas = tx.max_fee_per_blob_gas().unwrap();
2513 let max_fee_per_gas = tx.max_fee_per_gas() as u64;
2514
2515 let mut expected_promotions = vec![
2517 PromotionTest {
2518 blobfee: max_fee_per_blob_gas + 1,
2519 basefee: max_fee_per_gas + 1,
2520 subpool: SubPool::Blob,
2521 blobfee_update: max_fee_per_blob_gas + 1,
2522 basefee_update: max_fee_per_gas + 1,
2523 new_subpool: SubPool::Blob,
2524 },
2525 PromotionTest {
2526 blobfee: max_fee_per_blob_gas + 1,
2527 basefee: max_fee_per_gas + 1,
2528 subpool: SubPool::Blob,
2529 blobfee_update: max_fee_per_blob_gas,
2530 basefee_update: max_fee_per_gas + 1,
2531 new_subpool: SubPool::Blob,
2532 },
2533 PromotionTest {
2534 blobfee: max_fee_per_blob_gas + 1,
2535 basefee: max_fee_per_gas + 1,
2536 subpool: SubPool::Blob,
2537 blobfee_update: max_fee_per_blob_gas + 1,
2538 basefee_update: max_fee_per_gas,
2539 new_subpool: SubPool::Blob,
2540 },
2541 PromotionTest {
2542 blobfee: max_fee_per_blob_gas + 1,
2543 basefee: max_fee_per_gas + 1,
2544 subpool: SubPool::Blob,
2545 blobfee_update: max_fee_per_blob_gas,
2546 basefee_update: max_fee_per_gas,
2547 new_subpool: SubPool::Pending,
2548 },
2549 PromotionTest {
2550 blobfee: max_fee_per_blob_gas,
2551 basefee: max_fee_per_gas + 1,
2552 subpool: SubPool::Blob,
2553 blobfee_update: max_fee_per_blob_gas,
2554 basefee_update: max_fee_per_gas,
2555 new_subpool: SubPool::Pending,
2556 },
2557 PromotionTest {
2558 blobfee: max_fee_per_blob_gas + 1,
2559 basefee: max_fee_per_gas,
2560 subpool: SubPool::Blob,
2561 blobfee_update: max_fee_per_blob_gas,
2562 basefee_update: max_fee_per_gas,
2563 new_subpool: SubPool::Pending,
2564 },
2565 PromotionTest {
2566 blobfee: max_fee_per_blob_gas,
2567 basefee: max_fee_per_gas,
2568 subpool: SubPool::Pending,
2569 blobfee_update: max_fee_per_blob_gas,
2570 basefee_update: max_fee_per_gas,
2571 new_subpool: SubPool::Pending,
2572 },
2573 ];
2574
2575 let reversed = expected_promotions.iter().map(|test| test.opposite()).collect::<Vec<_>>();
2577 expected_promotions.extend(reversed);
2578
2579 let expected_promotions = expected_promotions.into_iter().collect::<HashSet<_>>();
2581
2582 for promotion_test in &expected_promotions {
2583 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2584
2585 let mut block_info = pool.block_info();
2587
2588 block_info.pending_blob_fee = Some(promotion_test.blobfee);
2589 block_info.pending_basefee = promotion_test.basefee;
2590 pool.set_block_info(block_info);
2591
2592 let validated = f.validated(tx.clone());
2593 let id = *validated.id();
2594 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2595
2596 promotion_test.assert_single_tx_starting_subpool(&pool);
2598
2599 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2601 assert_eq!(
2602 internal_tx.subpool, promotion_test.subpool,
2603 "Subpools do not match at start of test: {promotion_test:?}"
2604 );
2605
2606 block_info.pending_basefee = promotion_test.basefee_update;
2608 block_info.pending_blob_fee = Some(promotion_test.blobfee_update);
2609 pool.set_block_info(block_info);
2610
2611 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2613 assert_eq!(
2614 internal_tx.subpool, promotion_test.new_subpool,
2615 "Subpools do not match at end of test: {promotion_test:?}"
2616 );
2617
2618 promotion_test.assert_single_tx_ending_subpool(&pool);
2620 }
2621 }
2622
2623 #[test]
2624 fn test_insert_pending() {
2625 let on_chain_balance = U256::MAX;
2626 let on_chain_nonce = 0;
2627 let mut f = MockTransactionFactory::default();
2628 let mut pool = AllTransactions::default();
2629 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2630 let valid_tx = f.validated(tx);
2631 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2632 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2633 assert!(updates.is_empty());
2634 assert!(replaced_tx.is_none());
2635 assert!(state.contains(TxState::NO_NONCE_GAPS));
2636 assert!(state.contains(TxState::ENOUGH_BALANCE));
2637 assert_eq!(move_to, SubPool::Pending);
2638
2639 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2640 assert_eq!(inserted.subpool, SubPool::Pending);
2641 }
2642
2643 #[test]
2644 fn test_simple_insert() {
2645 let on_chain_balance = U256::ZERO;
2646 let on_chain_nonce = 0;
2647 let mut f = MockTransactionFactory::default();
2648 let mut pool = AllTransactions::default();
2649 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2650 tx.set_priority_fee(100);
2651 tx.set_max_fee(100);
2652 let valid_tx = f.validated(tx.clone());
2653 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2654 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2655 assert!(updates.is_empty());
2656 assert!(replaced_tx.is_none());
2657 assert!(state.contains(TxState::NO_NONCE_GAPS));
2658 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2659 assert_eq!(move_to, SubPool::Queued);
2660
2661 assert_eq!(pool.len(), 1);
2662 assert!(pool.contains(valid_tx.hash()));
2663 let expected_state = TxState::ENOUGH_FEE_CAP_BLOCK | TxState::NO_NONCE_GAPS;
2664 let inserted = pool.get(valid_tx.id()).unwrap();
2665 assert!(inserted.state.intersects(expected_state));
2666
2667 let res = pool.insert_tx(valid_tx, on_chain_balance, on_chain_nonce);
2669 res.unwrap_err();
2670 assert_eq!(pool.len(), 1);
2671
2672 let valid_tx = f.validated(tx.next());
2673 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2674 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2675
2676 assert!(updates.is_empty());
2677 assert!(replaced_tx.is_none());
2678 assert!(state.contains(TxState::NO_NONCE_GAPS));
2679 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2680 assert_eq!(move_to, SubPool::Queued);
2681
2682 assert!(pool.contains(valid_tx.hash()));
2683 assert_eq!(pool.len(), 2);
2684 let inserted = pool.get(valid_tx.id()).unwrap();
2685 assert!(inserted.state.intersects(expected_state));
2686 }
2687
2688 #[test]
2689 fn test_on_canonical_state_change_no_double_processing() {
2692 let mut tx_factory = MockTransactionFactory::default();
2693 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2694
2695 let tx = MockTransaction::eip1559().with_gas_price(50).with_gas_limit(30_000);
2697 let sender = tx.sender();
2698
2699 let mut block_info = pool.block_info();
2701 block_info.pending_basefee = 100;
2702 pool.set_block_info(block_info);
2703
2704 let validated = tx_factory.validated(tx);
2705 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2706
2707 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2709
2710 assert_eq!(pool.basefee_pool.len(), 1);
2711 assert_eq!(pool.pending_pool.len(), 0);
2712
2713 block_info.pending_basefee = 40;
2717
2718 let mut changed_senders = FxHashMap::default();
2719 changed_senders.insert(
2720 sender_id,
2721 SenderInfo {
2722 state_nonce: 0,
2723 balance: U256::from(20_000_000), },
2725 );
2726
2727 let outcome = pool.on_canonical_state_change(
2728 block_info,
2729 vec![], changed_senders,
2731 PoolUpdateKind::Commit,
2732 );
2733
2734 assert_eq!(pool.pending_pool.len(), 1, "Transaction should be in pending pool");
2736 assert_eq!(pool.basefee_pool.len(), 0, "Transaction should not be in basefee pool");
2737 assert_eq!(outcome.promoted.len(), 1, "Should report exactly one promotion");
2738 }
2739
2740 #[test]
2741 fn test_canonical_state_change_with_basefee_update_regression() {
2744 let mut tx_factory = MockTransactionFactory::default();
2745 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2746
2747 let sender_balance = U256::from(100_000_000);
2749
2750 let tx1 =
2752 MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000).with_nonce(0);
2753 let sender1 = tx1.sender();
2754
2755 let tx2 =
2757 MockTransaction::eip1559().with_gas_price(55).with_gas_limit(21_000).with_nonce(0);
2758 let sender2 = tx2.sender();
2759
2760 let tx3 =
2762 MockTransaction::eip1559().with_gas_price(45).with_gas_limit(21_000).with_nonce(0);
2763 let sender3 = tx3.sender();
2764
2765 let mut block_info = pool.block_info();
2767 block_info.pending_basefee = 70;
2768 pool.set_block_info(block_info);
2769
2770 let validated1 = tx_factory.validated(tx1);
2772 let validated2 = tx_factory.validated(tx2);
2773 let validated3 = tx_factory.validated(tx3);
2774
2775 pool.add_transaction(validated1, sender_balance, 0, None).unwrap();
2776 pool.add_transaction(validated2, sender_balance, 0, None).unwrap();
2777 pool.add_transaction(validated3, sender_balance, 0, None).unwrap();
2778
2779 let sender1_id = tx_factory.ids.sender_id(&sender1).unwrap();
2780 let sender2_id = tx_factory.ids.sender_id(&sender2).unwrap();
2781 let sender3_id = tx_factory.ids.sender_id(&sender3).unwrap();
2782
2783 assert_eq!(pool.basefee_pool.len(), 3, "All txs should be in basefee pool");
2785 assert_eq!(pool.pending_pool.len(), 0, "No txs should be in pending pool");
2786
2787 block_info.pending_basefee = 50;
2790
2791 let mut changed_senders = FxHashMap::default();
2793 changed_senders.insert(
2794 sender1_id,
2795 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2796 );
2797 changed_senders.insert(
2798 sender2_id,
2799 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2800 );
2801 changed_senders.insert(
2802 sender3_id,
2803 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2804 );
2805
2806 let outcome = pool.on_canonical_state_change(
2807 block_info,
2808 vec![],
2809 changed_senders,
2810 PoolUpdateKind::Commit,
2811 );
2812
2813 assert_eq!(pool.pending_pool.len(), 2, "tx1 and tx2 should be promoted");
2815 assert_eq!(pool.basefee_pool.len(), 1, "tx3 should remain in basefee");
2816
2817 assert_eq!(
2820 outcome.promoted.len(),
2821 2,
2822 "Should report exactly 2 promotions, not double-counted"
2823 );
2824
2825 let promoted_prices: Vec<u128> =
2827 outcome.promoted.iter().map(|tx| tx.max_fee_per_gas()).collect();
2828 assert!(promoted_prices.contains(&60));
2829 assert!(promoted_prices.contains(&55));
2830 }
2831
2832 #[test]
2833 fn test_basefee_decrease_with_empty_senders() {
2834 let mut tx_factory = MockTransactionFactory::default();
2837 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2838
2839 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2841
2842 let mut block_info = pool.block_info();
2844 block_info.pending_basefee = 100;
2845 pool.set_block_info(block_info);
2846
2847 let validated = tx_factory.validated(tx);
2849 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2850
2851 assert_eq!(pool.basefee_pool.len(), 1);
2852 assert_eq!(pool.pending_pool.len(), 0);
2853
2854 block_info.pending_basefee = 50;
2856 let outcome = pool.on_canonical_state_change(
2857 block_info,
2858 vec![],
2859 FxHashMap::default(), PoolUpdateKind::Commit,
2861 );
2862
2863 assert_eq!(pool.pending_pool.len(), 1, "Fee decrease should promote tx");
2865 assert_eq!(pool.basefee_pool.len(), 0);
2866 assert_eq!(outcome.promoted.len(), 1, "Should report promotion from fee update");
2867 }
2868
2869 #[test]
2870 fn test_basefee_decrease_account_makes_unfundable() {
2871 let mut tx_factory = MockTransactionFactory::default();
2874 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2875
2876 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2877 let sender = tx.sender();
2878
2879 let mut block_info = pool.block_info();
2881 block_info.pending_basefee = 100;
2882 pool.set_block_info(block_info);
2883
2884 let validated = tx_factory.validated(tx);
2885 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2886 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2887
2888 assert_eq!(pool.basefee_pool.len(), 1);
2889
2890 block_info.pending_basefee = 50;
2892 let mut changed_senders = FxHashMap::default();
2893 changed_senders.insert(
2894 sender_id,
2895 SenderInfo {
2896 state_nonce: 0,
2897 balance: U256::from(100), },
2899 );
2900
2901 let outcome = pool.on_canonical_state_change(
2902 block_info,
2903 vec![],
2904 changed_senders,
2905 PoolUpdateKind::Commit,
2906 );
2907
2908 assert_eq!(pool.pending_pool.len(), 0, "Unfunded tx should not be in pending");
2910 assert_eq!(pool.basefee_pool.len(), 0, "Tx no longer in basefee pool");
2911 assert_eq!(pool.queued_pool.len(), 1, "Unfunded tx should be in queued pool");
2912
2913 let tx_count = pool.all_transactions.txs.len();
2915 assert_eq!(tx_count, 1, "Transaction should still be in pool (in queued)");
2916
2917 assert_eq!(outcome.promoted.len(), 0, "Should not report promotion");
2918 assert_eq!(outcome.discarded.len(), 0, "Queued tx is not reported as discarded");
2919 }
2920
2921 #[test]
2922 fn insert_already_imported() {
2923 let on_chain_balance = U256::ZERO;
2924 let on_chain_nonce = 0;
2925 let mut f = MockTransactionFactory::default();
2926 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2927 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2928 let tx = f.validated(tx);
2929 pool.add_transaction(tx.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
2930 match pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap_err().kind {
2931 PoolErrorKind::AlreadyImported => {}
2932 _ => unreachable!(),
2933 }
2934 }
2935
2936 #[test]
2937 fn insert_replace() {
2938 let on_chain_balance = U256::ZERO;
2939 let on_chain_nonce = 0;
2940 let mut f = MockTransactionFactory::default();
2941 let mut pool = AllTransactions::default();
2942 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2943 let first = f.validated(tx.clone());
2944 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
2945 let replacement = f.validated(tx.rng_hash().inc_price());
2946 let InsertOk { updates, replaced_tx, .. } =
2947 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
2948 assert!(updates.is_empty());
2949 let replaced = replaced_tx.unwrap();
2950 assert_eq!(replaced.0.hash(), first.hash());
2951
2952 assert!(!pool.contains(first.hash()));
2954 assert!(pool.contains(replacement.hash()));
2955 assert_eq!(pool.len(), 1);
2956 }
2957
2958 #[test]
2959 fn insert_replace_txpool() {
2960 let on_chain_balance = U256::ZERO;
2961 let on_chain_nonce = 0;
2962 let mut f = MockTransactionFactory::default();
2963 let mut pool = TxPool::mock();
2964
2965 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2966 let first = f.validated(tx.clone());
2967 let first_added =
2968 pool.add_transaction(first, on_chain_balance, on_chain_nonce, None).unwrap();
2969 let replacement = f.validated(tx.rng_hash().inc_price());
2970 let replacement_added = pool
2971 .add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce, None)
2972 .unwrap();
2973
2974 assert!(!pool.contains(first_added.hash()));
2976 assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));
2978
2979 assert!(pool.contains(replacement.hash()));
2980 let size = pool.size();
2981 assert_eq!(size.total, 1);
2982 size.assert_invariants();
2983 }
2984
2985 #[test]
2986 fn insert_replace_underpriced() {
2987 let on_chain_balance = U256::ZERO;
2988 let on_chain_nonce = 0;
2989 let mut f = MockTransactionFactory::default();
2990 let mut pool = AllTransactions::default();
2991 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2992 let first = f.validated(tx.clone());
2993 let _res = pool.insert_tx(first, on_chain_balance, on_chain_nonce);
2994 let mut replacement = f.validated(tx.rng_hash());
2995 replacement.transaction = replacement.transaction.decr_price();
2996 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
2997 assert!(matches!(err, InsertErr::Underpriced { .. }));
2998 }
2999
3000 #[test]
3001 fn insert_replace_underpriced_not_enough_bump() {
3002 let on_chain_balance = U256::ZERO;
3003 let on_chain_nonce = 0;
3004 let mut f = MockTransactionFactory::default();
3005 let mut pool = AllTransactions::default();
3006 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3007 tx.set_priority_fee(100);
3008 tx.set_max_fee(100);
3009 let first = f.validated(tx.clone());
3010 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3011 let mut replacement = f.validated(tx.rng_hash().inc_price());
3012
3013 replacement.transaction.set_priority_fee(109);
3015 replacement.transaction.set_max_fee(109);
3016 let err =
3017 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3018 assert!(matches!(err, InsertErr::Underpriced { .. }));
3019 assert!(pool.contains(first.hash()));
3021 assert_eq!(pool.len(), 1);
3022
3023 replacement.transaction.set_priority_fee(110);
3025 replacement.transaction.set_max_fee(109);
3026 let err =
3027 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3028 assert!(matches!(err, InsertErr::Underpriced { .. }));
3029 assert!(pool.contains(first.hash()));
3030 assert_eq!(pool.len(), 1);
3031
3032 replacement.transaction.set_priority_fee(109);
3034 replacement.transaction.set_max_fee(110);
3035 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3036 assert!(matches!(err, InsertErr::Underpriced { .. }));
3037 assert!(pool.contains(first.hash()));
3038 assert_eq!(pool.len(), 1);
3039 }
3040
3041 #[test]
3042 fn insert_conflicting_type_normal_to_blob() {
3043 let on_chain_balance = U256::from(10_000);
3044 let on_chain_nonce = 0;
3045 let mut f = MockTransactionFactory::default();
3046 let mut pool = AllTransactions::default();
3047 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3048 let first = f.validated(tx.clone());
3049 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3050 let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3051 let blob = f.validated(tx);
3052 let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
3053 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3054 }
3055
3056 #[test]
3057 fn insert_conflicting_type_blob_to_normal() {
3058 let on_chain_balance = U256::from(10_000);
3059 let on_chain_nonce = 0;
3060 let mut f = MockTransactionFactory::default();
3061 let mut pool = AllTransactions::default();
3062 let tx = MockTransaction::eip4844().inc_price().inc_limit();
3063 let first = f.validated(tx.clone());
3064 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3065 let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3066 let tx = f.validated(tx);
3067 let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
3068 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3069 }
3070
3071 #[test]
3073 fn insert_previous() {
3074 let on_chain_balance = U256::ZERO;
3075 let on_chain_nonce = 0;
3076 let mut f = MockTransactionFactory::default();
3077 let mut pool = AllTransactions::default();
3078 let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
3079 let first = f.validated(tx.clone());
3080 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3081
3082 let first_in_pool = pool.get(first.id()).unwrap();
3083
3084 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3086
3087 let prev = f.validated(tx.prev());
3088 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3089 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3090
3091 assert!(updates.is_empty());
3093 assert!(replaced_tx.is_none());
3094 assert!(state.contains(TxState::NO_NONCE_GAPS));
3095 assert_eq!(move_to, SubPool::Queued);
3096
3097 let first_in_pool = pool.get(first.id()).unwrap();
3098 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3100 }
3101
3102 #[test]
3104 fn insert_with_updates() {
3105 let on_chain_balance = U256::from(10_000);
3106 let on_chain_nonce = 0;
3107 let mut f = MockTransactionFactory::default();
3108 let mut pool = AllTransactions::default();
3109 let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
3110 let first = f.validated(tx.clone());
3111 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3112
3113 let first_in_pool = pool.get(first.id()).unwrap();
3114 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3116 assert_eq!(SubPool::Queued, first_in_pool.subpool);
3117
3118 let prev = f.validated(tx.prev());
3119 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3120 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3121
3122 assert_eq!(updates.len(), 1);
3124 assert!(replaced_tx.is_none());
3125 assert!(state.contains(TxState::NO_NONCE_GAPS));
3126 assert_eq!(move_to, SubPool::Pending);
3127
3128 let first_in_pool = pool.get(first.id()).unwrap();
3129 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3131 assert_eq!(SubPool::Pending, first_in_pool.subpool);
3132 }
3133
3134 #[test]
3135 fn insert_previous_blocking() {
3136 let on_chain_balance = U256::from(1_000);
3137 let on_chain_nonce = 0;
3138 let mut f = MockTransactionFactory::default();
3139 let mut pool = AllTransactions::default();
3140 pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
3141 let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
3142 let first = f.validated(tx.clone());
3143
3144 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3145
3146 let first_in_pool = pool.get(first.id()).unwrap();
3147
3148 assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
3149 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3151
3152 let prev = f.validated(tx.prev());
3153 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3154 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3155
3156 assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3157 assert!(updates.is_empty());
3159 assert!(replaced_tx.is_none());
3160 assert!(state.contains(TxState::NO_NONCE_GAPS));
3161 assert_eq!(move_to, SubPool::BaseFee);
3162
3163 let first_in_pool = pool.get(first.id()).unwrap();
3164 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3166 }
3167
3168 #[test]
3169 fn rejects_spammer() {
3170 let on_chain_balance = U256::from(1_000);
3171 let on_chain_nonce = 0;
3172 let mut f = MockTransactionFactory::default();
3173 let mut pool = AllTransactions::default();
3174
3175 let mut tx = MockTransaction::eip1559();
3176 let unblocked_tx = tx.clone();
3177 for _ in 0..pool.max_account_slots {
3178 tx = tx.next();
3179 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
3180 }
3181
3182 assert_eq!(
3183 pool.max_account_slots,
3184 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3185 );
3186
3187 let err =
3188 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
3189 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
3190
3191 assert!(pool
3192 .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
3193 .is_ok());
3194 }
3195
3196 #[test]
3197 fn allow_local_spamming() {
3198 let on_chain_balance = U256::from(1_000);
3199 let on_chain_nonce = 0;
3200 let mut f = MockTransactionFactory::default();
3201 let mut pool = AllTransactions::default();
3202
3203 let mut tx = MockTransaction::eip1559();
3204 for _ in 0..pool.max_account_slots {
3205 tx = tx.next();
3206 pool.insert_tx(
3207 f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
3208 on_chain_balance,
3209 on_chain_nonce,
3210 )
3211 .unwrap();
3212 }
3213
3214 assert_eq!(
3215 pool.max_account_slots,
3216 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3217 );
3218
3219 pool.insert_tx(
3220 f.validated_with_origin(TransactionOrigin::Local, tx.next()),
3221 on_chain_balance,
3222 on_chain_nonce,
3223 )
3224 .unwrap();
3225 }
3226
3227 #[test]
3228 fn reject_tx_over_gas_limit() {
3229 let on_chain_balance = U256::from(1_000);
3230 let on_chain_nonce = 0;
3231 let mut f = MockTransactionFactory::default();
3232 let mut pool = AllTransactions::default();
3233
3234 let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
3235
3236 assert!(matches!(
3237 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
3238 Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
3239 ));
3240 }
3241
3242 #[test]
3243 fn test_tx_equal_gas_limit() {
3244 let on_chain_balance = U256::from(1_000);
3245 let on_chain_nonce = 0;
3246 let mut f = MockTransactionFactory::default();
3247 let mut pool = AllTransactions::default();
3248
3249 let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
3250
3251 let InsertOk { state, .. } =
3252 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
3253 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
3254 }
3255
3256 #[test]
3257 fn update_basefee_subpools() {
3258 let mut f = MockTransactionFactory::default();
3259 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3260
3261 let tx = MockTransaction::eip1559().inc_price_by(10);
3262 let validated = f.validated(tx.clone());
3263 let id = *validated.id();
3264 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3265
3266 assert_eq!(pool.pending_pool.len(), 1);
3267
3268 pool.update_basefee((tx.max_fee_per_gas() + 1) as u64, |_| {});
3269
3270 assert!(pool.pending_pool.is_empty());
3271 assert_eq!(pool.basefee_pool.len(), 1);
3272
3273 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3274 }
3275
3276 #[test]
3277 fn update_basefee_subpools_setting_block_info() {
3278 let mut f = MockTransactionFactory::default();
3279 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3280
3281 let tx = MockTransaction::eip1559().inc_price_by(10);
3282 let validated = f.validated(tx.clone());
3283 let id = *validated.id();
3284 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3285
3286 assert_eq!(pool.pending_pool.len(), 1);
3287
3288 let mut block_info = pool.block_info();
3290 block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
3291 pool.set_block_info(block_info);
3292
3293 assert!(pool.pending_pool.is_empty());
3294 assert_eq!(pool.basefee_pool.len(), 1);
3295
3296 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3297 }
3298
3299 #[test]
3300 fn basefee_decrease_promotes_affordable_and_keeps_unaffordable() {
3301 use alloy_primitives::address;
3302 let mut f = MockTransactionFactory::default();
3303 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3304
3305 let sender_a = address!("0x000000000000000000000000000000000000000a");
3308 let sender_b = address!("0x000000000000000000000000000000000000000b");
3309 let sender_c = address!("0x000000000000000000000000000000000000000c");
3310
3311 let tx1 = MockTransaction::eip1559()
3312 .set_sender(sender_a)
3313 .set_nonce(0)
3314 .set_max_fee(500)
3315 .inc_limit();
3316 let tx2 = MockTransaction::eip1559()
3317 .set_sender(sender_b)
3318 .set_nonce(0)
3319 .set_max_fee(600)
3320 .inc_limit();
3321 let tx3 = MockTransaction::eip1559()
3322 .set_sender(sender_c)
3323 .set_nonce(0)
3324 .set_max_fee(400)
3325 .inc_limit();
3326
3327 let mut block_info = pool.block_info();
3329 block_info.pending_basefee = 700;
3330 pool.set_block_info(block_info);
3331
3332 let validated1 = f.validated(tx1);
3333 let validated2 = f.validated(tx2);
3334 let validated3 = f.validated(tx3);
3335 let id1 = *validated1.id();
3336 let id2 = *validated2.id();
3337 let id3 = *validated3.id();
3338
3339 pool.add_transaction(validated1, U256::from(10_000), 0, None).unwrap();
3343 pool.add_transaction(validated2, U256::from(10_000), 0, None).unwrap();
3344 pool.add_transaction(validated3, U256::from(10_000), 0, None).unwrap();
3345
3346 println!("Basefee pool len: {}", pool.basefee_pool.len());
3348 println!("Pending pool len: {}", pool.pending_pool.len());
3349 println!("tx1 subpool: {:?}", pool.all_transactions.txs.get(&id1).unwrap().subpool);
3350 println!("tx2 subpool: {:?}", pool.all_transactions.txs.get(&id2).unwrap().subpool);
3351 println!("tx3 subpool: {:?}", pool.all_transactions.txs.get(&id3).unwrap().subpool);
3352
3353 assert_eq!(pool.basefee_pool.len(), 3);
3355 assert_eq!(pool.pending_pool.len(), 0);
3356 assert_eq!(pool.all_transactions.txs.get(&id1).unwrap().subpool, SubPool::BaseFee);
3357 assert_eq!(pool.all_transactions.txs.get(&id2).unwrap().subpool, SubPool::BaseFee);
3358 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3359
3360 let mut block_info = pool.block_info();
3362 block_info.pending_basefee = 450; pool.set_block_info(block_info);
3364
3365 assert_eq!(pool.basefee_pool.len(), 1);
3370 assert_eq!(pool.pending_pool.len(), 2);
3371
3372 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3374
3375 let tx1_meta = pool.all_transactions.txs.get(&id1).unwrap();
3377 let tx2_meta = pool.all_transactions.txs.get(&id2).unwrap();
3378 assert_eq!(tx1_meta.subpool, SubPool::Pending);
3379 assert_eq!(tx2_meta.subpool, SubPool::Pending);
3380 assert!(tx1_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3381 assert!(tx2_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3382
3383 let best: Vec<_> = pool.best_transactions().take(3).collect();
3385 assert_eq!(best.len(), 2); assert!(best.iter().any(|tx| tx.id() == &id1));
3387 assert!(best.iter().any(|tx| tx.id() == &id2));
3388 }
3389
3390 #[test]
3391 fn apply_fee_updates_records_promotions_after_basefee_drop() {
3392 let mut f = MockTransactionFactory::default();
3393 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3394
3395 let tx = MockTransaction::eip1559()
3396 .with_gas_limit(21_000)
3397 .with_max_fee(500)
3398 .with_priority_fee(1);
3399 let validated = f.validated(tx);
3400 let id = *validated.id();
3401 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3402
3403 assert_eq!(pool.pending_pool.len(), 1);
3404
3405 pool.update_basefee(600, |_| {});
3407 assert!(pool.pending_pool.is_empty());
3408 assert_eq!(pool.basefee_pool.len(), 1);
3409
3410 let prev_base_fee = 600;
3411 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3412
3413 pool.all_transactions.pending_fees.base_fee = 400;
3415
3416 let mut outcome = UpdateOutcome::default();
3417 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3418
3419 assert_eq!(pool.pending_pool.len(), 1);
3420 assert!(pool.basefee_pool.is_empty());
3421 assert_eq!(outcome.promoted.len(), 1);
3422 assert_eq!(outcome.promoted[0].id(), &id);
3423 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3424 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3425
3426 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3427 assert_eq!(tx_meta.subpool, SubPool::Pending);
3428 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3429 }
3430
3431 #[test]
3432 fn apply_fee_updates_records_promotions_after_blob_fee_drop() {
3433 let mut f = MockTransactionFactory::default();
3434 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3435
3436 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3437
3438 let tx = MockTransaction::eip4844().with_blob_fee(initial_blob_fee + 100);
3439 let validated = f.validated(tx.clone());
3440 let id = *validated.id();
3441 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3442
3443 assert_eq!(pool.pending_pool.len(), 1);
3444
3445 let increased_blob_fee = tx.max_fee_per_blob_gas().unwrap() + 200;
3447 pool.update_blob_fee(increased_blob_fee, Ordering::Equal, |_| {});
3448 assert!(pool.pending_pool.is_empty());
3449 assert_eq!(pool.blob_pool.len(), 1);
3450
3451 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3452 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3453
3454 pool.all_transactions.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
3456
3457 let mut outcome = UpdateOutcome::default();
3458 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3459
3460 assert_eq!(pool.pending_pool.len(), 1);
3461 assert!(pool.blob_pool.is_empty());
3462 assert_eq!(outcome.promoted.len(), 1);
3463 assert_eq!(outcome.promoted[0].id(), &id);
3464 assert_eq!(pool.all_transactions.pending_fees.base_fee, prev_base_fee);
3465 assert_eq!(pool.all_transactions.pending_fees.blob_fee, tx.max_fee_per_blob_gas().unwrap());
3466
3467 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3468 assert_eq!(tx_meta.subpool, SubPool::Pending);
3469 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3470 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3471 }
3472
3473 #[test]
3474 fn apply_fee_updates_promotes_blob_after_basefee_drop() {
3475 let mut f = MockTransactionFactory::default();
3476 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3477
3478 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3479
3480 let tx = MockTransaction::eip4844()
3481 .with_max_fee(500)
3482 .with_priority_fee(1)
3483 .with_blob_fee(initial_blob_fee + 100);
3484 let validated = f.validated(tx);
3485 let id = *validated.id();
3486 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3487
3488 assert_eq!(pool.pending_pool.len(), 1);
3489
3490 let high_base_fee = 600;
3492 pool.update_basefee(high_base_fee, |_| {});
3493 assert!(pool.pending_pool.is_empty());
3494 assert_eq!(pool.blob_pool.len(), 1);
3495
3496 let prev_base_fee = high_base_fee;
3497 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3498
3499 pool.all_transactions.pending_fees.base_fee = 400;
3501
3502 let mut outcome = UpdateOutcome::default();
3503 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3504
3505 assert_eq!(pool.pending_pool.len(), 1);
3506 assert!(pool.blob_pool.is_empty());
3507 assert_eq!(outcome.promoted.len(), 1);
3508 assert_eq!(outcome.promoted[0].id(), &id);
3509 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3510 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3511
3512 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3513 assert_eq!(tx_meta.subpool, SubPool::Pending);
3514 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3515 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3516 }
3517
3518 #[test]
3519 fn apply_fee_updates_demotes_after_basefee_rise() {
3520 let mut f = MockTransactionFactory::default();
3521 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3522
3523 let tx = MockTransaction::eip1559()
3524 .with_gas_limit(21_000)
3525 .with_max_fee(400)
3526 .with_priority_fee(1);
3527 let validated = f.validated(tx);
3528 let id = *validated.id();
3529 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3530
3531 assert_eq!(pool.pending_pool.len(), 1);
3532
3533 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3534 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3535
3536 let new_base_fee = prev_base_fee + 1_000;
3538 pool.all_transactions.pending_fees.base_fee = new_base_fee;
3539
3540 let mut outcome = UpdateOutcome::default();
3541 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3542
3543 assert!(pool.pending_pool.is_empty());
3544 assert_eq!(pool.basefee_pool.len(), 1);
3545 assert!(outcome.promoted.is_empty());
3546 assert_eq!(pool.all_transactions.pending_fees.base_fee, new_base_fee);
3547 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3548
3549 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3550 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
3551 assert!(!tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3552 }
3553
3554 #[test]
3555 fn get_highest_transaction_by_sender_and_nonce() {
3556 let mut f = MockTransactionFactory::default();
3558 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3559
3560 let tx = MockTransaction::eip1559();
3562 pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0, None).unwrap();
3563
3564 let tx1 = tx.inc_price().next();
3566
3567 let tx1_validated = f.validated(tx1.clone());
3569 pool.add_transaction(tx1_validated, U256::from(1_000), 0, None).unwrap();
3570
3571 assert_eq!(
3573 pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
3574 Some(1)
3575 );
3576
3577 let highest_tx = pool
3579 .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
3580 .expect("Failed to retrieve highest transaction");
3581
3582 assert_eq!(highest_tx.as_ref().transaction, tx1);
3584 }
3585
3586 #[test]
3587 fn get_highest_consecutive_transaction_by_sender() {
3588 let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
3590 let mut f = MockTransactionFactory::default();
3591
3592 let sender = Address::random();
3594 let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
3595 for nonce in txs {
3596 let mut mock_tx = MockTransaction::eip1559();
3597 mock_tx.set_sender(sender);
3598 mock_tx.set_nonce(nonce);
3599
3600 let validated_tx = f.validated(mock_tx);
3601 pool.add_transaction(validated_tx, U256::from(1000), 0, None).unwrap();
3602 }
3603
3604 let sender_id = f.ids.sender_id(&sender).unwrap();
3606 let next_tx =
3607 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
3608 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
3609
3610 let next_tx =
3611 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
3612 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
3613
3614 let next_tx =
3615 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3616 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
3617
3618 let mut info = SenderInfo::default();
3620 info.update(8, U256::ZERO);
3621 pool.sender_info.insert(sender_id, info);
3622 let next_tx =
3623 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3624 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
3625 }
3626
3627 #[test]
3628 fn discard_nonce_too_low() {
3629 let mut f = MockTransactionFactory::default();
3630 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3631
3632 let tx = MockTransaction::eip1559().inc_price_by(10);
3633 let validated = f.validated(tx.clone());
3634 let id = *validated.id();
3635 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3636
3637 let next = tx.next();
3638 let validated = f.validated(next.clone());
3639 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3640
3641 assert_eq!(pool.pending_pool.len(), 2);
3642
3643 let mut changed_senders = HashMap::default();
3644 changed_senders.insert(
3645 id.sender,
3646 SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
3647 );
3648 let outcome = pool.update_accounts(changed_senders);
3649 assert_eq!(outcome.discarded.len(), 1);
3650 assert_eq!(pool.pending_pool.len(), 1);
3651 }
3652
3653 #[test]
3654 fn discard_with_large_blob_txs() {
3655 reth_tracing::init_test_tracing();
3657
3658 let mut f = MockTransactionFactory::default();
3660 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3661 let default_limits = pool.config.blob_limit;
3662
3663 let a_sender = address!("0x000000000000000000000000000000000000000a");
3666
3667 let mut block_info = pool.block_info();
3669 block_info.pending_blob_fee = Some(100);
3670 block_info.pending_basefee = 100;
3671
3672 pool.set_block_info(block_info);
3674
3675 let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
3677 .into_iter()
3678 .map(|mut tx| {
3679 tx.set_size(default_limits.max_size / 2 + 1);
3680 tx.set_max_fee((block_info.pending_basefee - 1).into());
3681 tx
3682 })
3683 .collect::<Vec<_>>();
3684
3685 for tx in a_txs {
3687 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3688 }
3689
3690 let removed = pool.discard_worst();
3692 assert_eq!(removed.len(), 1);
3693 }
3694
3695 #[test]
3696 fn discard_with_parked_large_txs() {
3697 reth_tracing::init_test_tracing();
3699
3700 let mut f = MockTransactionFactory::default();
3702 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3703 let default_limits = pool.config.queued_limit;
3704
3705 let a_sender = address!("0x000000000000000000000000000000000000000a");
3708
3709 let pool_base_fee = 100;
3711 pool.update_basefee(pool_base_fee, |_| {});
3712
3713 let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
3715 .into_iter()
3716 .map(|mut tx| {
3717 tx.set_size(default_limits.max_size / 2 + 1);
3718 tx.set_max_fee((pool_base_fee - 1).into());
3719 tx
3720 })
3721 .collect::<Vec<_>>();
3722
3723 for tx in a_txs {
3725 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3726 }
3727
3728 let removed = pool.discard_worst();
3730 assert_eq!(removed.len(), 1);
3731 }
3732
3733 #[test]
3734 fn discard_at_capacity() {
3735 let mut f = MockTransactionFactory::default();
3736 let queued_limit = SubPoolLimit::new(1000, usize::MAX);
3737 let mut pool =
3738 TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
3739
3740 for _ in 0..queued_limit.max_txs {
3742 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3743 let validated = f.validated(tx.clone());
3744 let _id = *validated.id();
3745 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3746 }
3747
3748 let size = pool.size();
3749 assert_eq!(size.queued, queued_limit.max_txs);
3750
3751 for _ in 0..queued_limit.max_txs {
3752 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3753 let validated = f.validated(tx.clone());
3754 let _id = *validated.id();
3755 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3756
3757 pool.discard_worst();
3758 pool.assert_invariants();
3759 assert!(pool.size().queued <= queued_limit.max_txs);
3760 }
3761 }
3762
3763 #[test]
3764 fn discard_blobs_at_capacity() {
3765 let mut f = MockTransactionFactory::default();
3766 let blob_limit = SubPoolLimit::new(1000, usize::MAX);
3767 let mut pool =
3768 TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
3769 pool.all_transactions.pending_fees.blob_fee = 10000;
3770 for _ in 0..blob_limit.max_txs {
3772 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3773 let validated = f.validated(tx.clone());
3774 let _id = *validated.id();
3775 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3776 }
3777
3778 let size = pool.size();
3779 assert_eq!(size.blob, blob_limit.max_txs);
3780
3781 for _ in 0..blob_limit.max_txs {
3782 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3783 let validated = f.validated(tx.clone());
3784 let _id = *validated.id();
3785 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3786
3787 pool.discard_worst();
3788 pool.assert_invariants();
3789 assert!(pool.size().blob <= blob_limit.max_txs);
3790 }
3791 }
3792
3793 #[test]
3794 fn account_updates_sender_balance() {
3795 let mut on_chain_balance = U256::from(100);
3796 let on_chain_nonce = 0;
3797 let mut f = MockTransactionFactory::default();
3798 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3799
3800 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3801 let tx_1 = tx_0.next();
3802 let tx_2 = tx_1.next();
3803
3804 let v0 = f.validated(tx_0);
3806 let v1 = f.validated(tx_1);
3807 let v2 = f.validated(tx_2);
3808
3809 let _res =
3810 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3811 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3812 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3813
3814 assert_eq!(1, pool.pending_transactions().len());
3816 assert_eq!(2, pool.queued_transactions().len());
3817
3818 let mut updated_accounts = HashMap::default();
3820 on_chain_balance = U256::from(300);
3821 updated_accounts.insert(
3822 v0.sender_id(),
3823 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3824 );
3825 pool.update_accounts(updated_accounts.clone());
3826
3827 assert_eq!(3, pool.pending_transactions().len());
3828 assert!(pool.queued_transactions().is_empty());
3829
3830 updated_accounts.entry(v0.sender_id()).and_modify(|v| v.balance = U256::from(1));
3832 pool.update_accounts(updated_accounts);
3833
3834 assert!(pool.pending_transactions().is_empty());
3835 assert_eq!(3, pool.queued_transactions().len());
3836 }
3837
3838 #[test]
3839 fn account_updates_nonce_gap() {
3840 let on_chain_balance = U256::from(10_000);
3841 let mut on_chain_nonce = 0;
3842 let mut f = MockTransactionFactory::default();
3843 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3844
3845 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3846 let tx_1 = tx_0.next();
3847 let tx_2 = tx_1.next();
3848
3849 let v0 = f.validated(tx_0);
3851 let v1 = f.validated(tx_1);
3852 let v2 = f.validated(tx_2);
3853
3854 let _res =
3856 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3857 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3858
3859 assert!(pool.queued_transactions().is_empty());
3860 assert_eq!(2, pool.pending_transactions().len());
3861
3862 pool.remove_transaction_by_hash(v0.hash());
3864
3865 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3867
3868 assert_eq!(2, pool.queued_transactions().len());
3870 assert!(pool.pending_transactions().is_empty());
3871
3872 let mut updated_accounts = HashMap::default();
3874 on_chain_nonce += 1;
3875 updated_accounts.insert(
3876 v0.sender_id(),
3877 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3878 );
3879 pool.update_accounts(updated_accounts);
3880
3881 assert!(pool.queued_transactions().is_empty());
3883 assert_eq!(2, pool.pending_transactions().len());
3884 }
3885 #[test]
3886 fn test_transaction_removal() {
3887 let on_chain_balance = U256::from(10_000);
3888 let on_chain_nonce = 0;
3889 let mut f = MockTransactionFactory::default();
3890 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3891
3892 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3893 let tx_1 = tx_0.next();
3894
3895 let v0 = f.validated(tx_0);
3897 let v1 = f.validated(tx_1);
3898
3899 let _res =
3901 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3902 let _res =
3903 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3904
3905 assert_eq!(0, pool.queued_transactions().len());
3906 assert_eq!(2, pool.pending_transactions().len());
3907
3908 pool.remove_transaction(v0.id());
3910 let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
3912 assert_eq!(vec![v1.nonce()], pool_txs);
3913 }
3914 #[test]
3915 fn test_remove_transactions() {
3916 let on_chain_balance = U256::from(10_000);
3917 let on_chain_nonce = 0;
3918 let mut f = MockTransactionFactory::default();
3919 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3920
3921 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3922 let tx_1 = tx_0.next();
3923 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3924 let tx_3 = tx_2.next();
3925
3926 let v0 = f.validated(tx_0);
3928 let v1 = f.validated(tx_1);
3929 let v2 = f.validated(tx_2);
3930 let v3 = f.validated(tx_3);
3931
3932 let _res =
3934 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3935 let _res =
3936 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3937 let _res =
3938 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3939 let _res =
3940 pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3941
3942 assert_eq!(0, pool.queued_transactions().len());
3943 assert_eq!(4, pool.pending_transactions().len());
3944
3945 pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
3946
3947 assert_eq!(2, pool.queued_transactions().len());
3948 assert!(pool.pending_transactions().is_empty());
3949 assert!(pool.contains(v1.hash()));
3950 assert!(pool.contains(v3.hash()));
3951 }
3952
3953 #[test]
3954 fn test_remove_transactions_middle_pending_hash() {
3955 let on_chain_balance = U256::from(10_000);
3956 let on_chain_nonce = 0;
3957 let mut f = MockTransactionFactory::default();
3958 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3959
3960 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3961 let tx_1 = tx_0.next();
3962 let tx_2 = tx_1.next();
3963 let tx_3 = tx_2.next();
3964
3965 let v0 = f.validated(tx_0);
3967 let v1 = f.validated(tx_1);
3968 let v2 = f.validated(tx_2);
3969 let v3 = f.validated(tx_3);
3970
3971 let _res = pool.add_transaction(v0, on_chain_balance, on_chain_nonce, None).unwrap();
3973 let _res =
3974 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3975 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3976 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
3977
3978 assert_eq!(0, pool.queued_transactions().len());
3979 assert_eq!(4, pool.pending_transactions().len());
3980
3981 let mut removed_txs = pool.remove_transactions(vec![*v1.hash()]);
3982 assert_eq!(1, removed_txs.len());
3983
3984 assert_eq!(2, pool.queued_transactions().len());
3985 assert_eq!(1, pool.pending_transactions().len());
3986
3987 let removed_tx = removed_txs.pop().unwrap();
3989 let v1 = f.validated(removed_tx.transaction.clone());
3990 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3991 assert_eq!(0, pool.queued_transactions().len());
3992 assert_eq!(4, pool.pending_transactions().len());
3993 }
3994
3995 #[test]
3996 fn test_remove_transactions_and_descendants() {
3997 let on_chain_balance = U256::from(10_000);
3998 let on_chain_nonce = 0;
3999 let mut f = MockTransactionFactory::default();
4000 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4001
4002 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4003 let tx_1 = tx_0.next();
4004 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4005 let tx_3 = tx_2.next();
4006 let tx_4 = tx_3.next();
4007
4008 let v0 = f.validated(tx_0);
4010 let v1 = f.validated(tx_1);
4011 let v2 = f.validated(tx_2);
4012 let v3 = f.validated(tx_3);
4013 let v4 = f.validated(tx_4);
4014
4015 let _res =
4017 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4018 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4019 let _res =
4020 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4021 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4022 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4023
4024 assert_eq!(0, pool.queued_transactions().len());
4025 assert_eq!(5, pool.pending_transactions().len());
4026
4027 pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
4028
4029 assert_eq!(0, pool.queued_transactions().len());
4030 assert_eq!(0, pool.pending_transactions().len());
4031 }
4032 #[test]
4033 fn test_remove_descendants() {
4034 let on_chain_balance = U256::from(10_000);
4035 let on_chain_nonce = 0;
4036 let mut f = MockTransactionFactory::default();
4037 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4038
4039 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4040 let tx_1 = tx_0.next();
4041 let tx_2 = tx_1.next();
4042 let tx_3 = tx_2.next();
4043
4044 let v0 = f.validated(tx_0);
4046 let v1 = f.validated(tx_1);
4047 let v2 = f.validated(tx_2);
4048 let v3 = f.validated(tx_3);
4049
4050 let _res =
4052 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4053 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4054 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4055 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4056
4057 assert_eq!(0, pool.queued_transactions().len());
4058 assert_eq!(4, pool.pending_transactions().len());
4059
4060 let mut removed = Vec::new();
4061 pool.remove_transaction(v0.id());
4062 pool.remove_descendants(v0.id(), &mut removed);
4063
4064 assert_eq!(0, pool.queued_transactions().len());
4065 assert_eq!(0, pool.pending_transactions().len());
4066 assert_eq!(3, removed.len());
4067 }
4068 #[test]
4069 fn test_remove_transactions_by_sender() {
4070 let on_chain_balance = U256::from(10_000);
4071 let on_chain_nonce = 0;
4072 let mut f = MockTransactionFactory::default();
4073 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4074
4075 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4076 let tx_1 = tx_0.next();
4077 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4078 let tx_3 = tx_2.next();
4079 let tx_4 = tx_3.next();
4080
4081 let v0 = f.validated(tx_0);
4083 let v1 = f.validated(tx_1);
4084 let v2 = f.validated(tx_2);
4085 let v3 = f.validated(tx_3);
4086 let v4 = f.validated(tx_4);
4087
4088 let _res =
4090 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4091 let _res =
4092 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4093 let _res =
4094 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4095 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4096 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4097
4098 assert_eq!(0, pool.queued_transactions().len());
4099 assert_eq!(5, pool.pending_transactions().len());
4100
4101 pool.remove_transactions_by_sender(v2.sender_id());
4102
4103 assert_eq!(0, pool.queued_transactions().len());
4104 assert_eq!(2, pool.pending_transactions().len());
4105 assert!(pool.contains(v0.hash()));
4106 assert!(pool.contains(v1.hash()));
4107 }
4108 #[test]
4109 fn wrong_best_order_of_transactions() {
4110 let on_chain_balance = U256::from(10_000);
4111 let mut on_chain_nonce = 0;
4112 let mut f = MockTransactionFactory::default();
4113 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4114
4115 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4116 let tx_1 = tx_0.next();
4117 let tx_2 = tx_1.next();
4118 let tx_3 = tx_2.next();
4119
4120 let v0 = f.validated(tx_0);
4122 let v1 = f.validated(tx_1);
4123 let v2 = f.validated(tx_2);
4124 let v3 = f.validated(tx_3);
4125
4126 let _res =
4128 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4129 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4130
4131 assert_eq!(0, pool.queued_transactions().len());
4132 assert_eq!(2, pool.pending_transactions().len());
4133
4134 pool.remove_transaction(v0.id());
4136
4137 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4139
4140 assert_eq!(1, pool.queued_transactions().len());
4142 assert_eq!(1, pool.pending_transactions().len());
4143
4144 let mut updated_accounts = HashMap::default();
4146 on_chain_nonce += 1;
4147 updated_accounts.insert(
4148 v0.sender_id(),
4149 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4150 );
4151 pool.update_accounts(updated_accounts);
4152
4153 assert_eq!(0, pool.queued_transactions().len());
4156 assert_eq!(2, pool.pending_transactions().len());
4157
4158 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4160 assert_eq!(0, pool.queued_transactions().len());
4161 assert_eq!(3, pool.pending_transactions().len());
4162
4163 assert_eq!(
4166 pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
4167 vec![1, 2, 3]
4168 );
4169 }
4170
4171 #[test]
4172 fn test_best_with_attributes() {
4173 let on_chain_balance = U256::MAX;
4174 let on_chain_nonce = 0;
4175 let mut f = MockTransactionFactory::default();
4176 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4177
4178 let base_fee: u128 = 100;
4179 let blob_fee: u128 = 100;
4180
4181 let mut block_info = pool.block_info();
4183 block_info.pending_basefee = base_fee as u64;
4184 block_info.pending_blob_fee = Some(blob_fee);
4185 pool.set_block_info(block_info);
4186
4187 let tx1 = MockTransaction::eip4844()
4189 .with_sender(Address::with_last_byte(1))
4190 .with_max_fee(base_fee + 10)
4191 .with_blob_fee(blob_fee + 10);
4192 let tx2 = MockTransaction::eip4844()
4193 .with_sender(Address::with_last_byte(2))
4194 .with_max_fee(base_fee + 10)
4195 .with_blob_fee(blob_fee);
4196 let tx3 = MockTransaction::eip4844()
4197 .with_sender(Address::with_last_byte(3))
4198 .with_max_fee(base_fee)
4199 .with_blob_fee(blob_fee + 10);
4200 let tx4 = MockTransaction::eip4844()
4201 .with_sender(Address::with_last_byte(4))
4202 .with_max_fee(base_fee)
4203 .with_blob_fee(blob_fee);
4204 let tx5 = MockTransaction::eip4844()
4205 .with_sender(Address::with_last_byte(5))
4206 .with_max_fee(base_fee)
4207 .with_blob_fee(blob_fee - 10);
4208 let tx6 = MockTransaction::eip4844()
4209 .with_sender(Address::with_last_byte(6))
4210 .with_max_fee(base_fee - 10)
4211 .with_blob_fee(blob_fee);
4212 let tx7 = MockTransaction::eip4844()
4213 .with_sender(Address::with_last_byte(7))
4214 .with_max_fee(base_fee - 10)
4215 .with_blob_fee(blob_fee - 10);
4216
4217 for tx in vec![
4218 tx1.clone(),
4219 tx2.clone(),
4220 tx3.clone(),
4221 tx4.clone(),
4222 tx5.clone(),
4223 tx6.clone(),
4224 tx7.clone(),
4225 ] {
4226 pool.add_transaction(f.validated(tx.clone()), on_chain_balance, on_chain_nonce, None)
4227 .unwrap();
4228 }
4229
4230 let base_fee = base_fee as u64;
4231 let blob_fee = blob_fee as u64;
4232
4233 let cases = vec![
4234 (BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee + 5)), vec![tx1.clone()]),
4236 (
4238 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee)),
4239 vec![tx1.clone(), tx2.clone()],
4240 ),
4241 (
4243 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee - 5)),
4244 vec![tx1.clone(), tx2.clone()],
4245 ),
4246 (
4248 BestTransactionsAttributes::new(base_fee, Some(blob_fee + 5)),
4249 vec![tx1.clone(), tx3.clone()],
4250 ),
4251 (
4253 BestTransactionsAttributes::new(base_fee, Some(blob_fee)),
4254 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
4255 ),
4256 (
4258 BestTransactionsAttributes::new(base_fee, Some(blob_fee - 10)),
4259 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx5.clone()],
4260 ),
4261 (
4263 BestTransactionsAttributes::new(base_fee - 5, Some(blob_fee + 5)),
4264 vec![tx1.clone(), tx3.clone()],
4265 ),
4266 (
4268 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee)),
4269 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx6.clone()],
4270 ),
4271 (
4273 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee - 10)),
4274 vec![tx1, tx2, tx5, tx3, tx4, tx6, tx7],
4275 ),
4276 ];
4277
4278 for (idx, (attribute, expected)) in cases.into_iter().enumerate() {
4279 let mut best = pool.best_transactions_with_attributes(attribute);
4280
4281 for (tx_idx, expected_tx) in expected.into_iter().enumerate() {
4282 let tx = best.next().expect("Transaction should be returned");
4283 assert_eq!(
4284 tx.transaction,
4285 expected_tx,
4286 "Failed tx {} in case {}",
4287 tx_idx + 1,
4288 idx + 1
4289 );
4290 }
4291
4292 assert!(best.next().is_none());
4294 }
4295 }
4296
4297 #[test]
4298 fn test_pending_ordering() {
4299 let mut f = MockTransactionFactory::default();
4300 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4301
4302 let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
4303 let tx_1 = tx_0.next();
4304
4305 let v0 = f.validated(tx_0);
4306 let v1 = f.validated(tx_1);
4307
4308 pool.add_transaction(v0.clone(), U256::MAX, 0, None).unwrap();
4310 assert_eq!(1, pool.queued_transactions().len());
4311
4312 pool.add_transaction(v1, U256::MAX, 1, None).unwrap();
4314
4315 assert_eq!(2, pool.pending_transactions().len());
4316 assert_eq!(0, pool.queued_transactions().len());
4317
4318 assert_eq!(
4319 pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
4320 v0.nonce()
4321 );
4322 }
4323
4324 #[test]
4326 fn one_sender_one_independent_transaction() {
4327 let mut on_chain_balance = U256::from(4_999); let mut on_chain_nonce = 40;
4329 let mut f = MockTransactionFactory::default();
4330 let mut pool = TxPool::mock();
4331 let mut submitted_txs = Vec::new();
4332
4333 let template =
4335 MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
4336
4337 for tx_nonce in 40..48 {
4340 let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
4341 submitted_txs.push(*tx.id());
4342 pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap();
4343 }
4344
4345 on_chain_balance = U256::from(999_999);
4348 on_chain_nonce = 42;
4349 pool.remove_transaction(&submitted_txs[0]);
4350 pool.remove_transaction(&submitted_txs[1]);
4351
4352 for tx_nonce in 48..52 {
4354 pool.add_transaction(
4355 f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
4356 on_chain_balance,
4357 on_chain_nonce,
4358 None,
4359 )
4360 .unwrap();
4361 }
4362
4363 let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
4364 assert_eq!(best_txs.len(), 10); assert_eq!(pool.pending_pool.independent().len(), 1);
4367 }
4368
4369 #[test]
4370 fn test_insertion_disorder() {
4371 let mut f = MockTransactionFactory::default();
4372 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4373
4374 let sender = address!("0x1234567890123456789012345678901234567890");
4375 let tx0 = f.validated_arc(
4376 MockTransaction::legacy().with_sender(sender).with_nonce(0).with_gas_price(10),
4377 );
4378 let tx1 = f.validated_arc(
4379 MockTransaction::eip1559()
4380 .with_sender(sender)
4381 .with_nonce(1)
4382 .with_gas_limit(1000)
4383 .with_gas_price(10),
4384 );
4385 let tx2 = f.validated_arc(
4386 MockTransaction::legacy().with_sender(sender).with_nonce(2).with_gas_price(10),
4387 );
4388 let tx3 = f.validated_arc(
4389 MockTransaction::legacy().with_sender(sender).with_nonce(3).with_gas_price(10),
4390 );
4391
4392 pool.add_transaction((*tx0).clone(), U256::from(1000), 0, None).unwrap();
4394 let mut best = pool.best_transactions();
4395 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4396 assert_eq!(t0.id(), tx0.id());
4397 pool.add_transaction((*tx1).clone(), U256::from(1000), 0, None).unwrap();
4399 let mut best = pool.best_transactions();
4400 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4401 assert_eq!(t0.id(), tx0.id());
4402 assert!(best.next().is_none());
4403
4404 pool.add_transaction((*tx2).clone(), U256::MAX, 0, None).unwrap();
4406
4407 let mut best = pool.best_transactions();
4408
4409 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4410 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4411 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4412 assert_eq!(t0.id(), tx0.id());
4413 assert_eq!(t1.id(), tx1.id());
4414 assert_eq!(t2.id(), tx2.id());
4415
4416 pool.add_transaction((*tx3).clone(), U256::MAX, 0, None).unwrap();
4418 let mut best = pool.best_transactions();
4419 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4420 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4421 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4422 let t3 = best.next().expect("tx3 should be put in the pending subpool");
4423 assert_eq!(t0.id(), tx0.id());
4424 assert_eq!(t1.id(), tx1.id());
4425 assert_eq!(t2.id(), tx2.id());
4426 assert_eq!(t3.id(), tx3.id());
4427 }
4428
4429 #[test]
4430 fn test_non_4844_blob_fee_bit_invariant() {
4431 let mut f = MockTransactionFactory::default();
4432 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4433
4434 let non_4844_tx = MockTransaction::eip1559().set_max_fee(200).inc_limit();
4435 let validated = f.validated(non_4844_tx.clone());
4436
4437 assert!(!non_4844_tx.is_eip4844());
4438 pool.add_transaction(validated.clone(), U256::from(10_000), 0, None).unwrap();
4439
4440 let tx_meta = pool.all_transactions.txs.get(validated.id()).unwrap();
4442 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4443 assert_eq!(tx_meta.subpool, SubPool::Pending);
4444 }
4445
4446 #[test]
4447 fn test_blob_fee_enforcement_only_applies_to_eip4844() {
4448 let mut f = MockTransactionFactory::default();
4449 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4450
4451 let mut block_info = pool.block_info();
4453 block_info.pending_blob_fee = Some(160);
4454 block_info.pending_basefee = 100;
4455 pool.set_block_info(block_info);
4456
4457 let eip4844_tx = MockTransaction::eip4844()
4458 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4459 .with_max_fee(200)
4460 .with_blob_fee(150) .inc_limit();
4462
4463 let non_4844_tx = MockTransaction::eip1559()
4464 .with_sender(address!("0x000000000000000000000000000000000000000b"))
4465 .set_max_fee(200)
4466 .inc_limit();
4467
4468 let validated_4844 = f.validated(eip4844_tx);
4469 let validated_non_4844 = f.validated(non_4844_tx);
4470
4471 pool.add_transaction(validated_4844.clone(), U256::from(10_000), 0, None).unwrap();
4472 pool.add_transaction(validated_non_4844.clone(), U256::from(10_000), 0, None).unwrap();
4473
4474 let tx_4844_meta = pool.all_transactions.txs.get(validated_4844.id()).unwrap();
4475 let tx_non_4844_meta = pool.all_transactions.txs.get(validated_non_4844.id()).unwrap();
4476
4477 assert!(!tx_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4479 assert_eq!(tx_4844_meta.subpool, SubPool::Blob);
4480
4481 assert!(tx_non_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4483 assert_eq!(tx_non_4844_meta.subpool, SubPool::Pending);
4484 }
4485
4486 #[test]
4487 fn test_basefee_decrease_preserves_non_4844_blob_fee_bit() {
4488 let mut f = MockTransactionFactory::default();
4489 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4490
4491 let non_4844_tx = MockTransaction::eip1559()
4493 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4494 .set_max_fee(500) .inc_limit();
4496
4497 pool.update_basefee(600, |_| {});
4499
4500 let validated = f.validated(non_4844_tx);
4501 let tx_id = *validated.id();
4502 pool.add_transaction(validated, U256::from(10_000), 0, None).unwrap();
4503
4504 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4506 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
4507 assert!(
4508 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4509 "Non-4844 tx in BaseFee pool must retain ENOUGH_BLOB_FEE_CAP_BLOCK bit"
4510 );
4511
4512 pool.update_basefee(400, |_| {});
4515
4516 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4518 assert_eq!(
4519 tx_meta.subpool,
4520 SubPool::Pending,
4521 "Non-4844 tx should be promoted from BaseFee to Pending after basefee decrease"
4522 );
4523 assert!(
4524 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4525 "Non-4844 tx must NEVER lose ENOUGH_BLOB_FEE_CAP_BLOCK bit during basefee promotion"
4526 );
4527 assert!(
4528 tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK),
4529 "Non-4844 tx should gain ENOUGH_FEE_CAP_BLOCK bit after basefee decrease"
4530 );
4531 }
4532}