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}
120
121impl<T: TransactionOrdering> TxPool<T> {
124 pub fn new(ordering: T, config: PoolConfig) -> Self {
126 Self {
127 sender_info: Default::default(),
128 pending_pool: PendingPool::with_buffer(
129 ordering,
130 config.max_new_pending_txs_notifications,
131 ),
132 queued_pool: Default::default(),
133 basefee_pool: Default::default(),
134 blob_pool: Default::default(),
135 all_transactions: AllTransactions::new(&config),
136 config,
137 metrics: Default::default(),
138 }
139 }
140
141 pub fn get_highest_nonce_by_sender(&self, sender: SenderId) -> Option<u64> {
143 self.all().txs_iter(sender).last().map(|(_, tx)| tx.transaction.nonce())
144 }
145
146 pub fn get_highest_transaction_by_sender(
149 &self,
150 sender: SenderId,
151 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
152 self.all().txs_iter(sender).last().map(|(_, tx)| Arc::clone(&tx.transaction))
153 }
154
155 pub(crate) fn get_highest_consecutive_transaction_by_sender(
162 &self,
163 mut on_chain: TransactionId,
164 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
165 let mut last_consecutive_tx = None;
166
167 if let Some(current) = self.sender_info.get(&on_chain.sender) {
169 on_chain.nonce = on_chain.nonce.max(current.state_nonce);
170 }
171
172 let mut next_expected_nonce = on_chain.nonce;
173 for (id, tx) in self.all().descendant_txs_inclusive(&on_chain) {
174 if next_expected_nonce != id.nonce {
175 break
176 }
177 next_expected_nonce = id.next_nonce();
178 last_consecutive_tx = Some(tx);
179 }
180
181 last_consecutive_tx.map(|tx| Arc::clone(&tx.transaction))
182 }
183
184 pub(crate) const fn all(&self) -> &AllTransactions<T::Transaction> {
186 &self.all_transactions
187 }
188
189 pub(crate) fn unique_senders(&self) -> HashSet<Address> {
191 self.all_transactions.txs.values().map(|tx| tx.transaction.sender()).collect()
192 }
193
194 pub fn size(&self) -> PoolSize {
196 PoolSize {
197 pending: self.pending_pool.len(),
198 pending_size: self.pending_pool.size(),
199 basefee: self.basefee_pool.len(),
200 basefee_size: self.basefee_pool.size(),
201 queued: self.queued_pool.len(),
202 queued_size: self.queued_pool.size(),
203 blob: self.blob_pool.len(),
204 blob_size: self.blob_pool.size(),
205 total: self.all_transactions.len(),
206 }
207 }
208
209 pub const fn block_info(&self) -> BlockInfo {
211 BlockInfo {
212 block_gas_limit: self.all_transactions.block_gas_limit,
213 last_seen_block_hash: self.all_transactions.last_seen_block_hash,
214 last_seen_block_number: self.all_transactions.last_seen_block_number,
215 pending_basefee: self.all_transactions.pending_fees.base_fee,
216 pending_blob_fee: Some(self.all_transactions.pending_fees.blob_fee),
217 }
218 }
219
220 fn update_blob_fee<F>(
222 &mut self,
223 mut pending_blob_fee: u128,
224 base_fee_update: Ordering,
225 mut on_promoted: F,
226 ) where
227 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
228 {
229 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut pending_blob_fee);
230 match (self.all_transactions.pending_fees.blob_fee.cmp(&pending_blob_fee), base_fee_update)
231 {
232 (Ordering::Equal, Ordering::Equal | Ordering::Greater) => {
233 }
235 (Ordering::Greater, Ordering::Equal | Ordering::Greater) => {
236 let removed =
238 self.pending_pool.update_blob_fee(self.all_transactions.pending_fees.blob_fee);
239 for tx in removed {
240 let to = {
241 let tx =
242 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
243
244 tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
246 tx.subpool = tx.state.into();
247 tx.subpool
248 };
249 self.add_transaction_to_subpool(to, tx);
250 }
251 }
252 (Ordering::Less, _) | (_, Ordering::Less) => {
253 let removed =
255 self.blob_pool.enforce_pending_fees(&self.all_transactions.pending_fees);
256 for tx in removed {
257 let subpool = {
258 let tx_meta =
259 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
260 tx_meta.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
261 tx_meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
262 tx_meta.subpool = tx_meta.state.into();
263 tx_meta.subpool
264 };
265
266 if subpool == SubPool::Pending {
267 on_promoted(&tx);
268 }
269
270 self.add_transaction_to_subpool(subpool, tx);
271 }
272 }
273 }
274 }
275
276 fn update_basefee<F>(&mut self, mut pending_basefee: u64, mut on_promoted: F) -> Ordering
281 where
282 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
283 {
284 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut pending_basefee);
285 match self.all_transactions.pending_fees.base_fee.cmp(&pending_basefee) {
286 Ordering::Equal => {
287 Ordering::Equal
289 }
290 Ordering::Greater => {
291 let removed =
293 self.pending_pool.update_base_fee(self.all_transactions.pending_fees.base_fee);
294 for tx in removed {
295 let to = {
296 let tx =
297 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
298 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
299 tx.subpool = tx.state.into();
300 tx.subpool
301 };
302 self.add_transaction_to_subpool(to, tx);
303 }
304
305 Ordering::Greater
306 }
307 Ordering::Less => {
308 let current_base_fee = self.all_transactions.pending_fees.base_fee;
317 self.basefee_pool.enforce_basefee_with(current_base_fee, |tx| {
318 let subpool = {
320 let meta =
321 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
322 meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
323 meta.subpool = meta.state.into();
324 meta.subpool
325 };
326
327 if subpool == SubPool::Pending {
328 on_promoted(&tx);
329 }
330
331 trace!(target: "txpool", hash=%tx.transaction.hash(), pool=?subpool, "Adding transaction to a subpool");
332 match subpool {
333 SubPool::Queued => self.queued_pool.add_transaction(tx),
334 SubPool::Pending => {
335 self.pending_pool.add_transaction(tx, current_base_fee);
336 }
337 SubPool::Blob => {
338 self.blob_pool.add_transaction(tx);
339 }
340 SubPool::BaseFee => {
341 warn!(target: "txpool", "BaseFee transactions should become Pending after basefee decrease");
344 }
345 }
346 });
347
348 Ordering::Less
349 }
350 }
351 }
352
353 pub fn set_block_info(&mut self, info: BlockInfo) {
357 let basefee_ordering = self.update_basefee(info.pending_basefee, |_| {});
359 if let Some(blob_fee) = info.pending_blob_fee {
360 self.update_blob_fee(blob_fee, basefee_ordering, |_| {})
361 }
362 self.all_transactions.set_block_info(info);
364 }
365
366 pub(crate) fn best_transactions(&self) -> BestTransactions<T> {
369 self.pending_pool.best()
370 }
371
372 pub(crate) fn best_transactions_with_attributes(
379 &self,
380 best_transactions_attributes: BestTransactionsAttributes,
381 ) -> Box<dyn crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T::Transaction>>>>
382 {
383 match best_transactions_attributes.basefee.cmp(&self.all_transactions.pending_fees.base_fee)
386 {
387 Ordering::Equal => {
388 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
392 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
393 Ordering::Less => {
394 let unlocked =
396 self.blob_pool.satisfy_attributes(best_transactions_attributes);
397 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
398 unlocked,
399 best_transactions_attributes.basefee,
400 new_blob_fee,
401 ))
402 }
403 Ordering::Equal => Box::new(self.pending_pool.best()),
404 Ordering::Greater => {
405 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
407 best_transactions_attributes.basefee,
408 best_transactions_attributes.blob_fee.unwrap_or_default(),
409 ))
410 }
411 }
412 }
413 Ordering::Greater => {
414 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
416 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
417 Ordering::Less => {
418 let unlocked =
420 self.blob_pool.satisfy_attributes(best_transactions_attributes);
421 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
422 unlocked,
423 best_transactions_attributes.basefee,
424 new_blob_fee,
425 ))
426 }
427 Ordering::Equal | Ordering::Greater => {
428 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
430 best_transactions_attributes.basefee,
431 new_blob_fee,
432 ))
433 }
434 }
435 }
436 Ordering::Less => {
437 let mut unlocked = self
440 .basefee_pool
441 .satisfy_base_fee_transactions(best_transactions_attributes.basefee);
442
443 unlocked.extend(self.blob_pool.satisfy_attributes(best_transactions_attributes));
445
446 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
447 unlocked,
448 best_transactions_attributes.basefee,
449 best_transactions_attributes.blob_fee.unwrap_or_default(),
450 ))
451 }
452 }
453 }
454
455 pub(crate) fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
457 self.pending_pool.all().collect()
458 }
459 pub(crate) fn pending_transactions_iter(
461 &self,
462 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
463 self.pending_pool.all()
464 }
465
466 pub(crate) fn pending_transactions_count(&self) -> usize {
468 self.pending_pool.len()
469 }
470
471 pub(crate) fn pending_transactions_with_predicate(
473 &self,
474 mut predicate: impl FnMut(&ValidPoolTransaction<T::Transaction>) -> bool,
475 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
476 self.pending_transactions_iter().filter(|tx| predicate(tx)).collect()
477 }
478
479 pub(crate) fn pending_txs_by_sender(
481 &self,
482 sender: SenderId,
483 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
484 self.pending_transactions_iter().filter(|tx| tx.sender_id() == sender).collect()
485 }
486
487 pub(crate) fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
489 self.basefee_pool.all().chain(self.queued_pool.all()).collect()
490 }
491
492 pub(crate) fn queued_transactions_iter(
494 &self,
495 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
496 self.basefee_pool.all().chain(self.queued_pool.all())
497 }
498
499 pub(crate) fn queued_transactions_count(&self) -> usize {
501 self.basefee_pool.len() + self.queued_pool.len()
502 }
503
504 pub fn queued_and_pending_txs_by_sender(
506 &self,
507 sender: SenderId,
508 ) -> (SmallVec<[TransactionId; TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER]>, Vec<TransactionId>) {
509 (self.queued_pool.get_txs_by_sender(sender), self.pending_pool.get_txs_by_sender(sender))
510 }
511
512 pub(crate) fn queued_txs_by_sender(
514 &self,
515 sender: SenderId,
516 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
517 self.queued_transactions_iter().filter(|tx| tx.sender_id() == sender).collect()
518 }
519
520 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
522 self.all_transactions.contains(tx_hash)
523 }
524
525 #[cfg(test)]
527 pub(crate) fn subpool_contains(&self, subpool: SubPool, id: &TransactionId) -> bool {
528 match subpool {
529 SubPool::Queued => self.queued_pool.contains(id),
530 SubPool::Pending => self.pending_pool.contains(id),
531 SubPool::BaseFee => self.basefee_pool.contains(id),
532 SubPool::Blob => self.blob_pool.contains(id),
533 }
534 }
535
536 #[inline]
538 pub(crate) fn is_exceeded(&self) -> bool {
539 self.config.is_exceeded(self.size())
540 }
541
542 pub(crate) fn get(
544 &self,
545 tx_hash: &TxHash,
546 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
547 self.all_transactions.by_hash.get(tx_hash).cloned()
548 }
549
550 pub(crate) fn get_all(
552 &self,
553 txs: Vec<TxHash>,
554 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
555 txs.into_iter().filter_map(|tx| self.get(&tx))
556 }
557
558 pub(crate) fn get_transactions_by_sender(
560 &self,
561 sender: SenderId,
562 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
563 self.all_transactions.txs_iter(sender).map(|(_, tx)| Arc::clone(&tx.transaction)).collect()
564 }
565
566 const fn update_pending_fees_only(
569 &mut self,
570 mut new_base_fee: u64,
571 new_blob_fee: Option<u128>,
572 ) -> (u64, u128) {
573 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut new_base_fee);
574
575 let prev_blob_fee = if let Some(mut blob_fee) = new_blob_fee {
576 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut blob_fee);
577 blob_fee
578 } else {
579 self.all_transactions.pending_fees.blob_fee
580 };
581
582 (new_base_fee, prev_blob_fee)
583 }
584
585 fn apply_fee_updates(
592 &mut self,
593 prev_base_fee: u64,
594 prev_blob_fee: u128,
595 outcome: &mut UpdateOutcome<T::Transaction>,
596 ) {
597 let new_base_fee = self.all_transactions.pending_fees.base_fee;
598 let new_blob_fee = self.all_transactions.pending_fees.blob_fee;
599
600 if new_base_fee == prev_base_fee && new_blob_fee == prev_blob_fee {
601 return;
603 }
604
605 self.all_transactions.pending_fees.base_fee = prev_base_fee;
608 self.all_transactions.pending_fees.blob_fee = prev_blob_fee;
609
610 let base_fee_ordering = self.update_basefee(new_base_fee, |tx| {
611 outcome.promoted.push(tx.clone());
612 });
613
614 self.update_blob_fee(new_blob_fee, base_fee_ordering, |tx| {
615 outcome.promoted.push(tx.clone());
616 });
617 }
618
619 pub(crate) fn update_accounts(
621 &mut self,
622 changed_senders: FxHashMap<SenderId, SenderInfo>,
623 ) -> UpdateOutcome<T::Transaction> {
624 let updates = self.all_transactions.update(&changed_senders);
626
627 self.sender_info.extend(changed_senders);
629
630 let update = self.process_updates(updates);
632 self.update_size_metrics();
634 update
635 }
636
637 pub(crate) fn on_canonical_state_change(
642 &mut self,
643 block_info: BlockInfo,
644 mined_transactions: Vec<TxHash>,
645 changed_senders: FxHashMap<SenderId, SenderInfo>,
646 _update_kind: PoolUpdateKind,
647 ) -> OnNewCanonicalStateOutcome<T::Transaction> {
648 let block_hash = block_info.last_seen_block_hash;
650
651 let mut removed_txs_count = 0;
653 for tx_hash in &mined_transactions {
654 if self.prune_transaction_by_hash(tx_hash).is_some() {
655 removed_txs_count += 1;
656 }
657 }
658
659 self.metrics.removed_transactions.increment(removed_txs_count);
661
662 let (prev_base_fee, prev_blob_fee) =
667 self.update_pending_fees_only(block_info.pending_basefee, block_info.pending_blob_fee);
668
669 let mut outcome = self.update_accounts(changed_senders);
671
672 self.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
675
676 self.all_transactions.set_block_info(block_info);
678
679 self.update_transaction_type_metrics();
680 self.metrics.performed_state_updates.increment(1);
681
682 OnNewCanonicalStateOutcome {
683 block_hash,
684 mined: mined_transactions,
685 promoted: outcome.promoted,
686 discarded: outcome.discarded,
687 }
688 }
689
690 pub(crate) fn update_size_metrics(&self) {
692 let stats = self.size();
693 self.metrics.pending_pool_transactions.set(stats.pending as f64);
694 self.metrics.pending_pool_size_bytes.set(stats.pending_size as f64);
695 self.metrics.basefee_pool_transactions.set(stats.basefee as f64);
696 self.metrics.basefee_pool_size_bytes.set(stats.basefee_size as f64);
697 self.metrics.queued_pool_transactions.set(stats.queued as f64);
698 self.metrics.queued_pool_size_bytes.set(stats.queued_size as f64);
699 self.metrics.blob_pool_transactions.set(stats.blob as f64);
700 self.metrics.blob_pool_size_bytes.set(stats.blob_size as f64);
701 self.metrics.total_transactions.set(stats.total as f64);
702 }
703
704 pub(crate) fn update_transaction_type_metrics(&self) {
706 let mut legacy_count = 0;
707 let mut eip2930_count = 0;
708 let mut eip1559_count = 0;
709 let mut eip4844_count = 0;
710 let mut eip7702_count = 0;
711 let mut other_count = 0;
712
713 for tx in self.all_transactions.transactions_iter() {
714 match tx.transaction.ty() {
715 LEGACY_TX_TYPE_ID => legacy_count += 1,
716 EIP2930_TX_TYPE_ID => eip2930_count += 1,
717 EIP1559_TX_TYPE_ID => eip1559_count += 1,
718 EIP4844_TX_TYPE_ID => eip4844_count += 1,
719 EIP7702_TX_TYPE_ID => eip7702_count += 1,
720 _ => other_count += 1,
721 }
722 }
723
724 self.metrics.total_legacy_transactions.set(legacy_count as f64);
725 self.metrics.total_eip2930_transactions.set(eip2930_count as f64);
726 self.metrics.total_eip1559_transactions.set(eip1559_count as f64);
727 self.metrics.total_eip4844_transactions.set(eip4844_count as f64);
728 self.metrics.total_eip7702_transactions.set(eip7702_count as f64);
729 self.metrics.total_other_transactions.set(other_count as f64);
730 }
731
732 pub(crate) fn add_transaction(
733 &mut self,
734 tx: ValidPoolTransaction<T::Transaction>,
735 on_chain_balance: U256,
736 on_chain_nonce: u64,
737 on_chain_code_hash: Option<B256>,
738 ) -> PoolResult<AddedTransaction<T::Transaction>> {
739 if self.contains(tx.hash()) {
740 return Err(PoolError::new(*tx.hash(), PoolErrorKind::AlreadyImported))
741 }
742
743 self.validate_auth(&tx, on_chain_nonce, on_chain_code_hash)?;
744
745 self.sender_info
747 .entry(tx.sender_id())
748 .or_default()
749 .update(on_chain_nonce, on_chain_balance);
750
751 match self.all_transactions.insert_tx(tx, on_chain_balance, on_chain_nonce) {
752 Ok(InsertOk { transaction, move_to, replaced_tx, updates, state }) => {
753 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
755 self.metrics.inserted_transactions.increment(1);
757 let UpdateOutcome { promoted, discarded } = self.process_updates(updates);
758
759 let replaced = replaced_tx.map(|(tx, _)| tx);
760
761 let res = if move_to.is_pending() {
763 AddedTransaction::Pending(AddedPendingTransaction {
764 transaction,
765 promoted,
766 discarded,
767 replaced,
768 })
769 } else {
770 let queued_reason = state.determine_queued_reason(move_to);
772 AddedTransaction::Parked {
773 transaction,
774 subpool: move_to,
775 replaced,
776 queued_reason,
777 }
778 };
779
780 self.update_size_metrics();
782
783 Ok(res)
784 }
785 Err(err) => {
786 self.metrics.invalid_transactions.increment(1);
788 match err {
789 InsertErr::Underpriced { existing: _, transaction } => Err(PoolError::new(
790 *transaction.hash(),
791 PoolErrorKind::ReplacementUnderpriced,
792 )),
793 InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap } => {
794 Err(PoolError::new(
795 *transaction.hash(),
796 PoolErrorKind::FeeCapBelowMinimumProtocolFeeCap(fee_cap),
797 ))
798 }
799 InsertErr::ExceededSenderTransactionsCapacity { transaction } => {
800 Err(PoolError::new(
801 *transaction.hash(),
802 PoolErrorKind::SpammerExceededCapacity(transaction.sender()),
803 ))
804 }
805 InsertErr::TxGasLimitMoreThanAvailableBlockGas {
806 transaction,
807 block_gas_limit,
808 tx_gas_limit,
809 } => Err(PoolError::new(
810 *transaction.hash(),
811 PoolErrorKind::InvalidTransaction(
812 InvalidPoolTransactionError::ExceedsGasLimit(
813 tx_gas_limit,
814 block_gas_limit,
815 ),
816 ),
817 )),
818 InsertErr::BlobTxHasNonceGap { transaction } => Err(PoolError::new(
819 *transaction.hash(),
820 PoolErrorKind::InvalidTransaction(
821 Eip4844PoolTransactionError::Eip4844NonceGap.into(),
822 ),
823 )),
824 InsertErr::Overdraft { transaction } => Err(PoolError::new(
825 *transaction.hash(),
826 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Overdraft {
827 cost: *transaction.cost(),
828 balance: on_chain_balance,
829 }),
830 )),
831 InsertErr::TxTypeConflict { transaction } => Err(PoolError::new(
832 *transaction.hash(),
833 PoolErrorKind::ExistingConflictingTransactionType(
834 transaction.sender(),
835 transaction.tx_type(),
836 ),
837 )),
838 }
839 }
840 }
841 }
842
843 fn check_delegation_limit(
847 &self,
848 transaction: &ValidPoolTransaction<T::Transaction>,
849 on_chain_nonce: u64,
850 on_chain_code_hash: Option<B256>,
851 ) -> Result<(), PoolError> {
852 if (on_chain_code_hash.is_none() || on_chain_code_hash == Some(KECCAK_EMPTY)) &&
854 !self.all_transactions.auths.contains_key(&transaction.sender_id())
855 {
856 return Ok(())
857 }
858
859 let mut txs_by_sender =
860 self.pending_pool.iter_txs_by_sender(transaction.sender_id()).peekable();
861
862 if txs_by_sender.peek().is_none() {
863 let nonce_gap_distance = transaction.nonce().saturating_sub(on_chain_nonce);
868 if nonce_gap_distance >= self.config.max_inflight_delegated_slot_limit as u64 {
869 return Err(PoolError::new(
870 *transaction.hash(),
871 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
872 Eip7702PoolTransactionError::OutOfOrderTxFromDelegated,
873 )),
874 ))
875 }
876 return Ok(())
877 }
878
879 let mut count = 0;
880 for id in txs_by_sender {
881 if id == &transaction.transaction_id {
882 return Ok(())
884 }
885 count += 1;
886 }
887
888 if count < self.config.max_inflight_delegated_slot_limit {
889 return Ok(())
891 }
892
893 Err(PoolError::new(
894 *transaction.hash(),
895 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
896 Eip7702PoolTransactionError::InflightTxLimitReached,
897 )),
898 ))
899 }
900
901 fn validate_auth(
911 &self,
912 transaction: &ValidPoolTransaction<T::Transaction>,
913 on_chain_nonce: u64,
914 on_chain_code_hash: Option<B256>,
915 ) -> Result<(), PoolError> {
916 self.check_delegation_limit(transaction, on_chain_nonce, on_chain_code_hash)?;
918
919 if let Some(authority_list) = &transaction.authority_ids {
920 for sender_id in authority_list {
921 if self.all_transactions.txs_iter(*sender_id).nth(1).is_some() {
923 return Err(PoolError::new(
924 *transaction.hash(),
925 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
926 Eip7702PoolTransactionError::AuthorityReserved,
927 )),
928 ))
929 }
930 }
931 }
932
933 Ok(())
934 }
935
936 fn process_updates(&mut self, updates: Vec<PoolUpdate>) -> UpdateOutcome<T::Transaction> {
940 let mut outcome = UpdateOutcome::default();
941 for PoolUpdate { id, current, destination } in updates {
942 match destination {
943 Destination::Discard => {
944 if let Some(tx) = self.prune_transaction_by_id(&id) {
946 outcome.discarded.push(tx);
947 }
948 self.metrics.removed_transactions.increment(1);
949 }
950 Destination::Pool(move_to) => {
951 debug_assert_ne!(&move_to, ¤t, "destination must be different");
952 let moved = self.move_transaction(current, move_to, &id);
953 if matches!(move_to, SubPool::Pending) &&
954 let Some(tx) = moved
955 {
956 trace!(target: "txpool", hash=%tx.transaction.hash(), "Promoted transaction to pending");
957 outcome.promoted.push(tx);
958 }
959 }
960 }
961 }
962
963 outcome
964 }
965
966 fn move_transaction(
971 &mut self,
972 from: SubPool,
973 to: SubPool,
974 id: &TransactionId,
975 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
976 let tx = self.remove_from_subpool(from, id)?;
977 self.add_transaction_to_subpool(to, tx.clone());
978 Some(tx)
979 }
980
981 pub(crate) fn remove_transactions(
986 &mut self,
987 hashes: Vec<TxHash>,
988 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
989 let txs =
990 hashes.into_iter().filter_map(|hash| self.remove_transaction_by_hash(&hash)).collect();
991 self.update_size_metrics();
992 txs
993 }
994
995 pub(crate) fn remove_transactions_and_descendants(
997 &mut self,
998 hashes: Vec<TxHash>,
999 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1000 let mut removed = Vec::new();
1001 for hash in hashes {
1002 if let Some(tx) = self.remove_transaction_by_hash(&hash) {
1003 removed.push(tx.clone());
1004 self.remove_descendants(tx.id(), &mut removed);
1005 }
1006 }
1007 self.update_size_metrics();
1008 removed
1009 }
1010
1011 pub(crate) fn remove_transactions_by_sender(
1013 &mut self,
1014 sender_id: SenderId,
1015 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1016 let mut removed = Vec::new();
1017 let txs = self.get_transactions_by_sender(sender_id);
1018 for tx in txs {
1019 if let Some(tx) = self.remove_transaction(tx.id()) {
1020 removed.push(tx);
1021 }
1022 }
1023 self.update_size_metrics();
1024 removed
1025 }
1026
1027 fn remove_transaction(
1031 &mut self,
1032 id: &TransactionId,
1033 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1034 let (tx, pool) = self.all_transactions.remove_transaction(id)?;
1035 self.remove_from_subpool(pool, tx.id())
1036 }
1037
1038 fn remove_transaction_by_hash(
1044 &mut self,
1045 tx_hash: &B256,
1046 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1047 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1048
1049 let updates = self.all_transactions.park_descendant_transactions(tx.id());
1051 self.process_updates(updates);
1052 self.remove_from_subpool(pool, tx.id())
1053 }
1054
1055 fn prune_transaction_by_hash(
1062 &mut self,
1063 tx_hash: &B256,
1064 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1065 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1066 self.remove_from_subpool(pool, tx.id())
1067 }
1068 fn prune_transaction_by_id(
1073 &mut self,
1074 tx_id: &TransactionId,
1075 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1076 let (tx, pool) = self.all_transactions.remove_transaction_by_id(tx_id)?;
1077 self.remove_from_subpool(pool, tx.id())
1078 }
1079
1080 fn remove_from_subpool(
1084 &mut self,
1085 pool: SubPool,
1086 tx: &TransactionId,
1087 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1088 let tx = match pool {
1089 SubPool::Queued => self.queued_pool.remove_transaction(tx),
1090 SubPool::Pending => self.pending_pool.remove_transaction(tx),
1091 SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
1092 SubPool::Blob => self.blob_pool.remove_transaction(tx),
1093 };
1094
1095 if let Some(ref tx) = tx {
1096 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Removed transaction from a subpool");
1100 }
1101
1102 tx
1103 }
1104
1105 fn remove_descendants(
1109 &mut self,
1110 tx: &TransactionId,
1111 removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
1112 ) {
1113 let mut id = *tx;
1114
1115 loop {
1117 let descendant =
1118 self.all_transactions.descendant_txs_exclusive(&id).map(|(id, _)| *id).next();
1119 if let Some(descendant) = descendant {
1120 if let Some(tx) = self.remove_transaction(&descendant) {
1121 removed.push(tx)
1122 }
1123 id = descendant;
1124 } else {
1125 return
1126 }
1127 }
1128 }
1129
1130 fn add_transaction_to_subpool(
1132 &mut self,
1133 pool: SubPool,
1134 tx: Arc<ValidPoolTransaction<T::Transaction>>,
1135 ) {
1136 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Adding transaction to a subpool");
1140 match pool {
1141 SubPool::Queued => self.queued_pool.add_transaction(tx),
1142 SubPool::Pending => {
1143 self.pending_pool.add_transaction(tx, self.all_transactions.pending_fees.base_fee);
1144 }
1145 SubPool::BaseFee => {
1146 self.basefee_pool.add_transaction(tx);
1147 }
1148 SubPool::Blob => {
1149 self.blob_pool.add_transaction(tx);
1150 }
1151 }
1152 }
1153
1154 fn add_new_transaction(
1157 &mut self,
1158 transaction: Arc<ValidPoolTransaction<T::Transaction>>,
1159 replaced: Option<(Arc<ValidPoolTransaction<T::Transaction>>, SubPool)>,
1160 pool: SubPool,
1161 ) {
1162 if let Some((replaced, replaced_pool)) = replaced {
1163 self.remove_from_subpool(replaced_pool, replaced.id());
1165 }
1166
1167 self.add_transaction_to_subpool(pool, transaction)
1168 }
1169
1170 pub(crate) fn discard_worst(&mut self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1177 let mut removed = Vec::new();
1178
1179 macro_rules! discard_worst {
1181 ($this:ident, $removed:ident, [$($limit:ident => ($pool:ident, $metric:ident)),* $(,)*]) => {
1182 $ (
1183 while $this.$pool.exceeds(&$this.config.$limit)
1184 {
1185 trace!(
1186 target: "txpool",
1187 "discarding transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1188 stringify!($pool),
1189 $this.config.$limit,
1190 $this.$pool.size(),
1191 $this.$pool.len(),
1192 );
1193
1194 let removed_from_subpool = $this.$pool.truncate_pool($this.config.$limit.clone());
1196
1197 trace!(
1198 target: "txpool",
1199 "removed {} transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1200 removed_from_subpool.len(),
1201 stringify!($pool),
1202 $this.config.$limit,
1203 $this.$pool.size(),
1204 $this.$pool.len()
1205 );
1206 $this.metrics.$metric.increment(removed_from_subpool.len() as u64);
1207
1208 for tx in removed_from_subpool {
1210 $this.all_transactions.remove_transaction(tx.id());
1211
1212 let id = *tx.id();
1213
1214 removed.push(tx);
1216
1217 $this.remove_descendants(&id, &mut $removed);
1219 }
1220 }
1221
1222 )*
1223 };
1224 }
1225
1226 discard_worst!(
1227 self, removed, [
1228 pending_limit => (pending_pool, pending_transactions_evicted),
1229 basefee_limit => (basefee_pool, basefee_transactions_evicted),
1230 blob_limit => (blob_pool, blob_transactions_evicted),
1231 queued_limit => (queued_pool, queued_transactions_evicted),
1232 ]
1233 );
1234
1235 removed
1236 }
1237
1238 pub(crate) fn len(&self) -> usize {
1240 self.all_transactions.len()
1241 }
1242
1243 pub(crate) fn is_empty(&self) -> bool {
1245 self.all_transactions.is_empty()
1246 }
1247
1248 #[cfg(any(test, feature = "test-utils"))]
1256 pub fn assert_invariants(&self) {
1257 let size = self.size();
1258 let actual = size.basefee + size.pending + size.queued + size.blob;
1259 assert_eq!(
1260 size.total, actual,
1261 "total size must be equal to the sum of all sub-pools, basefee:{}, pending:{}, queued:{}, blob:{}",
1262 size.basefee, size.pending, size.queued, size.blob
1263 );
1264 self.all_transactions.assert_invariants();
1265 self.pending_pool.assert_invariants();
1266 self.basefee_pool.assert_invariants();
1267 self.queued_pool.assert_invariants();
1268 self.blob_pool.assert_invariants();
1269 }
1270}
1271
1272#[cfg(any(test, feature = "test-utils"))]
1273impl TxPool<crate::test_utils::MockOrdering> {
1274 pub fn mock() -> Self {
1276 Self::new(crate::test_utils::MockOrdering::default(), PoolConfig::default())
1277 }
1278}
1279
1280#[cfg(test)]
1281impl<T: TransactionOrdering> Drop for TxPool<T> {
1282 fn drop(&mut self) {
1283 self.assert_invariants();
1284 }
1285}
1286
1287impl<T: TransactionOrdering> TxPool<T> {
1288 pub const fn pending(&self) -> &PendingPool<T> {
1290 &self.pending_pool
1291 }
1292
1293 pub const fn base_fee(&self) -> &ParkedPool<BasefeeOrd<T::Transaction>> {
1295 &self.basefee_pool
1296 }
1297
1298 pub const fn queued(&self) -> &ParkedPool<QueuedOrd<T::Transaction>> {
1300 &self.queued_pool
1301 }
1302}
1303
1304impl<T: TransactionOrdering> fmt::Debug for TxPool<T> {
1305 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1306 f.debug_struct("TxPool").field("config", &self.config).finish_non_exhaustive()
1307 }
1308}
1309
1310pub(crate) struct AllTransactions<T: PoolTransaction> {
1315 minimal_protocol_basefee: u64,
1319 block_gas_limit: u64,
1321 max_account_slots: usize,
1323 by_hash: HashMap<TxHash, Arc<ValidPoolTransaction<T>>>,
1325 txs: BTreeMap<TransactionId, PoolInternalTransaction<T>>,
1327 tx_counter: FxHashMap<SenderId, usize>,
1329 last_seen_block_number: u64,
1331 last_seen_block_hash: B256,
1333 pending_fees: PendingFees,
1335 price_bumps: PriceBumpConfig,
1337 local_transactions_config: LocalTransactionConfig,
1339 auths: FxHashMap<SenderId, HashSet<TxHash>>,
1341 metrics: AllTransactionsMetrics,
1343}
1344
1345impl<T: PoolTransaction> AllTransactions<T> {
1346 fn new(config: &PoolConfig) -> Self {
1348 Self {
1349 max_account_slots: config.max_account_slots,
1350 price_bumps: config.price_bumps,
1351 local_transactions_config: config.local_transactions_config.clone(),
1352 minimal_protocol_basefee: config.minimal_protocol_basefee,
1353 block_gas_limit: config.gas_limit,
1354 ..Default::default()
1355 }
1356 }
1357
1358 #[expect(dead_code)]
1360 pub(crate) fn hashes_iter(&self) -> impl Iterator<Item = TxHash> + '_ {
1361 self.by_hash.keys().copied()
1362 }
1363
1364 pub(crate) fn transactions_iter(
1366 &self,
1367 ) -> impl Iterator<Item = &Arc<ValidPoolTransaction<T>>> + '_ {
1368 self.by_hash.values()
1369 }
1370
1371 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
1373 self.by_hash.contains_key(tx_hash)
1374 }
1375
1376 pub(crate) fn get(&self, id: &TransactionId) -> Option<&PoolInternalTransaction<T>> {
1378 self.txs.get(id)
1379 }
1380
1381 pub(crate) fn tx_inc(&mut self, sender: SenderId) {
1383 let count = self.tx_counter.entry(sender).or_default();
1384 *count += 1;
1385 self.metrics.all_transactions_by_all_senders.increment(1.0);
1386 }
1387
1388 pub(crate) fn tx_decr(&mut self, sender: SenderId) {
1390 if let hash_map::Entry::Occupied(mut entry) = self.tx_counter.entry(sender) {
1391 let count = entry.get_mut();
1392 if *count == 1 {
1393 entry.remove();
1394 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1395 return
1396 }
1397 *count -= 1;
1398 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1399 }
1400 }
1401
1402 fn set_block_info(&mut self, block_info: BlockInfo) {
1404 let BlockInfo {
1405 block_gas_limit,
1406 last_seen_block_hash,
1407 last_seen_block_number,
1408 pending_basefee,
1409 pending_blob_fee,
1410 } = block_info;
1411 self.last_seen_block_number = last_seen_block_number;
1412 self.last_seen_block_hash = last_seen_block_hash;
1413
1414 self.pending_fees.base_fee = pending_basefee;
1415 self.metrics.base_fee.set(pending_basefee as f64);
1416
1417 self.block_gas_limit = block_gas_limit;
1418
1419 if let Some(pending_blob_fee) = pending_blob_fee {
1420 self.pending_fees.blob_fee = pending_blob_fee;
1421 self.metrics.blob_base_fee.set(pending_blob_fee as f64);
1422 }
1423 }
1424
1425 pub(crate) fn update_size_metrics(&self) {
1427 self.metrics.all_transactions_by_hash.set(self.by_hash.len() as f64);
1428 self.metrics.all_transactions_by_id.set(self.txs.len() as f64);
1429 }
1430
1431 pub(crate) fn update(
1448 &mut self,
1449 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1450 ) -> Vec<PoolUpdate> {
1451 let mut updates = Vec::with_capacity(64);
1453
1454 let mut iter = self.txs.iter_mut().peekable();
1455
1456 'transactions: while let Some((id, tx)) = iter.next() {
1466 macro_rules! next_sender {
1467 ($iter:ident) => {
1468 'this: while let Some((peek, _)) = iter.peek() {
1469 if peek.sender != id.sender {
1470 break 'this
1471 }
1472 iter.next();
1473 }
1474 };
1475 }
1476
1477 let changed_balance = if let Some(info) = changed_accounts.get(&id.sender) {
1480 if id.nonce < info.state_nonce {
1482 updates.push(PoolUpdate {
1483 id: *tx.transaction.id(),
1484 current: tx.subpool,
1485 destination: Destination::Discard,
1486 });
1487 continue 'transactions
1488 }
1489
1490 let ancestor = TransactionId::ancestor(id.nonce, info.state_nonce, id.sender);
1491 if ancestor.is_none() {
1493 tx.state.insert(TxState::NO_NONCE_GAPS);
1494 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1495 tx.cumulative_cost = U256::ZERO;
1496 if tx.transaction.cost() > &info.balance {
1497 tx.state.remove(TxState::ENOUGH_BALANCE);
1499 } else {
1500 tx.state.insert(TxState::ENOUGH_BALANCE);
1501 }
1502 }
1503
1504 Some(&info.balance)
1505 } else {
1506 None
1507 };
1508
1509 if tx.state.has_nonce_gap() {
1511 next_sender!(iter);
1512 continue 'transactions
1513 }
1514
1515 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1517
1518 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1520 Self::record_subpool_update(&mut updates, tx);
1522
1523 let mut has_parked_ancestor = !tx.state.is_pending();
1525
1526 let mut cumulative_cost = tx.next_cumulative_cost();
1527
1528 let mut next_nonce_in_line = tx.transaction.nonce().saturating_add(1);
1530
1531 while let Some((peek, tx)) = iter.peek_mut() {
1533 if peek.sender != id.sender {
1534 continue 'transactions
1536 }
1537
1538 if tx.transaction.nonce() == next_nonce_in_line {
1539 tx.state.insert(TxState::NO_NONCE_GAPS);
1541 } else {
1542 next_sender!(iter);
1544 continue 'transactions
1545 }
1546
1547 next_nonce_in_line = next_nonce_in_line.saturating_add(1);
1549
1550 tx.cumulative_cost = cumulative_cost;
1552 cumulative_cost = tx.next_cumulative_cost();
1554
1555 if let Some(changed_balance) = changed_balance {
1557 if &cumulative_cost > changed_balance {
1558 tx.state.remove(TxState::ENOUGH_BALANCE);
1560 } else {
1561 tx.state.insert(TxState::ENOUGH_BALANCE);
1562 }
1563 }
1564
1565 if has_parked_ancestor {
1567 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1568 } else {
1569 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1570 }
1571 has_parked_ancestor = !tx.state.is_pending();
1572
1573 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1575 Self::record_subpool_update(&mut updates, tx);
1576
1577 iter.next();
1579 }
1580 }
1581
1582 updates
1583 }
1584
1585 fn record_subpool_update(updates: &mut Vec<PoolUpdate>, tx: &mut PoolInternalTransaction<T>) {
1590 let current_pool = tx.subpool;
1591 tx.subpool = tx.state.into();
1592 if current_pool != tx.subpool {
1593 updates.push(PoolUpdate {
1594 id: *tx.transaction.id(),
1595 current: current_pool,
1596 destination: tx.subpool.into(),
1597 })
1598 }
1599 }
1600
1601 fn update_tx_base_fee(pending_block_base_fee: u64, tx: &mut PoolInternalTransaction<T>) {
1603 match tx.transaction.max_fee_per_gas().cmp(&(pending_block_base_fee as u128)) {
1605 Ordering::Greater | Ordering::Equal => {
1606 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1607 }
1608 Ordering::Less => {
1609 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
1610 }
1611 }
1612 }
1613
1614 pub(crate) fn txs_iter(
1617 &self,
1618 sender: SenderId,
1619 ) -> impl Iterator<Item = (&TransactionId, &PoolInternalTransaction<T>)> + '_ {
1620 self.txs
1621 .range((sender.start_bound(), Unbounded))
1622 .take_while(move |(other, _)| sender == other.sender)
1623 }
1624
1625 #[cfg(test)]
1628 #[expect(dead_code)]
1629 pub(crate) fn txs_iter_mut(
1630 &mut self,
1631 sender: SenderId,
1632 ) -> impl Iterator<Item = (&TransactionId, &mut PoolInternalTransaction<T>)> + '_ {
1633 self.txs
1634 .range_mut((sender.start_bound(), Unbounded))
1635 .take_while(move |(other, _)| sender == other.sender)
1636 }
1637
1638 pub(crate) fn descendant_txs_exclusive<'a, 'b: 'a>(
1642 &'a self,
1643 id: &'b TransactionId,
1644 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1645 self.txs.range((Excluded(id), Unbounded)).take_while(|(other, _)| id.sender == other.sender)
1646 }
1647
1648 pub(crate) fn descendant_txs_inclusive<'a, 'b: 'a>(
1653 &'a self,
1654 id: &'b TransactionId,
1655 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1656 self.txs.range(id..).take_while(|(other, _)| id.sender == other.sender)
1657 }
1658
1659 pub(crate) fn descendant_txs_mut<'a, 'b: 'a>(
1664 &'a mut self,
1665 id: &'b TransactionId,
1666 ) -> impl Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)> + 'a {
1667 self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender)
1668 }
1669
1670 pub(crate) fn remove_transaction_by_hash(
1672 &mut self,
1673 tx_hash: &B256,
1674 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1675 let tx = self.by_hash.remove(tx_hash)?;
1676 let internal = self.txs.remove(&tx.transaction_id)?;
1677 self.remove_auths(&internal);
1678 self.tx_decr(tx.sender_id());
1680 Some((tx, internal.subpool))
1681 }
1682
1683 pub(crate) fn remove_transaction_by_id(
1687 &mut self,
1688 tx_id: &TransactionId,
1689 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1690 let internal = self.txs.remove(tx_id)?;
1691 let tx = self.by_hash.remove(internal.transaction.hash())?;
1692 self.remove_auths(&internal);
1693 self.tx_decr(tx.sender_id());
1695 Some((tx, internal.subpool))
1696 }
1697
1698 pub(crate) fn park_descendant_transactions(
1700 &mut self,
1701 tx_id: &TransactionId,
1702 ) -> Vec<PoolUpdate> {
1703 let mut updates = Vec::new();
1704
1705 for (id, tx) in self.descendant_txs_mut(tx_id) {
1706 let current_pool = tx.subpool;
1707
1708 tx.state.remove(TxState::NO_NONCE_GAPS);
1709
1710 tx.subpool = tx.state.into();
1712
1713 if current_pool != tx.subpool {
1715 updates.push(PoolUpdate {
1716 id: *id,
1717 current: current_pool,
1718 destination: tx.subpool.into(),
1719 })
1720 }
1721 }
1722
1723 updates
1724 }
1725
1726 pub(crate) fn remove_transaction(
1732 &mut self,
1733 id: &TransactionId,
1734 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1735 let internal = self.txs.remove(id)?;
1736
1737 self.tx_decr(internal.transaction.sender_id());
1739
1740 let result =
1741 self.by_hash.remove(internal.transaction.hash()).map(|tx| (tx, internal.subpool));
1742
1743 self.remove_auths(&internal);
1744
1745 result
1746 }
1747
1748 fn remove_auths(&mut self, tx: &PoolInternalTransaction<T>) {
1752 let Some(auths) = &tx.transaction.authority_ids else { return };
1753
1754 let tx_hash = tx.transaction.hash();
1755 for auth in auths {
1756 if let Some(list) = self.auths.get_mut(auth) {
1757 list.remove(tx_hash);
1758 if list.is_empty() {
1759 self.auths.remove(auth);
1760 }
1761 }
1762 }
1763 }
1764
1765 #[inline]
1771 fn contains_conflicting_transaction(&self, tx: &ValidPoolTransaction<T>) -> bool {
1772 self.txs_iter(tx.transaction_id.sender)
1773 .next()
1774 .is_some_and(|(_, existing)| tx.tx_type_conflicts_with(&existing.transaction))
1775 }
1776
1777 fn ensure_valid(
1786 &self,
1787 transaction: ValidPoolTransaction<T>,
1788 on_chain_nonce: u64,
1789 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1790 if !self.local_transactions_config.is_local(transaction.origin, transaction.sender_ref()) {
1791 let current_txs =
1792 self.tx_counter.get(&transaction.sender_id()).copied().unwrap_or_default();
1793
1794 if current_txs >= self.max_account_slots && transaction.nonce() > on_chain_nonce {
1797 return Err(InsertErr::ExceededSenderTransactionsCapacity {
1798 transaction: Arc::new(transaction),
1799 })
1800 }
1801 }
1802 if transaction.gas_limit() > self.block_gas_limit {
1803 return Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas {
1804 block_gas_limit: self.block_gas_limit,
1805 tx_gas_limit: transaction.gas_limit(),
1806 transaction: Arc::new(transaction),
1807 })
1808 }
1809
1810 if self.contains_conflicting_transaction(&transaction) {
1811 return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) })
1813 }
1814
1815 Ok(transaction)
1816 }
1817
1818 fn ensure_valid_blob_transaction(
1824 &self,
1825 new_blob_tx: ValidPoolTransaction<T>,
1826 on_chain_balance: U256,
1827 ancestor: Option<TransactionId>,
1828 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1829 if let Some(ancestor) = ancestor {
1830 let Some(ancestor_tx) = self.txs.get(&ancestor) else {
1831 self.metrics.blob_transactions_nonce_gaps.increment(1);
1833 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1834 };
1835 if ancestor_tx.state.has_nonce_gap() {
1836 self.metrics.blob_transactions_nonce_gaps.increment(1);
1839 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1840 }
1841
1842 let mut cumulative_cost = ancestor_tx.next_cumulative_cost() + new_blob_tx.cost();
1844
1845 if cumulative_cost > on_chain_balance {
1847 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1849 }
1850
1851 let id = new_blob_tx.transaction_id;
1854 let mut descendants = self.descendant_txs_inclusive(&id).peekable();
1855 if let Some((maybe_replacement, _)) = descendants.peek() &&
1856 **maybe_replacement == new_blob_tx.transaction_id
1857 {
1858 descendants.next();
1860
1861 for (_, tx) in descendants {
1863 cumulative_cost += tx.transaction.cost();
1864 if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance {
1865 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1867 }
1868 }
1869 }
1870 } else if new_blob_tx.cost() > &on_chain_balance {
1871 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1873 }
1874
1875 Ok(new_blob_tx)
1876 }
1877
1878 pub(crate) fn insert_tx(
1910 &mut self,
1911 transaction: ValidPoolTransaction<T>,
1912 on_chain_balance: U256,
1913 on_chain_nonce: u64,
1914 ) -> InsertResult<T> {
1915 assert!(on_chain_nonce <= transaction.nonce(), "Invalid transaction");
1916
1917 let mut transaction = self.ensure_valid(transaction, on_chain_nonce)?;
1918
1919 let inserted_tx_id = *transaction.id();
1920 let mut state = TxState::default();
1921 let mut cumulative_cost = U256::ZERO;
1922 let mut updates = Vec::new();
1923
1924 state.insert(TxState::NOT_TOO_MUCH_GAS);
1926
1927 let ancestor = TransactionId::ancestor(
1930 transaction.transaction.nonce(),
1931 on_chain_nonce,
1932 inserted_tx_id.sender,
1933 );
1934
1935 if transaction.is_eip4844() {
1938 state.insert(TxState::BLOB_TRANSACTION);
1939
1940 transaction =
1941 self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
1942 let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();
1943 if blob_fee_cap >= self.pending_fees.blob_fee {
1944 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1945 }
1946 } else {
1947 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1949 }
1950
1951 let transaction = Arc::new(transaction);
1952
1953 if ancestor.is_none() {
1955 state.insert(TxState::NO_NONCE_GAPS);
1956 state.insert(TxState::NO_PARKED_ANCESTORS);
1957 }
1958
1959 let fee_cap = transaction.max_fee_per_gas();
1961
1962 if fee_cap < self.minimal_protocol_basefee as u128 {
1963 return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap })
1964 }
1965 if fee_cap >= self.pending_fees.base_fee as u128 {
1966 state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1967 }
1968
1969 let mut replaced_tx = None;
1971
1972 let pool_tx = PoolInternalTransaction {
1973 transaction: Arc::clone(&transaction),
1974 subpool: state.into(),
1975 state,
1976 cumulative_cost,
1977 };
1978
1979 match self.txs.entry(*transaction.id()) {
1981 Entry::Vacant(entry) => {
1982 self.by_hash.insert(*pool_tx.transaction.hash(), pool_tx.transaction.clone());
1984 entry.insert(pool_tx);
1985 }
1986 Entry::Occupied(mut entry) => {
1987 let existing_transaction = entry.get().transaction.as_ref();
1989 let maybe_replacement = transaction.as_ref();
1990
1991 if existing_transaction.is_underpriced(maybe_replacement, &self.price_bumps) {
1993 return Err(InsertErr::Underpriced {
1994 transaction: pool_tx.transaction,
1995 existing: *entry.get().transaction.hash(),
1996 })
1997 }
1998 let new_hash = *pool_tx.transaction.hash();
1999 let new_transaction = pool_tx.transaction.clone();
2000 let replaced = entry.insert(pool_tx);
2001 self.by_hash.remove(replaced.transaction.hash());
2002 self.by_hash.insert(new_hash, new_transaction);
2003
2004 self.remove_auths(&replaced);
2005
2006 replaced_tx = Some((replaced.transaction, replaced.subpool));
2008 }
2009 }
2010
2011 if let Some(auths) = &transaction.authority_ids {
2012 let tx_hash = transaction.hash();
2013 for auth in auths {
2014 self.auths.entry(*auth).or_default().insert(*tx_hash);
2015 }
2016 }
2017
2018 let on_chain_id = TransactionId::new(transaction.sender_id(), on_chain_nonce);
2020 {
2021 let mut next_nonce = on_chain_id.nonce;
2023
2024 let mut has_parked_ancestor = false;
2028
2029 for (id, tx) in self.descendant_txs_mut(&on_chain_id) {
2032 let current_pool = tx.subpool;
2033
2034 if next_nonce != id.nonce {
2036 break
2037 }
2038
2039 tx.state.insert(TxState::NO_NONCE_GAPS);
2041
2042 tx.cumulative_cost = cumulative_cost;
2044
2045 cumulative_cost = tx.next_cumulative_cost();
2047
2048 if cumulative_cost > on_chain_balance {
2049 tx.state.remove(TxState::ENOUGH_BALANCE);
2051 } else {
2052 tx.state.insert(TxState::ENOUGH_BALANCE);
2053 }
2054
2055 if has_parked_ancestor {
2057 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
2058 } else {
2059 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
2060 }
2061 has_parked_ancestor = !tx.state.is_pending();
2062
2063 tx.subpool = tx.state.into();
2065
2066 if inserted_tx_id.eq(id) {
2067 state = tx.state;
2069 } else {
2070 if current_pool != tx.subpool {
2072 updates.push(PoolUpdate {
2073 id: *id,
2074 current: current_pool,
2075 destination: tx.subpool.into(),
2076 })
2077 }
2078 }
2079
2080 next_nonce = id.next_nonce();
2082 }
2083 }
2084
2085 if replaced_tx.is_none() {
2087 self.tx_inc(inserted_tx_id.sender);
2088 }
2089
2090 self.update_size_metrics();
2091
2092 Ok(InsertOk { transaction, move_to: state.into(), state, replaced_tx, updates })
2093 }
2094
2095 pub(crate) fn len(&self) -> usize {
2097 self.txs.len()
2098 }
2099
2100 pub(crate) fn is_empty(&self) -> bool {
2102 self.txs.is_empty()
2103 }
2104
2105 #[cfg(any(test, feature = "test-utils"))]
2107 pub(crate) fn assert_invariants(&self) {
2108 assert_eq!(self.by_hash.len(), self.txs.len(), "by_hash.len() != txs.len()");
2109 assert!(self.auths.len() <= self.txs.len(), "auths.len() > txs.len()");
2110 }
2111}
2112
2113#[cfg(test)]
2114impl<T: PoolTransaction> AllTransactions<T> {
2115 pub(crate) fn tx_count(&self, sender: SenderId) -> usize {
2119 self.tx_counter.get(&sender).copied().unwrap_or_default()
2120 }
2121}
2122
2123impl<T: PoolTransaction> Default for AllTransactions<T> {
2124 fn default() -> Self {
2125 Self {
2126 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
2127 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
2128 block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
2129 by_hash: Default::default(),
2130 txs: Default::default(),
2131 tx_counter: Default::default(),
2132 last_seen_block_number: Default::default(),
2133 last_seen_block_hash: Default::default(),
2134 pending_fees: Default::default(),
2135 price_bumps: Default::default(),
2136 local_transactions_config: Default::default(),
2137 auths: Default::default(),
2138 metrics: Default::default(),
2139 }
2140 }
2141}
2142
2143#[derive(Debug, Clone)]
2145pub(crate) struct PendingFees {
2146 pub(crate) base_fee: u64,
2148 pub(crate) blob_fee: u128,
2150}
2151
2152impl Default for PendingFees {
2153 fn default() -> Self {
2154 Self { base_fee: Default::default(), blob_fee: BLOB_TX_MIN_BLOB_GASPRICE }
2155 }
2156}
2157
2158pub(crate) type InsertResult<T> = Result<InsertOk<T>, InsertErr<T>>;
2160
2161#[derive(Debug)]
2163pub(crate) enum InsertErr<T: PoolTransaction> {
2164 Underpriced {
2166 transaction: Arc<ValidPoolTransaction<T>>,
2167 #[expect(dead_code)]
2168 existing: TxHash,
2169 },
2170 BlobTxHasNonceGap { transaction: Arc<ValidPoolTransaction<T>> },
2172 Overdraft { transaction: Arc<ValidPoolTransaction<T>> },
2175 FeeCapBelowMinimumProtocolFeeCap { transaction: Arc<ValidPoolTransaction<T>>, fee_cap: u128 },
2179 ExceededSenderTransactionsCapacity { transaction: Arc<ValidPoolTransaction<T>> },
2183 TxGasLimitMoreThanAvailableBlockGas {
2185 transaction: Arc<ValidPoolTransaction<T>>,
2186 block_gas_limit: u64,
2187 tx_gas_limit: u64,
2188 },
2189 TxTypeConflict { transaction: Arc<ValidPoolTransaction<T>> },
2191}
2192
2193#[derive(Debug)]
2195pub(crate) struct InsertOk<T: PoolTransaction> {
2196 transaction: Arc<ValidPoolTransaction<T>>,
2198 move_to: SubPool,
2200 state: TxState,
2202 replaced_tx: Option<(Arc<ValidPoolTransaction<T>>, SubPool)>,
2204 updates: Vec<PoolUpdate>,
2206}
2207
2208#[derive(Debug)]
2211pub(crate) struct PoolInternalTransaction<T: PoolTransaction> {
2212 pub(crate) transaction: Arc<ValidPoolTransaction<T>>,
2214 pub(crate) subpool: SubPool,
2216 pub(crate) state: TxState,
2219 pub(crate) cumulative_cost: U256,
2224}
2225
2226impl<T: PoolTransaction> PoolInternalTransaction<T> {
2229 fn next_cumulative_cost(&self) -> U256 {
2230 self.cumulative_cost + self.transaction.cost()
2231 }
2232}
2233
2234#[derive(Debug, Clone, Default)]
2236pub(crate) struct SenderInfo {
2237 pub(crate) state_nonce: u64,
2239 pub(crate) balance: U256,
2241}
2242
2243impl SenderInfo {
2246 const fn update(&mut self, state_nonce: u64, balance: U256) {
2248 *self = Self { state_nonce, balance };
2249 }
2250}
2251
2252#[cfg(test)]
2253mod tests {
2254 use super::*;
2255 use crate::{
2256 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
2257 traits::TransactionOrigin,
2258 SubPoolLimit,
2259 };
2260 use alloy_consensus::{Transaction, TxType};
2261 use alloy_primitives::address;
2262
2263 #[test]
2264 fn test_insert_blob() {
2265 let on_chain_balance = U256::MAX;
2266 let on_chain_nonce = 0;
2267 let mut f = MockTransactionFactory::default();
2268 let mut pool = AllTransactions::default();
2269 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2270 let valid_tx = f.validated(tx);
2271 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2272 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2273 assert!(updates.is_empty());
2274 assert!(replaced_tx.is_none());
2275 assert!(state.contains(TxState::NO_NONCE_GAPS));
2276 assert!(state.contains(TxState::ENOUGH_BALANCE));
2277 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2278 assert_eq!(move_to, SubPool::Pending);
2279
2280 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2281 assert_eq!(inserted.subpool, SubPool::Pending);
2282 }
2283
2284 #[test]
2285 fn test_insert_blob_not_enough_blob_fee() {
2286 let on_chain_balance = U256::MAX;
2287 let on_chain_nonce = 0;
2288 let mut f = MockTransactionFactory::default();
2289 let mut pool = AllTransactions {
2290 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2291 ..Default::default()
2292 };
2293 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2294 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2295 let valid_tx = f.validated(tx);
2296 let InsertOk { state, .. } =
2297 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2298 assert!(state.contains(TxState::NO_NONCE_GAPS));
2299 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2300
2301 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2302 }
2303
2304 #[test]
2305 fn test_valid_tx_with_decreasing_blob_fee() {
2306 let on_chain_balance = U256::MAX;
2307 let on_chain_nonce = 0;
2308 let mut f = MockTransactionFactory::default();
2309 let mut pool = AllTransactions {
2310 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2311 ..Default::default()
2312 };
2313 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2314
2315 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2316 let valid_tx = f.validated(tx.clone());
2317 let InsertOk { state, .. } =
2318 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2319 assert!(state.contains(TxState::NO_NONCE_GAPS));
2320 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2321
2322 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2323 pool.remove_transaction(&valid_tx.transaction_id);
2324
2325 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
2326 let InsertOk { state, .. } =
2327 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2328 assert!(state.contains(TxState::NO_NONCE_GAPS));
2329 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2330 }
2331
2332 #[test]
2333 fn test_demote_valid_tx_with_increasing_blob_fee() {
2334 let on_chain_balance = U256::MAX;
2335 let on_chain_nonce = 0;
2336 let mut f = MockTransactionFactory::default();
2337 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2338 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2339
2340 let mut block_info = pool.block_info();
2342 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2343 pool.set_block_info(block_info);
2344
2345 let validated = f.validated(tx.clone());
2346 let id = *validated.id();
2347 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2348
2349 assert!(pool.blob_pool.is_empty());
2351 assert_eq!(pool.pending_pool.len(), 1);
2352
2353 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2355 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2356 assert_eq!(internal_tx.subpool, SubPool::Pending);
2357
2358 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2360 pool.set_block_info(block_info);
2361
2362 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2364 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2365 assert_eq!(internal_tx.subpool, SubPool::Blob);
2366
2367 assert_eq!(pool.blob_pool.len(), 1);
2369 assert!(pool.pending_pool.is_empty());
2370 }
2371
2372 #[test]
2373 fn test_promote_valid_tx_with_decreasing_blob_fee() {
2374 let on_chain_balance = U256::MAX;
2375 let on_chain_nonce = 0;
2376 let mut f = MockTransactionFactory::default();
2377 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2378 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2379
2380 let mut block_info = pool.block_info();
2382 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2383 pool.set_block_info(block_info);
2384
2385 let validated = f.validated(tx.clone());
2386 let id = *validated.id();
2387 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2388
2389 assert!(pool.pending_pool.is_empty());
2391 assert_eq!(pool.blob_pool.len(), 1);
2392
2393 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2395 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2396 assert_eq!(internal_tx.subpool, SubPool::Blob);
2397
2398 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2400 pool.set_block_info(block_info);
2401
2402 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2404 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2405 assert_eq!(internal_tx.subpool, SubPool::Pending);
2406
2407 assert_eq!(pool.pending_pool.len(), 1);
2409 assert!(pool.blob_pool.is_empty());
2410 }
2411
2412 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
2414 struct PromotionTest {
2415 basefee: u64,
2417 blobfee: u128,
2419 subpool: SubPool,
2421 basefee_update: u64,
2423 blobfee_update: u128,
2425 new_subpool: SubPool,
2427 }
2428
2429 impl PromotionTest {
2430 const fn opposite(&self) -> Self {
2432 Self {
2433 basefee: self.basefee_update,
2434 blobfee: self.blobfee_update,
2435 subpool: self.new_subpool,
2436 blobfee_update: self.blobfee,
2437 basefee_update: self.basefee,
2438 new_subpool: self.subpool,
2439 }
2440 }
2441
2442 fn assert_subpool_lengths<T: TransactionOrdering>(
2443 &self,
2444 pool: &TxPool<T>,
2445 failure_message: String,
2446 check_subpool: SubPool,
2447 ) {
2448 match check_subpool {
2449 SubPool::Blob => {
2450 assert_eq!(pool.blob_pool.len(), 1, "{failure_message}");
2451 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2452 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2453 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2454 }
2455 SubPool::Pending => {
2456 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2457 assert_eq!(pool.pending_pool.len(), 1, "{failure_message}");
2458 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2459 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2460 }
2461 SubPool::BaseFee => {
2462 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2463 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2464 assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}");
2465 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2466 }
2467 SubPool::Queued => {
2468 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2469 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2470 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2471 assert_eq!(pool.queued_pool.len(), 1, "{failure_message}");
2472 }
2473 }
2474 }
2475
2476 fn assert_single_tx_starting_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2480 self.assert_subpool_lengths(
2481 pool,
2482 format!("pool length check failed at start of test: {self:?}"),
2483 self.subpool,
2484 );
2485 }
2486
2487 fn assert_single_tx_ending_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2491 self.assert_subpool_lengths(
2492 pool,
2493 format!("pool length check failed at end of test: {self:?}"),
2494 self.new_subpool,
2495 );
2496 }
2497 }
2498
2499 #[test]
2500 fn test_promote_blob_tx_with_both_pending_fee_updates() {
2501 let on_chain_balance = U256::MAX;
2504 let on_chain_nonce = 0;
2505 let mut f = MockTransactionFactory::default();
2506 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2507
2508 let max_fee_per_blob_gas = tx.max_fee_per_blob_gas().unwrap();
2509 let max_fee_per_gas = tx.max_fee_per_gas() as u64;
2510
2511 let mut expected_promotions = vec![
2513 PromotionTest {
2514 blobfee: max_fee_per_blob_gas + 1,
2515 basefee: max_fee_per_gas + 1,
2516 subpool: SubPool::Blob,
2517 blobfee_update: max_fee_per_blob_gas + 1,
2518 basefee_update: max_fee_per_gas + 1,
2519 new_subpool: SubPool::Blob,
2520 },
2521 PromotionTest {
2522 blobfee: max_fee_per_blob_gas + 1,
2523 basefee: max_fee_per_gas + 1,
2524 subpool: SubPool::Blob,
2525 blobfee_update: max_fee_per_blob_gas,
2526 basefee_update: max_fee_per_gas + 1,
2527 new_subpool: SubPool::Blob,
2528 },
2529 PromotionTest {
2530 blobfee: max_fee_per_blob_gas + 1,
2531 basefee: max_fee_per_gas + 1,
2532 subpool: SubPool::Blob,
2533 blobfee_update: max_fee_per_blob_gas + 1,
2534 basefee_update: max_fee_per_gas,
2535 new_subpool: SubPool::Blob,
2536 },
2537 PromotionTest {
2538 blobfee: max_fee_per_blob_gas + 1,
2539 basefee: max_fee_per_gas + 1,
2540 subpool: SubPool::Blob,
2541 blobfee_update: max_fee_per_blob_gas,
2542 basefee_update: max_fee_per_gas,
2543 new_subpool: SubPool::Pending,
2544 },
2545 PromotionTest {
2546 blobfee: max_fee_per_blob_gas,
2547 basefee: max_fee_per_gas + 1,
2548 subpool: SubPool::Blob,
2549 blobfee_update: max_fee_per_blob_gas,
2550 basefee_update: max_fee_per_gas,
2551 new_subpool: SubPool::Pending,
2552 },
2553 PromotionTest {
2554 blobfee: max_fee_per_blob_gas + 1,
2555 basefee: max_fee_per_gas,
2556 subpool: SubPool::Blob,
2557 blobfee_update: max_fee_per_blob_gas,
2558 basefee_update: max_fee_per_gas,
2559 new_subpool: SubPool::Pending,
2560 },
2561 PromotionTest {
2562 blobfee: max_fee_per_blob_gas,
2563 basefee: max_fee_per_gas,
2564 subpool: SubPool::Pending,
2565 blobfee_update: max_fee_per_blob_gas,
2566 basefee_update: max_fee_per_gas,
2567 new_subpool: SubPool::Pending,
2568 },
2569 ];
2570
2571 let reversed = expected_promotions.iter().map(|test| test.opposite()).collect::<Vec<_>>();
2573 expected_promotions.extend(reversed);
2574
2575 let expected_promotions = expected_promotions.into_iter().collect::<HashSet<_>>();
2577
2578 for promotion_test in &expected_promotions {
2579 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2580
2581 let mut block_info = pool.block_info();
2583
2584 block_info.pending_blob_fee = Some(promotion_test.blobfee);
2585 block_info.pending_basefee = promotion_test.basefee;
2586 pool.set_block_info(block_info);
2587
2588 let validated = f.validated(tx.clone());
2589 let id = *validated.id();
2590 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2591
2592 promotion_test.assert_single_tx_starting_subpool(&pool);
2594
2595 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2597 assert_eq!(
2598 internal_tx.subpool, promotion_test.subpool,
2599 "Subpools do not match at start of test: {promotion_test:?}"
2600 );
2601
2602 block_info.pending_basefee = promotion_test.basefee_update;
2604 block_info.pending_blob_fee = Some(promotion_test.blobfee_update);
2605 pool.set_block_info(block_info);
2606
2607 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2609 assert_eq!(
2610 internal_tx.subpool, promotion_test.new_subpool,
2611 "Subpools do not match at end of test: {promotion_test:?}"
2612 );
2613
2614 promotion_test.assert_single_tx_ending_subpool(&pool);
2616 }
2617 }
2618
2619 #[test]
2620 fn test_insert_pending() {
2621 let on_chain_balance = U256::MAX;
2622 let on_chain_nonce = 0;
2623 let mut f = MockTransactionFactory::default();
2624 let mut pool = AllTransactions::default();
2625 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2626 let valid_tx = f.validated(tx);
2627 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2628 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2629 assert!(updates.is_empty());
2630 assert!(replaced_tx.is_none());
2631 assert!(state.contains(TxState::NO_NONCE_GAPS));
2632 assert!(state.contains(TxState::ENOUGH_BALANCE));
2633 assert_eq!(move_to, SubPool::Pending);
2634
2635 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2636 assert_eq!(inserted.subpool, SubPool::Pending);
2637 }
2638
2639 #[test]
2640 fn test_simple_insert() {
2641 let on_chain_balance = U256::ZERO;
2642 let on_chain_nonce = 0;
2643 let mut f = MockTransactionFactory::default();
2644 let mut pool = AllTransactions::default();
2645 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2646 tx.set_priority_fee(100);
2647 tx.set_max_fee(100);
2648 let valid_tx = f.validated(tx.clone());
2649 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2650 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2651 assert!(updates.is_empty());
2652 assert!(replaced_tx.is_none());
2653 assert!(state.contains(TxState::NO_NONCE_GAPS));
2654 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2655 assert_eq!(move_to, SubPool::Queued);
2656
2657 assert_eq!(pool.len(), 1);
2658 assert!(pool.contains(valid_tx.hash()));
2659 let expected_state = TxState::ENOUGH_FEE_CAP_BLOCK | TxState::NO_NONCE_GAPS;
2660 let inserted = pool.get(valid_tx.id()).unwrap();
2661 assert!(inserted.state.intersects(expected_state));
2662
2663 let res = pool.insert_tx(valid_tx, on_chain_balance, on_chain_nonce);
2665 res.unwrap_err();
2666 assert_eq!(pool.len(), 1);
2667
2668 let valid_tx = f.validated(tx.next());
2669 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2670 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2671
2672 assert!(updates.is_empty());
2673 assert!(replaced_tx.is_none());
2674 assert!(state.contains(TxState::NO_NONCE_GAPS));
2675 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2676 assert_eq!(move_to, SubPool::Queued);
2677
2678 assert!(pool.contains(valid_tx.hash()));
2679 assert_eq!(pool.len(), 2);
2680 let inserted = pool.get(valid_tx.id()).unwrap();
2681 assert!(inserted.state.intersects(expected_state));
2682 }
2683
2684 #[test]
2685 fn test_on_canonical_state_change_no_double_processing() {
2688 let mut tx_factory = MockTransactionFactory::default();
2689 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2690
2691 let tx = MockTransaction::eip1559().with_gas_price(50).with_gas_limit(30_000);
2693 let sender = tx.sender();
2694
2695 let mut block_info = pool.block_info();
2697 block_info.pending_basefee = 100;
2698 pool.set_block_info(block_info);
2699
2700 let validated = tx_factory.validated(tx);
2701 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2702
2703 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2705
2706 assert_eq!(pool.basefee_pool.len(), 1);
2707 assert_eq!(pool.pending_pool.len(), 0);
2708
2709 block_info.pending_basefee = 40;
2713
2714 let mut changed_senders = FxHashMap::default();
2715 changed_senders.insert(
2716 sender_id,
2717 SenderInfo {
2718 state_nonce: 0,
2719 balance: U256::from(20_000_000), },
2721 );
2722
2723 let outcome = pool.on_canonical_state_change(
2724 block_info,
2725 vec![], changed_senders,
2727 PoolUpdateKind::Commit,
2728 );
2729
2730 assert_eq!(pool.pending_pool.len(), 1, "Transaction should be in pending pool");
2732 assert_eq!(pool.basefee_pool.len(), 0, "Transaction should not be in basefee pool");
2733 assert_eq!(outcome.promoted.len(), 1, "Should report exactly one promotion");
2734 }
2735
2736 #[test]
2737 fn test_canonical_state_change_with_basefee_update_regression() {
2740 let mut tx_factory = MockTransactionFactory::default();
2741 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2742
2743 let sender_balance = U256::from(100_000_000);
2745
2746 let tx1 =
2748 MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000).with_nonce(0);
2749 let sender1 = tx1.sender();
2750
2751 let tx2 =
2753 MockTransaction::eip1559().with_gas_price(55).with_gas_limit(21_000).with_nonce(0);
2754 let sender2 = tx2.sender();
2755
2756 let tx3 =
2758 MockTransaction::eip1559().with_gas_price(45).with_gas_limit(21_000).with_nonce(0);
2759 let sender3 = tx3.sender();
2760
2761 let mut block_info = pool.block_info();
2763 block_info.pending_basefee = 70;
2764 pool.set_block_info(block_info);
2765
2766 let validated1 = tx_factory.validated(tx1);
2768 let validated2 = tx_factory.validated(tx2);
2769 let validated3 = tx_factory.validated(tx3);
2770
2771 pool.add_transaction(validated1, sender_balance, 0, None).unwrap();
2772 pool.add_transaction(validated2, sender_balance, 0, None).unwrap();
2773 pool.add_transaction(validated3, sender_balance, 0, None).unwrap();
2774
2775 let sender1_id = tx_factory.ids.sender_id(&sender1).unwrap();
2776 let sender2_id = tx_factory.ids.sender_id(&sender2).unwrap();
2777 let sender3_id = tx_factory.ids.sender_id(&sender3).unwrap();
2778
2779 assert_eq!(pool.basefee_pool.len(), 3, "All txs should be in basefee pool");
2781 assert_eq!(pool.pending_pool.len(), 0, "No txs should be in pending pool");
2782
2783 block_info.pending_basefee = 50;
2786
2787 let mut changed_senders = FxHashMap::default();
2789 changed_senders.insert(
2790 sender1_id,
2791 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2792 );
2793 changed_senders.insert(
2794 sender2_id,
2795 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2796 );
2797 changed_senders.insert(
2798 sender3_id,
2799 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2800 );
2801
2802 let outcome = pool.on_canonical_state_change(
2803 block_info,
2804 vec![],
2805 changed_senders,
2806 PoolUpdateKind::Commit,
2807 );
2808
2809 assert_eq!(pool.pending_pool.len(), 2, "tx1 and tx2 should be promoted");
2811 assert_eq!(pool.basefee_pool.len(), 1, "tx3 should remain in basefee");
2812
2813 assert_eq!(
2816 outcome.promoted.len(),
2817 2,
2818 "Should report exactly 2 promotions, not double-counted"
2819 );
2820
2821 let promoted_prices: Vec<u128> =
2823 outcome.promoted.iter().map(|tx| tx.max_fee_per_gas()).collect();
2824 assert!(promoted_prices.contains(&60));
2825 assert!(promoted_prices.contains(&55));
2826 }
2827
2828 #[test]
2829 fn test_basefee_decrease_with_empty_senders() {
2830 let mut tx_factory = MockTransactionFactory::default();
2833 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2834
2835 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2837
2838 let mut block_info = pool.block_info();
2840 block_info.pending_basefee = 100;
2841 pool.set_block_info(block_info);
2842
2843 let validated = tx_factory.validated(tx);
2845 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2846
2847 assert_eq!(pool.basefee_pool.len(), 1);
2848 assert_eq!(pool.pending_pool.len(), 0);
2849
2850 block_info.pending_basefee = 50;
2852 let outcome = pool.on_canonical_state_change(
2853 block_info,
2854 vec![],
2855 FxHashMap::default(), PoolUpdateKind::Commit,
2857 );
2858
2859 assert_eq!(pool.pending_pool.len(), 1, "Fee decrease should promote tx");
2861 assert_eq!(pool.basefee_pool.len(), 0);
2862 assert_eq!(outcome.promoted.len(), 1, "Should report promotion from fee update");
2863 }
2864
2865 #[test]
2866 fn test_basefee_decrease_account_makes_unfundable() {
2867 let mut tx_factory = MockTransactionFactory::default();
2870 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2871
2872 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2873 let sender = tx.sender();
2874
2875 let mut block_info = pool.block_info();
2877 block_info.pending_basefee = 100;
2878 pool.set_block_info(block_info);
2879
2880 let validated = tx_factory.validated(tx);
2881 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2882 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2883
2884 assert_eq!(pool.basefee_pool.len(), 1);
2885
2886 block_info.pending_basefee = 50;
2888 let mut changed_senders = FxHashMap::default();
2889 changed_senders.insert(
2890 sender_id,
2891 SenderInfo {
2892 state_nonce: 0,
2893 balance: U256::from(100), },
2895 );
2896
2897 let outcome = pool.on_canonical_state_change(
2898 block_info,
2899 vec![],
2900 changed_senders,
2901 PoolUpdateKind::Commit,
2902 );
2903
2904 assert_eq!(pool.pending_pool.len(), 0, "Unfunded tx should not be in pending");
2906 assert_eq!(pool.basefee_pool.len(), 0, "Tx no longer in basefee pool");
2907 assert_eq!(pool.queued_pool.len(), 1, "Unfunded tx should be in queued pool");
2908
2909 let tx_count = pool.all_transactions.txs.len();
2911 assert_eq!(tx_count, 1, "Transaction should still be in pool (in queued)");
2912
2913 assert_eq!(outcome.promoted.len(), 0, "Should not report promotion");
2914 assert_eq!(outcome.discarded.len(), 0, "Queued tx is not reported as discarded");
2915 }
2916
2917 #[test]
2918 fn insert_already_imported() {
2919 let on_chain_balance = U256::ZERO;
2920 let on_chain_nonce = 0;
2921 let mut f = MockTransactionFactory::default();
2922 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2923 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2924 let tx = f.validated(tx);
2925 pool.add_transaction(tx.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
2926 match pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap_err().kind {
2927 PoolErrorKind::AlreadyImported => {}
2928 _ => unreachable!(),
2929 }
2930 }
2931
2932 #[test]
2933 fn insert_replace() {
2934 let on_chain_balance = U256::ZERO;
2935 let on_chain_nonce = 0;
2936 let mut f = MockTransactionFactory::default();
2937 let mut pool = AllTransactions::default();
2938 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2939 let first = f.validated(tx.clone());
2940 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
2941 let replacement = f.validated(tx.rng_hash().inc_price());
2942 let InsertOk { updates, replaced_tx, .. } =
2943 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
2944 assert!(updates.is_empty());
2945 let replaced = replaced_tx.unwrap();
2946 assert_eq!(replaced.0.hash(), first.hash());
2947
2948 assert!(!pool.contains(first.hash()));
2950 assert!(pool.contains(replacement.hash()));
2951 assert_eq!(pool.len(), 1);
2952 }
2953
2954 #[test]
2955 fn insert_replace_txpool() {
2956 let on_chain_balance = U256::ZERO;
2957 let on_chain_nonce = 0;
2958 let mut f = MockTransactionFactory::default();
2959 let mut pool = TxPool::mock();
2960
2961 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2962 let first = f.validated(tx.clone());
2963 let first_added =
2964 pool.add_transaction(first, on_chain_balance, on_chain_nonce, None).unwrap();
2965 let replacement = f.validated(tx.rng_hash().inc_price());
2966 let replacement_added = pool
2967 .add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce, None)
2968 .unwrap();
2969
2970 assert!(!pool.contains(first_added.hash()));
2972 assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));
2974
2975 assert!(pool.contains(replacement.hash()));
2976 let size = pool.size();
2977 assert_eq!(size.total, 1);
2978 size.assert_invariants();
2979 }
2980
2981 #[test]
2982 fn insert_replace_underpriced() {
2983 let on_chain_balance = U256::ZERO;
2984 let on_chain_nonce = 0;
2985 let mut f = MockTransactionFactory::default();
2986 let mut pool = AllTransactions::default();
2987 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2988 let first = f.validated(tx.clone());
2989 let _res = pool.insert_tx(first, on_chain_balance, on_chain_nonce);
2990 let mut replacement = f.validated(tx.rng_hash());
2991 replacement.transaction = replacement.transaction.decr_price();
2992 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
2993 assert!(matches!(err, InsertErr::Underpriced { .. }));
2994 }
2995
2996 #[test]
2997 fn insert_replace_underpriced_not_enough_bump() {
2998 let on_chain_balance = U256::ZERO;
2999 let on_chain_nonce = 0;
3000 let mut f = MockTransactionFactory::default();
3001 let mut pool = AllTransactions::default();
3002 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3003 tx.set_priority_fee(100);
3004 tx.set_max_fee(100);
3005 let first = f.validated(tx.clone());
3006 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3007 let mut replacement = f.validated(tx.rng_hash().inc_price());
3008
3009 replacement.transaction.set_priority_fee(109);
3011 replacement.transaction.set_max_fee(109);
3012 let err =
3013 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3014 assert!(matches!(err, InsertErr::Underpriced { .. }));
3015 assert!(pool.contains(first.hash()));
3017 assert_eq!(pool.len(), 1);
3018
3019 replacement.transaction.set_priority_fee(110);
3021 replacement.transaction.set_max_fee(109);
3022 let err =
3023 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3024 assert!(matches!(err, InsertErr::Underpriced { .. }));
3025 assert!(pool.contains(first.hash()));
3026 assert_eq!(pool.len(), 1);
3027
3028 replacement.transaction.set_priority_fee(109);
3030 replacement.transaction.set_max_fee(110);
3031 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3032 assert!(matches!(err, InsertErr::Underpriced { .. }));
3033 assert!(pool.contains(first.hash()));
3034 assert_eq!(pool.len(), 1);
3035 }
3036
3037 #[test]
3038 fn insert_conflicting_type_normal_to_blob() {
3039 let on_chain_balance = U256::from(10_000);
3040 let on_chain_nonce = 0;
3041 let mut f = MockTransactionFactory::default();
3042 let mut pool = AllTransactions::default();
3043 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3044 let first = f.validated(tx.clone());
3045 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3046 let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3047 let blob = f.validated(tx);
3048 let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
3049 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3050 }
3051
3052 #[test]
3053 fn insert_conflicting_type_blob_to_normal() {
3054 let on_chain_balance = U256::from(10_000);
3055 let on_chain_nonce = 0;
3056 let mut f = MockTransactionFactory::default();
3057 let mut pool = AllTransactions::default();
3058 let tx = MockTransaction::eip4844().inc_price().inc_limit();
3059 let first = f.validated(tx.clone());
3060 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3061 let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3062 let tx = f.validated(tx);
3063 let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
3064 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3065 }
3066
3067 #[test]
3069 fn insert_previous() {
3070 let on_chain_balance = U256::ZERO;
3071 let on_chain_nonce = 0;
3072 let mut f = MockTransactionFactory::default();
3073 let mut pool = AllTransactions::default();
3074 let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
3075 let first = f.validated(tx.clone());
3076 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3077
3078 let first_in_pool = pool.get(first.id()).unwrap();
3079
3080 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3082
3083 let prev = f.validated(tx.prev());
3084 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3085 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3086
3087 assert!(updates.is_empty());
3089 assert!(replaced_tx.is_none());
3090 assert!(state.contains(TxState::NO_NONCE_GAPS));
3091 assert_eq!(move_to, SubPool::Queued);
3092
3093 let first_in_pool = pool.get(first.id()).unwrap();
3094 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3096 }
3097
3098 #[test]
3100 fn insert_with_updates() {
3101 let on_chain_balance = U256::from(10_000);
3102 let on_chain_nonce = 0;
3103 let mut f = MockTransactionFactory::default();
3104 let mut pool = AllTransactions::default();
3105 let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
3106 let first = f.validated(tx.clone());
3107 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3108
3109 let first_in_pool = pool.get(first.id()).unwrap();
3110 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3112 assert_eq!(SubPool::Queued, first_in_pool.subpool);
3113
3114 let prev = f.validated(tx.prev());
3115 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3116 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3117
3118 assert_eq!(updates.len(), 1);
3120 assert!(replaced_tx.is_none());
3121 assert!(state.contains(TxState::NO_NONCE_GAPS));
3122 assert_eq!(move_to, SubPool::Pending);
3123
3124 let first_in_pool = pool.get(first.id()).unwrap();
3125 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3127 assert_eq!(SubPool::Pending, first_in_pool.subpool);
3128 }
3129
3130 #[test]
3131 fn insert_previous_blocking() {
3132 let on_chain_balance = U256::from(1_000);
3133 let on_chain_nonce = 0;
3134 let mut f = MockTransactionFactory::default();
3135 let mut pool = AllTransactions::default();
3136 pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
3137 let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
3138 let first = f.validated(tx.clone());
3139
3140 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3141
3142 let first_in_pool = pool.get(first.id()).unwrap();
3143
3144 assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
3145 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3147
3148 let prev = f.validated(tx.prev());
3149 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3150 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3151
3152 assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3153 assert!(updates.is_empty());
3155 assert!(replaced_tx.is_none());
3156 assert!(state.contains(TxState::NO_NONCE_GAPS));
3157 assert_eq!(move_to, SubPool::BaseFee);
3158
3159 let first_in_pool = pool.get(first.id()).unwrap();
3160 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3162 }
3163
3164 #[test]
3165 fn rejects_spammer() {
3166 let on_chain_balance = U256::from(1_000);
3167 let on_chain_nonce = 0;
3168 let mut f = MockTransactionFactory::default();
3169 let mut pool = AllTransactions::default();
3170
3171 let mut tx = MockTransaction::eip1559();
3172 let unblocked_tx = tx.clone();
3173 for _ in 0..pool.max_account_slots {
3174 tx = tx.next();
3175 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
3176 }
3177
3178 assert_eq!(
3179 pool.max_account_slots,
3180 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3181 );
3182
3183 let err =
3184 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
3185 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
3186
3187 assert!(pool
3188 .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
3189 .is_ok());
3190 }
3191
3192 #[test]
3193 fn allow_local_spamming() {
3194 let on_chain_balance = U256::from(1_000);
3195 let on_chain_nonce = 0;
3196 let mut f = MockTransactionFactory::default();
3197 let mut pool = AllTransactions::default();
3198
3199 let mut tx = MockTransaction::eip1559();
3200 for _ in 0..pool.max_account_slots {
3201 tx = tx.next();
3202 pool.insert_tx(
3203 f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
3204 on_chain_balance,
3205 on_chain_nonce,
3206 )
3207 .unwrap();
3208 }
3209
3210 assert_eq!(
3211 pool.max_account_slots,
3212 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3213 );
3214
3215 pool.insert_tx(
3216 f.validated_with_origin(TransactionOrigin::Local, tx.next()),
3217 on_chain_balance,
3218 on_chain_nonce,
3219 )
3220 .unwrap();
3221 }
3222
3223 #[test]
3224 fn reject_tx_over_gas_limit() {
3225 let on_chain_balance = U256::from(1_000);
3226 let on_chain_nonce = 0;
3227 let mut f = MockTransactionFactory::default();
3228 let mut pool = AllTransactions::default();
3229
3230 let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
3231
3232 assert!(matches!(
3233 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
3234 Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
3235 ));
3236 }
3237
3238 #[test]
3239 fn test_tx_equal_gas_limit() {
3240 let on_chain_balance = U256::from(1_000);
3241 let on_chain_nonce = 0;
3242 let mut f = MockTransactionFactory::default();
3243 let mut pool = AllTransactions::default();
3244
3245 let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
3246
3247 let InsertOk { state, .. } =
3248 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
3249 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
3250 }
3251
3252 #[test]
3253 fn update_basefee_subpools() {
3254 let mut f = MockTransactionFactory::default();
3255 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3256
3257 let tx = MockTransaction::eip1559().inc_price_by(10);
3258 let validated = f.validated(tx.clone());
3259 let id = *validated.id();
3260 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3261
3262 assert_eq!(pool.pending_pool.len(), 1);
3263
3264 pool.update_basefee((tx.max_fee_per_gas() + 1) as u64, |_| {});
3265
3266 assert!(pool.pending_pool.is_empty());
3267 assert_eq!(pool.basefee_pool.len(), 1);
3268
3269 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3270 }
3271
3272 #[test]
3273 fn update_basefee_subpools_setting_block_info() {
3274 let mut f = MockTransactionFactory::default();
3275 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3276
3277 let tx = MockTransaction::eip1559().inc_price_by(10);
3278 let validated = f.validated(tx.clone());
3279 let id = *validated.id();
3280 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3281
3282 assert_eq!(pool.pending_pool.len(), 1);
3283
3284 let mut block_info = pool.block_info();
3286 block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
3287 pool.set_block_info(block_info);
3288
3289 assert!(pool.pending_pool.is_empty());
3290 assert_eq!(pool.basefee_pool.len(), 1);
3291
3292 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3293 }
3294
3295 #[test]
3296 fn basefee_decrease_promotes_affordable_and_keeps_unaffordable() {
3297 use alloy_primitives::address;
3298 let mut f = MockTransactionFactory::default();
3299 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3300
3301 let sender_a = address!("0x000000000000000000000000000000000000000a");
3304 let sender_b = address!("0x000000000000000000000000000000000000000b");
3305 let sender_c = address!("0x000000000000000000000000000000000000000c");
3306
3307 let tx1 = MockTransaction::eip1559()
3308 .set_sender(sender_a)
3309 .set_nonce(0)
3310 .set_max_fee(500)
3311 .inc_limit();
3312 let tx2 = MockTransaction::eip1559()
3313 .set_sender(sender_b)
3314 .set_nonce(0)
3315 .set_max_fee(600)
3316 .inc_limit();
3317 let tx3 = MockTransaction::eip1559()
3318 .set_sender(sender_c)
3319 .set_nonce(0)
3320 .set_max_fee(400)
3321 .inc_limit();
3322
3323 let mut block_info = pool.block_info();
3325 block_info.pending_basefee = 700;
3326 pool.set_block_info(block_info);
3327
3328 let validated1 = f.validated(tx1);
3329 let validated2 = f.validated(tx2);
3330 let validated3 = f.validated(tx3);
3331 let id1 = *validated1.id();
3332 let id2 = *validated2.id();
3333 let id3 = *validated3.id();
3334
3335 pool.add_transaction(validated1, U256::from(10_000), 0, None).unwrap();
3339 pool.add_transaction(validated2, U256::from(10_000), 0, None).unwrap();
3340 pool.add_transaction(validated3, U256::from(10_000), 0, None).unwrap();
3341
3342 println!("Basefee pool len: {}", pool.basefee_pool.len());
3344 println!("Pending pool len: {}", pool.pending_pool.len());
3345 println!("tx1 subpool: {:?}", pool.all_transactions.txs.get(&id1).unwrap().subpool);
3346 println!("tx2 subpool: {:?}", pool.all_transactions.txs.get(&id2).unwrap().subpool);
3347 println!("tx3 subpool: {:?}", pool.all_transactions.txs.get(&id3).unwrap().subpool);
3348
3349 assert_eq!(pool.basefee_pool.len(), 3);
3351 assert_eq!(pool.pending_pool.len(), 0);
3352 assert_eq!(pool.all_transactions.txs.get(&id1).unwrap().subpool, SubPool::BaseFee);
3353 assert_eq!(pool.all_transactions.txs.get(&id2).unwrap().subpool, SubPool::BaseFee);
3354 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3355
3356 let mut block_info = pool.block_info();
3358 block_info.pending_basefee = 450; pool.set_block_info(block_info);
3360
3361 assert_eq!(pool.basefee_pool.len(), 1);
3366 assert_eq!(pool.pending_pool.len(), 2);
3367
3368 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3370
3371 let tx1_meta = pool.all_transactions.txs.get(&id1).unwrap();
3373 let tx2_meta = pool.all_transactions.txs.get(&id2).unwrap();
3374 assert_eq!(tx1_meta.subpool, SubPool::Pending);
3375 assert_eq!(tx2_meta.subpool, SubPool::Pending);
3376 assert!(tx1_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3377 assert!(tx2_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3378
3379 let best: Vec<_> = pool.best_transactions().take(3).collect();
3381 assert_eq!(best.len(), 2); assert!(best.iter().any(|tx| tx.id() == &id1));
3383 assert!(best.iter().any(|tx| tx.id() == &id2));
3384 }
3385
3386 #[test]
3387 fn apply_fee_updates_records_promotions_after_basefee_drop() {
3388 let mut f = MockTransactionFactory::default();
3389 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3390
3391 let tx = MockTransaction::eip1559()
3392 .with_gas_limit(21_000)
3393 .with_max_fee(500)
3394 .with_priority_fee(1);
3395 let validated = f.validated(tx);
3396 let id = *validated.id();
3397 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3398
3399 assert_eq!(pool.pending_pool.len(), 1);
3400
3401 pool.update_basefee(600, |_| {});
3403 assert!(pool.pending_pool.is_empty());
3404 assert_eq!(pool.basefee_pool.len(), 1);
3405
3406 let prev_base_fee = 600;
3407 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3408
3409 pool.all_transactions.pending_fees.base_fee = 400;
3411
3412 let mut outcome = UpdateOutcome::default();
3413 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3414
3415 assert_eq!(pool.pending_pool.len(), 1);
3416 assert!(pool.basefee_pool.is_empty());
3417 assert_eq!(outcome.promoted.len(), 1);
3418 assert_eq!(outcome.promoted[0].id(), &id);
3419 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3420 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3421
3422 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3423 assert_eq!(tx_meta.subpool, SubPool::Pending);
3424 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3425 }
3426
3427 #[test]
3428 fn apply_fee_updates_records_promotions_after_blob_fee_drop() {
3429 let mut f = MockTransactionFactory::default();
3430 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3431
3432 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3433
3434 let tx = MockTransaction::eip4844().with_blob_fee(initial_blob_fee + 100);
3435 let validated = f.validated(tx.clone());
3436 let id = *validated.id();
3437 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3438
3439 assert_eq!(pool.pending_pool.len(), 1);
3440
3441 let increased_blob_fee = tx.max_fee_per_blob_gas().unwrap() + 200;
3443 pool.update_blob_fee(increased_blob_fee, Ordering::Equal, |_| {});
3444 assert!(pool.pending_pool.is_empty());
3445 assert_eq!(pool.blob_pool.len(), 1);
3446
3447 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3448 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3449
3450 pool.all_transactions.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
3452
3453 let mut outcome = UpdateOutcome::default();
3454 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3455
3456 assert_eq!(pool.pending_pool.len(), 1);
3457 assert!(pool.blob_pool.is_empty());
3458 assert_eq!(outcome.promoted.len(), 1);
3459 assert_eq!(outcome.promoted[0].id(), &id);
3460 assert_eq!(pool.all_transactions.pending_fees.base_fee, prev_base_fee);
3461 assert_eq!(pool.all_transactions.pending_fees.blob_fee, tx.max_fee_per_blob_gas().unwrap());
3462
3463 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3464 assert_eq!(tx_meta.subpool, SubPool::Pending);
3465 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3466 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3467 }
3468
3469 #[test]
3470 fn apply_fee_updates_promotes_blob_after_basefee_drop() {
3471 let mut f = MockTransactionFactory::default();
3472 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3473
3474 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3475
3476 let tx = MockTransaction::eip4844()
3477 .with_max_fee(500)
3478 .with_priority_fee(1)
3479 .with_blob_fee(initial_blob_fee + 100);
3480 let validated = f.validated(tx);
3481 let id = *validated.id();
3482 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3483
3484 assert_eq!(pool.pending_pool.len(), 1);
3485
3486 let high_base_fee = 600;
3488 pool.update_basefee(high_base_fee, |_| {});
3489 assert!(pool.pending_pool.is_empty());
3490 assert_eq!(pool.blob_pool.len(), 1);
3491
3492 let prev_base_fee = high_base_fee;
3493 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3494
3495 pool.all_transactions.pending_fees.base_fee = 400;
3497
3498 let mut outcome = UpdateOutcome::default();
3499 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3500
3501 assert_eq!(pool.pending_pool.len(), 1);
3502 assert!(pool.blob_pool.is_empty());
3503 assert_eq!(outcome.promoted.len(), 1);
3504 assert_eq!(outcome.promoted[0].id(), &id);
3505 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3506 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3507
3508 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3509 assert_eq!(tx_meta.subpool, SubPool::Pending);
3510 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3511 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3512 }
3513
3514 #[test]
3515 fn apply_fee_updates_demotes_after_basefee_rise() {
3516 let mut f = MockTransactionFactory::default();
3517 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3518
3519 let tx = MockTransaction::eip1559()
3520 .with_gas_limit(21_000)
3521 .with_max_fee(400)
3522 .with_priority_fee(1);
3523 let validated = f.validated(tx);
3524 let id = *validated.id();
3525 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3526
3527 assert_eq!(pool.pending_pool.len(), 1);
3528
3529 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3530 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3531
3532 let new_base_fee = prev_base_fee + 1_000;
3534 pool.all_transactions.pending_fees.base_fee = new_base_fee;
3535
3536 let mut outcome = UpdateOutcome::default();
3537 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3538
3539 assert!(pool.pending_pool.is_empty());
3540 assert_eq!(pool.basefee_pool.len(), 1);
3541 assert!(outcome.promoted.is_empty());
3542 assert_eq!(pool.all_transactions.pending_fees.base_fee, new_base_fee);
3543 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3544
3545 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3546 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
3547 assert!(!tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3548 }
3549
3550 #[test]
3551 fn get_highest_transaction_by_sender_and_nonce() {
3552 let mut f = MockTransactionFactory::default();
3554 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3555
3556 let tx = MockTransaction::eip1559();
3558 pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0, None).unwrap();
3559
3560 let tx1 = tx.inc_price().next();
3562
3563 let tx1_validated = f.validated(tx1.clone());
3565 pool.add_transaction(tx1_validated, U256::from(1_000), 0, None).unwrap();
3566
3567 assert_eq!(
3569 pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
3570 Some(1)
3571 );
3572
3573 let highest_tx = pool
3575 .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
3576 .expect("Failed to retrieve highest transaction");
3577
3578 assert_eq!(highest_tx.as_ref().transaction, tx1);
3580 }
3581
3582 #[test]
3583 fn get_highest_consecutive_transaction_by_sender() {
3584 let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
3586 let mut f = MockTransactionFactory::default();
3587
3588 let sender = Address::random();
3590 let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
3591 for nonce in txs {
3592 let mut mock_tx = MockTransaction::eip1559();
3593 mock_tx.set_sender(sender);
3594 mock_tx.set_nonce(nonce);
3595
3596 let validated_tx = f.validated(mock_tx);
3597 pool.add_transaction(validated_tx, U256::from(1000), 0, None).unwrap();
3598 }
3599
3600 let sender_id = f.ids.sender_id(&sender).unwrap();
3602 let next_tx =
3603 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
3604 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
3605
3606 let next_tx =
3607 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
3608 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
3609
3610 let next_tx =
3611 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3612 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
3613
3614 let mut info = SenderInfo::default();
3616 info.update(8, U256::ZERO);
3617 pool.sender_info.insert(sender_id, info);
3618 let next_tx =
3619 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3620 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
3621 }
3622
3623 #[test]
3624 fn discard_nonce_too_low() {
3625 let mut f = MockTransactionFactory::default();
3626 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3627
3628 let tx = MockTransaction::eip1559().inc_price_by(10);
3629 let validated = f.validated(tx.clone());
3630 let id = *validated.id();
3631 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3632
3633 let next = tx.next();
3634 let validated = f.validated(next.clone());
3635 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3636
3637 assert_eq!(pool.pending_pool.len(), 2);
3638
3639 let mut changed_senders = HashMap::default();
3640 changed_senders.insert(
3641 id.sender,
3642 SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
3643 );
3644 let outcome = pool.update_accounts(changed_senders);
3645 assert_eq!(outcome.discarded.len(), 1);
3646 assert_eq!(pool.pending_pool.len(), 1);
3647 }
3648
3649 #[test]
3650 fn discard_with_large_blob_txs() {
3651 reth_tracing::init_test_tracing();
3653
3654 let mut f = MockTransactionFactory::default();
3656 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3657 let default_limits = pool.config.blob_limit;
3658
3659 let a_sender = address!("0x000000000000000000000000000000000000000a");
3662
3663 let mut block_info = pool.block_info();
3665 block_info.pending_blob_fee = Some(100);
3666 block_info.pending_basefee = 100;
3667
3668 pool.set_block_info(block_info);
3670
3671 let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
3673 .into_iter()
3674 .map(|mut tx| {
3675 tx.set_size(default_limits.max_size / 2 + 1);
3676 tx.set_max_fee((block_info.pending_basefee - 1).into());
3677 tx
3678 })
3679 .collect::<Vec<_>>();
3680
3681 for tx in a_txs {
3683 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3684 }
3685
3686 let removed = pool.discard_worst();
3688 assert_eq!(removed.len(), 1);
3689 }
3690
3691 #[test]
3692 fn discard_with_parked_large_txs() {
3693 reth_tracing::init_test_tracing();
3695
3696 let mut f = MockTransactionFactory::default();
3698 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3699 let default_limits = pool.config.queued_limit;
3700
3701 let a_sender = address!("0x000000000000000000000000000000000000000a");
3704
3705 let pool_base_fee = 100;
3707 pool.update_basefee(pool_base_fee, |_| {});
3708
3709 let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
3711 .into_iter()
3712 .map(|mut tx| {
3713 tx.set_size(default_limits.max_size / 2 + 1);
3714 tx.set_max_fee((pool_base_fee - 1).into());
3715 tx
3716 })
3717 .collect::<Vec<_>>();
3718
3719 for tx in a_txs {
3721 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3722 }
3723
3724 let removed = pool.discard_worst();
3726 assert_eq!(removed.len(), 1);
3727 }
3728
3729 #[test]
3730 fn discard_at_capacity() {
3731 let mut f = MockTransactionFactory::default();
3732 let queued_limit = SubPoolLimit::new(1000, usize::MAX);
3733 let mut pool =
3734 TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
3735
3736 for _ in 0..queued_limit.max_txs {
3738 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3739 let validated = f.validated(tx.clone());
3740 let _id = *validated.id();
3741 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3742 }
3743
3744 let size = pool.size();
3745 assert_eq!(size.queued, queued_limit.max_txs);
3746
3747 for _ in 0..queued_limit.max_txs {
3748 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3749 let validated = f.validated(tx.clone());
3750 let _id = *validated.id();
3751 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3752
3753 pool.discard_worst();
3754 pool.assert_invariants();
3755 assert!(pool.size().queued <= queued_limit.max_txs);
3756 }
3757 }
3758
3759 #[test]
3760 fn discard_blobs_at_capacity() {
3761 let mut f = MockTransactionFactory::default();
3762 let blob_limit = SubPoolLimit::new(1000, usize::MAX);
3763 let mut pool =
3764 TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
3765 pool.all_transactions.pending_fees.blob_fee = 10000;
3766 for _ in 0..blob_limit.max_txs {
3768 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3769 let validated = f.validated(tx.clone());
3770 let _id = *validated.id();
3771 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3772 }
3773
3774 let size = pool.size();
3775 assert_eq!(size.blob, blob_limit.max_txs);
3776
3777 for _ in 0..blob_limit.max_txs {
3778 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3779 let validated = f.validated(tx.clone());
3780 let _id = *validated.id();
3781 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3782
3783 pool.discard_worst();
3784 pool.assert_invariants();
3785 assert!(pool.size().blob <= blob_limit.max_txs);
3786 }
3787 }
3788
3789 #[test]
3790 fn account_updates_sender_balance() {
3791 let mut on_chain_balance = U256::from(100);
3792 let on_chain_nonce = 0;
3793 let mut f = MockTransactionFactory::default();
3794 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3795
3796 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3797 let tx_1 = tx_0.next();
3798 let tx_2 = tx_1.next();
3799
3800 let v0 = f.validated(tx_0);
3802 let v1 = f.validated(tx_1);
3803 let v2 = f.validated(tx_2);
3804
3805 let _res =
3806 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3807 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3808 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3809
3810 assert_eq!(1, pool.pending_transactions().len());
3812 assert_eq!(2, pool.queued_transactions().len());
3813
3814 let mut updated_accounts = HashMap::default();
3816 on_chain_balance = U256::from(300);
3817 updated_accounts.insert(
3818 v0.sender_id(),
3819 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3820 );
3821 pool.update_accounts(updated_accounts.clone());
3822
3823 assert_eq!(3, pool.pending_transactions().len());
3824 assert!(pool.queued_transactions().is_empty());
3825
3826 updated_accounts.entry(v0.sender_id()).and_modify(|v| v.balance = U256::from(1));
3828 pool.update_accounts(updated_accounts);
3829
3830 assert!(pool.pending_transactions().is_empty());
3831 assert_eq!(3, pool.queued_transactions().len());
3832 }
3833
3834 #[test]
3835 fn account_updates_nonce_gap() {
3836 let on_chain_balance = U256::from(10_000);
3837 let mut on_chain_nonce = 0;
3838 let mut f = MockTransactionFactory::default();
3839 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3840
3841 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3842 let tx_1 = tx_0.next();
3843 let tx_2 = tx_1.next();
3844
3845 let v0 = f.validated(tx_0);
3847 let v1 = f.validated(tx_1);
3848 let v2 = f.validated(tx_2);
3849
3850 let _res =
3852 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3853 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3854
3855 assert!(pool.queued_transactions().is_empty());
3856 assert_eq!(2, pool.pending_transactions().len());
3857
3858 pool.remove_transaction_by_hash(v0.hash());
3860
3861 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3863
3864 assert_eq!(2, pool.queued_transactions().len());
3866 assert!(pool.pending_transactions().is_empty());
3867
3868 let mut updated_accounts = HashMap::default();
3870 on_chain_nonce += 1;
3871 updated_accounts.insert(
3872 v0.sender_id(),
3873 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3874 );
3875 pool.update_accounts(updated_accounts);
3876
3877 assert!(pool.queued_transactions().is_empty());
3879 assert_eq!(2, pool.pending_transactions().len());
3880 }
3881 #[test]
3882 fn test_transaction_removal() {
3883 let on_chain_balance = U256::from(10_000);
3884 let on_chain_nonce = 0;
3885 let mut f = MockTransactionFactory::default();
3886 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3887
3888 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3889 let tx_1 = tx_0.next();
3890
3891 let v0 = f.validated(tx_0);
3893 let v1 = f.validated(tx_1);
3894
3895 let _res =
3897 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3898 let _res =
3899 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3900
3901 assert_eq!(0, pool.queued_transactions().len());
3902 assert_eq!(2, pool.pending_transactions().len());
3903
3904 pool.remove_transaction(v0.id());
3906 let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
3908 assert_eq!(vec![v1.nonce()], pool_txs);
3909 }
3910 #[test]
3911 fn test_remove_transactions() {
3912 let on_chain_balance = U256::from(10_000);
3913 let on_chain_nonce = 0;
3914 let mut f = MockTransactionFactory::default();
3915 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3916
3917 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3918 let tx_1 = tx_0.next();
3919 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3920 let tx_3 = tx_2.next();
3921
3922 let v0 = f.validated(tx_0);
3924 let v1 = f.validated(tx_1);
3925 let v2 = f.validated(tx_2);
3926 let v3 = f.validated(tx_3);
3927
3928 let _res =
3930 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3931 let _res =
3932 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3933 let _res =
3934 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3935 let _res =
3936 pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3937
3938 assert_eq!(0, pool.queued_transactions().len());
3939 assert_eq!(4, pool.pending_transactions().len());
3940
3941 pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
3942
3943 assert_eq!(2, pool.queued_transactions().len());
3944 assert!(pool.pending_transactions().is_empty());
3945 assert!(pool.contains(v1.hash()));
3946 assert!(pool.contains(v3.hash()));
3947 }
3948
3949 #[test]
3950 fn test_remove_transactions_middle_pending_hash() {
3951 let on_chain_balance = U256::from(10_000);
3952 let on_chain_nonce = 0;
3953 let mut f = MockTransactionFactory::default();
3954 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3955
3956 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3957 let tx_1 = tx_0.next();
3958 let tx_2 = tx_1.next();
3959 let tx_3 = tx_2.next();
3960
3961 let v0 = f.validated(tx_0);
3963 let v1 = f.validated(tx_1);
3964 let v2 = f.validated(tx_2);
3965 let v3 = f.validated(tx_3);
3966
3967 let _res = pool.add_transaction(v0, on_chain_balance, on_chain_nonce, None).unwrap();
3969 let _res =
3970 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3971 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3972 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
3973
3974 assert_eq!(0, pool.queued_transactions().len());
3975 assert_eq!(4, pool.pending_transactions().len());
3976
3977 let mut removed_txs = pool.remove_transactions(vec![*v1.hash()]);
3978 assert_eq!(1, removed_txs.len());
3979
3980 assert_eq!(2, pool.queued_transactions().len());
3981 assert_eq!(1, pool.pending_transactions().len());
3982
3983 let removed_tx = removed_txs.pop().unwrap();
3985 let v1 = f.validated(removed_tx.transaction.clone());
3986 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3987 assert_eq!(0, pool.queued_transactions().len());
3988 assert_eq!(4, pool.pending_transactions().len());
3989 }
3990
3991 #[test]
3992 fn test_remove_transactions_and_descendants() {
3993 let on_chain_balance = U256::from(10_000);
3994 let on_chain_nonce = 0;
3995 let mut f = MockTransactionFactory::default();
3996 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3997
3998 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3999 let tx_1 = tx_0.next();
4000 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4001 let tx_3 = tx_2.next();
4002 let tx_4 = tx_3.next();
4003
4004 let v0 = f.validated(tx_0);
4006 let v1 = f.validated(tx_1);
4007 let v2 = f.validated(tx_2);
4008 let v3 = f.validated(tx_3);
4009 let v4 = f.validated(tx_4);
4010
4011 let _res =
4013 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4014 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4015 let _res =
4016 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4017 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4018 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4019
4020 assert_eq!(0, pool.queued_transactions().len());
4021 assert_eq!(5, pool.pending_transactions().len());
4022
4023 pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
4024
4025 assert_eq!(0, pool.queued_transactions().len());
4026 assert_eq!(0, pool.pending_transactions().len());
4027 }
4028 #[test]
4029 fn test_remove_descendants() {
4030 let on_chain_balance = U256::from(10_000);
4031 let on_chain_nonce = 0;
4032 let mut f = MockTransactionFactory::default();
4033 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4034
4035 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4036 let tx_1 = tx_0.next();
4037 let tx_2 = tx_1.next();
4038 let tx_3 = tx_2.next();
4039
4040 let v0 = f.validated(tx_0);
4042 let v1 = f.validated(tx_1);
4043 let v2 = f.validated(tx_2);
4044 let v3 = f.validated(tx_3);
4045
4046 let _res =
4048 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4049 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4050 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4051 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4052
4053 assert_eq!(0, pool.queued_transactions().len());
4054 assert_eq!(4, pool.pending_transactions().len());
4055
4056 let mut removed = Vec::new();
4057 pool.remove_transaction(v0.id());
4058 pool.remove_descendants(v0.id(), &mut removed);
4059
4060 assert_eq!(0, pool.queued_transactions().len());
4061 assert_eq!(0, pool.pending_transactions().len());
4062 assert_eq!(3, removed.len());
4063 }
4064 #[test]
4065 fn test_remove_transactions_by_sender() {
4066 let on_chain_balance = U256::from(10_000);
4067 let on_chain_nonce = 0;
4068 let mut f = MockTransactionFactory::default();
4069 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4070
4071 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4072 let tx_1 = tx_0.next();
4073 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4074 let tx_3 = tx_2.next();
4075 let tx_4 = tx_3.next();
4076
4077 let v0 = f.validated(tx_0);
4079 let v1 = f.validated(tx_1);
4080 let v2 = f.validated(tx_2);
4081 let v3 = f.validated(tx_3);
4082 let v4 = f.validated(tx_4);
4083
4084 let _res =
4086 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4087 let _res =
4088 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4089 let _res =
4090 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4091 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4092 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4093
4094 assert_eq!(0, pool.queued_transactions().len());
4095 assert_eq!(5, pool.pending_transactions().len());
4096
4097 pool.remove_transactions_by_sender(v2.sender_id());
4098
4099 assert_eq!(0, pool.queued_transactions().len());
4100 assert_eq!(2, pool.pending_transactions().len());
4101 assert!(pool.contains(v0.hash()));
4102 assert!(pool.contains(v1.hash()));
4103 }
4104 #[test]
4105 fn wrong_best_order_of_transactions() {
4106 let on_chain_balance = U256::from(10_000);
4107 let mut on_chain_nonce = 0;
4108 let mut f = MockTransactionFactory::default();
4109 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4110
4111 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4112 let tx_1 = tx_0.next();
4113 let tx_2 = tx_1.next();
4114 let tx_3 = tx_2.next();
4115
4116 let v0 = f.validated(tx_0);
4118 let v1 = f.validated(tx_1);
4119 let v2 = f.validated(tx_2);
4120 let v3 = f.validated(tx_3);
4121
4122 let _res =
4124 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4125 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4126
4127 assert_eq!(0, pool.queued_transactions().len());
4128 assert_eq!(2, pool.pending_transactions().len());
4129
4130 pool.remove_transaction(v0.id());
4132
4133 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4135
4136 assert_eq!(1, pool.queued_transactions().len());
4138 assert_eq!(1, pool.pending_transactions().len());
4139
4140 let mut updated_accounts = HashMap::default();
4142 on_chain_nonce += 1;
4143 updated_accounts.insert(
4144 v0.sender_id(),
4145 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4146 );
4147 pool.update_accounts(updated_accounts);
4148
4149 assert_eq!(0, pool.queued_transactions().len());
4152 assert_eq!(2, pool.pending_transactions().len());
4153
4154 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4156 assert_eq!(0, pool.queued_transactions().len());
4157 assert_eq!(3, pool.pending_transactions().len());
4158
4159 assert_eq!(
4162 pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
4163 vec![1, 2, 3]
4164 );
4165 }
4166
4167 #[test]
4168 fn test_best_with_attributes() {
4169 let on_chain_balance = U256::MAX;
4170 let on_chain_nonce = 0;
4171 let mut f = MockTransactionFactory::default();
4172 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4173
4174 let base_fee: u128 = 100;
4175 let blob_fee: u128 = 100;
4176
4177 let mut block_info = pool.block_info();
4179 block_info.pending_basefee = base_fee as u64;
4180 block_info.pending_blob_fee = Some(blob_fee);
4181 pool.set_block_info(block_info);
4182
4183 let tx1 = MockTransaction::eip4844()
4185 .with_sender(Address::with_last_byte(1))
4186 .with_max_fee(base_fee + 10)
4187 .with_blob_fee(blob_fee + 10);
4188 let tx2 = MockTransaction::eip4844()
4189 .with_sender(Address::with_last_byte(2))
4190 .with_max_fee(base_fee + 10)
4191 .with_blob_fee(blob_fee);
4192 let tx3 = MockTransaction::eip4844()
4193 .with_sender(Address::with_last_byte(3))
4194 .with_max_fee(base_fee)
4195 .with_blob_fee(blob_fee + 10);
4196 let tx4 = MockTransaction::eip4844()
4197 .with_sender(Address::with_last_byte(4))
4198 .with_max_fee(base_fee)
4199 .with_blob_fee(blob_fee);
4200 let tx5 = MockTransaction::eip4844()
4201 .with_sender(Address::with_last_byte(5))
4202 .with_max_fee(base_fee)
4203 .with_blob_fee(blob_fee - 10);
4204 let tx6 = MockTransaction::eip4844()
4205 .with_sender(Address::with_last_byte(6))
4206 .with_max_fee(base_fee - 10)
4207 .with_blob_fee(blob_fee);
4208 let tx7 = MockTransaction::eip4844()
4209 .with_sender(Address::with_last_byte(7))
4210 .with_max_fee(base_fee - 10)
4211 .with_blob_fee(blob_fee - 10);
4212
4213 for tx in vec![
4214 tx1.clone(),
4215 tx2.clone(),
4216 tx3.clone(),
4217 tx4.clone(),
4218 tx5.clone(),
4219 tx6.clone(),
4220 tx7.clone(),
4221 ] {
4222 pool.add_transaction(f.validated(tx.clone()), on_chain_balance, on_chain_nonce, None)
4223 .unwrap();
4224 }
4225
4226 let base_fee = base_fee as u64;
4227 let blob_fee = blob_fee as u64;
4228
4229 let cases = vec![
4230 (BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee + 5)), vec![tx1.clone()]),
4232 (
4234 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee)),
4235 vec![tx1.clone(), tx2.clone()],
4236 ),
4237 (
4239 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee - 5)),
4240 vec![tx1.clone(), tx2.clone()],
4241 ),
4242 (
4244 BestTransactionsAttributes::new(base_fee, Some(blob_fee + 5)),
4245 vec![tx1.clone(), tx3.clone()],
4246 ),
4247 (
4249 BestTransactionsAttributes::new(base_fee, Some(blob_fee)),
4250 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
4251 ),
4252 (
4254 BestTransactionsAttributes::new(base_fee, Some(blob_fee - 10)),
4255 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx5.clone()],
4256 ),
4257 (
4259 BestTransactionsAttributes::new(base_fee - 5, Some(blob_fee + 5)),
4260 vec![tx1.clone(), tx3.clone()],
4261 ),
4262 (
4264 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee)),
4265 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx6.clone()],
4266 ),
4267 (
4269 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee - 10)),
4270 vec![tx1, tx2, tx5, tx3, tx4, tx6, tx7],
4271 ),
4272 ];
4273
4274 for (idx, (attribute, expected)) in cases.into_iter().enumerate() {
4275 let mut best = pool.best_transactions_with_attributes(attribute);
4276
4277 for (tx_idx, expected_tx) in expected.into_iter().enumerate() {
4278 let tx = best.next().expect("Transaction should be returned");
4279 assert_eq!(
4280 tx.transaction,
4281 expected_tx,
4282 "Failed tx {} in case {}",
4283 tx_idx + 1,
4284 idx + 1
4285 );
4286 }
4287
4288 assert!(best.next().is_none());
4290 }
4291 }
4292
4293 #[test]
4294 fn test_pending_ordering() {
4295 let mut f = MockTransactionFactory::default();
4296 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4297
4298 let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
4299 let tx_1 = tx_0.next();
4300
4301 let v0 = f.validated(tx_0);
4302 let v1 = f.validated(tx_1);
4303
4304 pool.add_transaction(v0.clone(), U256::MAX, 0, None).unwrap();
4306 assert_eq!(1, pool.queued_transactions().len());
4307
4308 pool.add_transaction(v1, U256::MAX, 1, None).unwrap();
4310
4311 assert_eq!(2, pool.pending_transactions().len());
4312 assert_eq!(0, pool.queued_transactions().len());
4313
4314 assert_eq!(
4315 pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
4316 v0.nonce()
4317 );
4318 }
4319
4320 #[test]
4322 fn one_sender_one_independent_transaction() {
4323 let mut on_chain_balance = U256::from(4_999); let mut on_chain_nonce = 40;
4325 let mut f = MockTransactionFactory::default();
4326 let mut pool = TxPool::mock();
4327 let mut submitted_txs = Vec::new();
4328
4329 let template =
4331 MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
4332
4333 for tx_nonce in 40..48 {
4336 let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
4337 submitted_txs.push(*tx.id());
4338 pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap();
4339 }
4340
4341 on_chain_balance = U256::from(999_999);
4344 on_chain_nonce = 42;
4345 pool.remove_transaction(&submitted_txs[0]);
4346 pool.remove_transaction(&submitted_txs[1]);
4347
4348 for tx_nonce in 48..52 {
4350 pool.add_transaction(
4351 f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
4352 on_chain_balance,
4353 on_chain_nonce,
4354 None,
4355 )
4356 .unwrap();
4357 }
4358
4359 let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
4360 assert_eq!(best_txs.len(), 10); assert_eq!(pool.pending_pool.independent().len(), 1);
4363 }
4364
4365 #[test]
4366 fn test_insertion_disorder() {
4367 let mut f = MockTransactionFactory::default();
4368 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4369
4370 let sender = address!("0x1234567890123456789012345678901234567890");
4371 let tx0 = f.validated_arc(
4372 MockTransaction::legacy().with_sender(sender).with_nonce(0).with_gas_price(10),
4373 );
4374 let tx1 = f.validated_arc(
4375 MockTransaction::eip1559()
4376 .with_sender(sender)
4377 .with_nonce(1)
4378 .with_gas_limit(1000)
4379 .with_gas_price(10),
4380 );
4381 let tx2 = f.validated_arc(
4382 MockTransaction::legacy().with_sender(sender).with_nonce(2).with_gas_price(10),
4383 );
4384 let tx3 = f.validated_arc(
4385 MockTransaction::legacy().with_sender(sender).with_nonce(3).with_gas_price(10),
4386 );
4387
4388 pool.add_transaction((*tx0).clone(), U256::from(1000), 0, None).unwrap();
4390 let mut best = pool.best_transactions();
4391 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4392 assert_eq!(t0.id(), tx0.id());
4393 pool.add_transaction((*tx1).clone(), U256::from(1000), 0, None).unwrap();
4395 let mut best = pool.best_transactions();
4396 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4397 assert_eq!(t0.id(), tx0.id());
4398 assert!(best.next().is_none());
4399
4400 pool.add_transaction((*tx2).clone(), U256::MAX, 0, None).unwrap();
4402
4403 let mut best = pool.best_transactions();
4404
4405 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4406 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4407 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4408 assert_eq!(t0.id(), tx0.id());
4409 assert_eq!(t1.id(), tx1.id());
4410 assert_eq!(t2.id(), tx2.id());
4411
4412 pool.add_transaction((*tx3).clone(), U256::MAX, 0, None).unwrap();
4414 let mut best = pool.best_transactions();
4415 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4416 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4417 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4418 let t3 = best.next().expect("tx3 should be put in the pending subpool");
4419 assert_eq!(t0.id(), tx0.id());
4420 assert_eq!(t1.id(), tx1.id());
4421 assert_eq!(t2.id(), tx2.id());
4422 assert_eq!(t3.id(), tx3.id());
4423 }
4424
4425 #[test]
4426 fn test_non_4844_blob_fee_bit_invariant() {
4427 let mut f = MockTransactionFactory::default();
4428 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4429
4430 let non_4844_tx = MockTransaction::eip1559().set_max_fee(200).inc_limit();
4431 let validated = f.validated(non_4844_tx.clone());
4432
4433 assert!(!non_4844_tx.is_eip4844());
4434 pool.add_transaction(validated.clone(), U256::from(10_000), 0, None).unwrap();
4435
4436 let tx_meta = pool.all_transactions.txs.get(validated.id()).unwrap();
4438 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4439 assert_eq!(tx_meta.subpool, SubPool::Pending);
4440 }
4441
4442 #[test]
4443 fn test_blob_fee_enforcement_only_applies_to_eip4844() {
4444 let mut f = MockTransactionFactory::default();
4445 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4446
4447 let mut block_info = pool.block_info();
4449 block_info.pending_blob_fee = Some(160);
4450 block_info.pending_basefee = 100;
4451 pool.set_block_info(block_info);
4452
4453 let eip4844_tx = MockTransaction::eip4844()
4454 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4455 .with_max_fee(200)
4456 .with_blob_fee(150) .inc_limit();
4458
4459 let non_4844_tx = MockTransaction::eip1559()
4460 .with_sender(address!("0x000000000000000000000000000000000000000b"))
4461 .set_max_fee(200)
4462 .inc_limit();
4463
4464 let validated_4844 = f.validated(eip4844_tx);
4465 let validated_non_4844 = f.validated(non_4844_tx);
4466
4467 pool.add_transaction(validated_4844.clone(), U256::from(10_000), 0, None).unwrap();
4468 pool.add_transaction(validated_non_4844.clone(), U256::from(10_000), 0, None).unwrap();
4469
4470 let tx_4844_meta = pool.all_transactions.txs.get(validated_4844.id()).unwrap();
4471 let tx_non_4844_meta = pool.all_transactions.txs.get(validated_non_4844.id()).unwrap();
4472
4473 assert!(!tx_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4475 assert_eq!(tx_4844_meta.subpool, SubPool::Blob);
4476
4477 assert!(tx_non_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4479 assert_eq!(tx_non_4844_meta.subpool, SubPool::Pending);
4480 }
4481
4482 #[test]
4483 fn test_basefee_decrease_preserves_non_4844_blob_fee_bit() {
4484 let mut f = MockTransactionFactory::default();
4485 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4486
4487 let non_4844_tx = MockTransaction::eip1559()
4489 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4490 .set_max_fee(500) .inc_limit();
4492
4493 pool.update_basefee(600, |_| {});
4495
4496 let validated = f.validated(non_4844_tx);
4497 let tx_id = *validated.id();
4498 pool.add_transaction(validated, U256::from(10_000), 0, None).unwrap();
4499
4500 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4502 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
4503 assert!(
4504 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4505 "Non-4844 tx in BaseFee pool must retain ENOUGH_BLOB_FEE_CAP_BLOCK bit"
4506 );
4507
4508 pool.update_basefee(400, |_| {});
4511
4512 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4514 assert_eq!(
4515 tx_meta.subpool,
4516 SubPool::Pending,
4517 "Non-4844 tx should be promoted from BaseFee to Pending after basefee decrease"
4518 );
4519 assert!(
4520 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4521 "Non-4844 tx must NEVER lose ENOUGH_BLOB_FEE_CAP_BLOCK bit during basefee promotion"
4522 );
4523 assert!(
4524 tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK),
4525 "Non-4844 tx should gain ENOUGH_FEE_CAP_BLOCK bit after basefee decrease"
4526 );
4527 }
4528}