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
712 for tx in self.all_transactions.transactions_iter() {
713 match tx.transaction.ty() {
714 LEGACY_TX_TYPE_ID => legacy_count += 1,
715 EIP2930_TX_TYPE_ID => eip2930_count += 1,
716 EIP1559_TX_TYPE_ID => eip1559_count += 1,
717 EIP4844_TX_TYPE_ID => eip4844_count += 1,
718 EIP7702_TX_TYPE_ID => eip7702_count += 1,
719 _ => {} }
721 }
722
723 self.metrics.total_legacy_transactions.set(legacy_count as f64);
724 self.metrics.total_eip2930_transactions.set(eip2930_count as f64);
725 self.metrics.total_eip1559_transactions.set(eip1559_count as f64);
726 self.metrics.total_eip4844_transactions.set(eip4844_count as f64);
727 self.metrics.total_eip7702_transactions.set(eip7702_count as f64);
728 }
729
730 pub(crate) fn add_transaction(
731 &mut self,
732 tx: ValidPoolTransaction<T::Transaction>,
733 on_chain_balance: U256,
734 on_chain_nonce: u64,
735 on_chain_code_hash: Option<B256>,
736 ) -> PoolResult<AddedTransaction<T::Transaction>> {
737 if self.contains(tx.hash()) {
738 return Err(PoolError::new(*tx.hash(), PoolErrorKind::AlreadyImported))
739 }
740
741 self.validate_auth(&tx, on_chain_nonce, on_chain_code_hash)?;
742
743 self.sender_info
745 .entry(tx.sender_id())
746 .or_default()
747 .update(on_chain_nonce, on_chain_balance);
748
749 match self.all_transactions.insert_tx(tx, on_chain_balance, on_chain_nonce) {
750 Ok(InsertOk { transaction, move_to, replaced_tx, updates, state }) => {
751 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
753 self.metrics.inserted_transactions.increment(1);
755 let UpdateOutcome { promoted, discarded } = self.process_updates(updates);
756
757 let replaced = replaced_tx.map(|(tx, _)| tx);
758
759 let res = if move_to.is_pending() {
761 AddedTransaction::Pending(AddedPendingTransaction {
762 transaction,
763 promoted,
764 discarded,
765 replaced,
766 })
767 } else {
768 let queued_reason = state.determine_queued_reason(move_to);
770 AddedTransaction::Parked {
771 transaction,
772 subpool: move_to,
773 replaced,
774 queued_reason,
775 }
776 };
777
778 self.update_size_metrics();
780
781 Ok(res)
782 }
783 Err(err) => {
784 self.metrics.invalid_transactions.increment(1);
786 match err {
787 InsertErr::Underpriced { existing: _, transaction } => Err(PoolError::new(
788 *transaction.hash(),
789 PoolErrorKind::ReplacementUnderpriced,
790 )),
791 InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap } => {
792 Err(PoolError::new(
793 *transaction.hash(),
794 PoolErrorKind::FeeCapBelowMinimumProtocolFeeCap(fee_cap),
795 ))
796 }
797 InsertErr::ExceededSenderTransactionsCapacity { transaction } => {
798 Err(PoolError::new(
799 *transaction.hash(),
800 PoolErrorKind::SpammerExceededCapacity(transaction.sender()),
801 ))
802 }
803 InsertErr::TxGasLimitMoreThanAvailableBlockGas {
804 transaction,
805 block_gas_limit,
806 tx_gas_limit,
807 } => Err(PoolError::new(
808 *transaction.hash(),
809 PoolErrorKind::InvalidTransaction(
810 InvalidPoolTransactionError::ExceedsGasLimit(
811 tx_gas_limit,
812 block_gas_limit,
813 ),
814 ),
815 )),
816 InsertErr::BlobTxHasNonceGap { transaction } => Err(PoolError::new(
817 *transaction.hash(),
818 PoolErrorKind::InvalidTransaction(
819 Eip4844PoolTransactionError::Eip4844NonceGap.into(),
820 ),
821 )),
822 InsertErr::Overdraft { transaction } => Err(PoolError::new(
823 *transaction.hash(),
824 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Overdraft {
825 cost: *transaction.cost(),
826 balance: on_chain_balance,
827 }),
828 )),
829 InsertErr::TxTypeConflict { transaction } => Err(PoolError::new(
830 *transaction.hash(),
831 PoolErrorKind::ExistingConflictingTransactionType(
832 transaction.sender(),
833 transaction.tx_type(),
834 ),
835 )),
836 }
837 }
838 }
839 }
840
841 fn check_delegation_limit(
845 &self,
846 transaction: &ValidPoolTransaction<T::Transaction>,
847 on_chain_nonce: u64,
848 on_chain_code_hash: Option<B256>,
849 ) -> Result<(), PoolError> {
850 if (on_chain_code_hash.is_none() || on_chain_code_hash == Some(KECCAK_EMPTY)) &&
852 !self.all_transactions.auths.contains_key(&transaction.sender_id())
853 {
854 return Ok(())
855 }
856
857 let mut txs_by_sender =
858 self.pending_pool.iter_txs_by_sender(transaction.sender_id()).peekable();
859
860 if txs_by_sender.peek().is_none() {
861 let nonce_gap_distance = transaction.nonce().saturating_sub(on_chain_nonce);
866 if nonce_gap_distance >= self.config.max_inflight_delegated_slot_limit as u64 {
867 return Err(PoolError::new(
868 *transaction.hash(),
869 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
870 Eip7702PoolTransactionError::OutOfOrderTxFromDelegated,
871 )),
872 ))
873 }
874 return Ok(())
875 }
876
877 let mut count = 0;
878 for id in txs_by_sender {
879 if id == &transaction.transaction_id {
880 return Ok(())
882 }
883 count += 1;
884 }
885
886 if count < self.config.max_inflight_delegated_slot_limit {
887 return Ok(())
889 }
890
891 Err(PoolError::new(
892 *transaction.hash(),
893 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
894 Eip7702PoolTransactionError::InflightTxLimitReached,
895 )),
896 ))
897 }
898
899 fn validate_auth(
909 &self,
910 transaction: &ValidPoolTransaction<T::Transaction>,
911 on_chain_nonce: u64,
912 on_chain_code_hash: Option<B256>,
913 ) -> Result<(), PoolError> {
914 self.check_delegation_limit(transaction, on_chain_nonce, on_chain_code_hash)?;
916
917 if let Some(authority_list) = &transaction.authority_ids {
918 for sender_id in authority_list {
919 if self.all_transactions.txs_iter(*sender_id).nth(1).is_some() {
921 return Err(PoolError::new(
922 *transaction.hash(),
923 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
924 Eip7702PoolTransactionError::AuthorityReserved,
925 )),
926 ))
927 }
928 }
929 }
930
931 Ok(())
932 }
933
934 fn process_updates(&mut self, updates: Vec<PoolUpdate>) -> UpdateOutcome<T::Transaction> {
938 let mut outcome = UpdateOutcome::default();
939 for PoolUpdate { id, current, destination } in updates {
940 match destination {
941 Destination::Discard => {
942 if let Some(tx) = self.prune_transaction_by_id(&id) {
944 outcome.discarded.push(tx);
945 }
946 self.metrics.removed_transactions.increment(1);
947 }
948 Destination::Pool(move_to) => {
949 debug_assert_ne!(&move_to, ¤t, "destination must be different");
950 let moved = self.move_transaction(current, move_to, &id);
951 if matches!(move_to, SubPool::Pending) &&
952 let Some(tx) = moved
953 {
954 trace!(target: "txpool", hash=%tx.transaction.hash(), "Promoted transaction to pending");
955 outcome.promoted.push(tx);
956 }
957 }
958 }
959 }
960
961 outcome
962 }
963
964 fn move_transaction(
969 &mut self,
970 from: SubPool,
971 to: SubPool,
972 id: &TransactionId,
973 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
974 let tx = self.remove_from_subpool(from, id)?;
975 self.add_transaction_to_subpool(to, tx.clone());
976 Some(tx)
977 }
978
979 pub(crate) fn remove_transactions(
984 &mut self,
985 hashes: Vec<TxHash>,
986 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
987 let txs =
988 hashes.into_iter().filter_map(|hash| self.remove_transaction_by_hash(&hash)).collect();
989 self.update_size_metrics();
990 txs
991 }
992
993 pub(crate) fn remove_transactions_and_descendants(
995 &mut self,
996 hashes: Vec<TxHash>,
997 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
998 let mut removed = Vec::new();
999 for hash in hashes {
1000 if let Some(tx) = self.remove_transaction_by_hash(&hash) {
1001 removed.push(tx.clone());
1002 self.remove_descendants(tx.id(), &mut removed);
1003 }
1004 }
1005 self.update_size_metrics();
1006 removed
1007 }
1008
1009 pub(crate) fn remove_transactions_by_sender(
1011 &mut self,
1012 sender_id: SenderId,
1013 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1014 let mut removed = Vec::new();
1015 let txs = self.get_transactions_by_sender(sender_id);
1016 for tx in txs {
1017 if let Some(tx) = self.remove_transaction(tx.id()) {
1018 removed.push(tx);
1019 }
1020 }
1021 self.update_size_metrics();
1022 removed
1023 }
1024
1025 fn remove_transaction(
1029 &mut self,
1030 id: &TransactionId,
1031 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1032 let (tx, pool) = self.all_transactions.remove_transaction(id)?;
1033 self.remove_from_subpool(pool, tx.id())
1034 }
1035
1036 fn remove_transaction_by_hash(
1042 &mut self,
1043 tx_hash: &B256,
1044 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1045 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1046
1047 let updates = self.all_transactions.park_descendant_transactions(tx.id());
1049 self.process_updates(updates);
1050 self.remove_from_subpool(pool, tx.id())
1051 }
1052
1053 fn prune_transaction_by_hash(
1060 &mut self,
1061 tx_hash: &B256,
1062 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1063 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1064 self.remove_from_subpool(pool, tx.id())
1065 }
1066 fn prune_transaction_by_id(
1071 &mut self,
1072 tx_id: &TransactionId,
1073 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1074 let (tx, pool) = self.all_transactions.remove_transaction_by_id(tx_id)?;
1075 self.remove_from_subpool(pool, tx.id())
1076 }
1077
1078 fn remove_from_subpool(
1082 &mut self,
1083 pool: SubPool,
1084 tx: &TransactionId,
1085 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1086 let tx = match pool {
1087 SubPool::Queued => self.queued_pool.remove_transaction(tx),
1088 SubPool::Pending => self.pending_pool.remove_transaction(tx),
1089 SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
1090 SubPool::Blob => self.blob_pool.remove_transaction(tx),
1091 };
1092
1093 if let Some(ref tx) = tx {
1094 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Removed transaction from a subpool");
1098 }
1099
1100 tx
1101 }
1102
1103 fn remove_descendants(
1107 &mut self,
1108 tx: &TransactionId,
1109 removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
1110 ) {
1111 let mut id = *tx;
1112
1113 loop {
1115 let descendant =
1116 self.all_transactions.descendant_txs_exclusive(&id).map(|(id, _)| *id).next();
1117 if let Some(descendant) = descendant {
1118 if let Some(tx) = self.remove_transaction(&descendant) {
1119 removed.push(tx)
1120 }
1121 id = descendant;
1122 } else {
1123 return
1124 }
1125 }
1126 }
1127
1128 fn add_transaction_to_subpool(
1130 &mut self,
1131 pool: SubPool,
1132 tx: Arc<ValidPoolTransaction<T::Transaction>>,
1133 ) {
1134 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Adding transaction to a subpool");
1138 match pool {
1139 SubPool::Queued => self.queued_pool.add_transaction(tx),
1140 SubPool::Pending => {
1141 self.pending_pool.add_transaction(tx, self.all_transactions.pending_fees.base_fee);
1142 }
1143 SubPool::BaseFee => {
1144 self.basefee_pool.add_transaction(tx);
1145 }
1146 SubPool::Blob => {
1147 self.blob_pool.add_transaction(tx);
1148 }
1149 }
1150 }
1151
1152 fn add_new_transaction(
1155 &mut self,
1156 transaction: Arc<ValidPoolTransaction<T::Transaction>>,
1157 replaced: Option<(Arc<ValidPoolTransaction<T::Transaction>>, SubPool)>,
1158 pool: SubPool,
1159 ) {
1160 if let Some((replaced, replaced_pool)) = replaced {
1161 self.remove_from_subpool(replaced_pool, replaced.id());
1163 }
1164
1165 self.add_transaction_to_subpool(pool, transaction)
1166 }
1167
1168 pub(crate) fn discard_worst(&mut self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1175 let mut removed = Vec::new();
1176
1177 macro_rules! discard_worst {
1179 ($this:ident, $removed:ident, [$($limit:ident => ($pool:ident, $metric:ident)),* $(,)*]) => {
1180 $ (
1181 while $this.$pool.exceeds(&$this.config.$limit)
1182 {
1183 trace!(
1184 target: "txpool",
1185 "discarding transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1186 stringify!($pool),
1187 $this.config.$limit,
1188 $this.$pool.size(),
1189 $this.$pool.len(),
1190 );
1191
1192 let removed_from_subpool = $this.$pool.truncate_pool($this.config.$limit.clone());
1194
1195 trace!(
1196 target: "txpool",
1197 "removed {} transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1198 removed_from_subpool.len(),
1199 stringify!($pool),
1200 $this.config.$limit,
1201 $this.$pool.size(),
1202 $this.$pool.len()
1203 );
1204 $this.metrics.$metric.increment(removed_from_subpool.len() as u64);
1205
1206 for tx in removed_from_subpool {
1208 $this.all_transactions.remove_transaction(tx.id());
1209
1210 let id = *tx.id();
1211
1212 removed.push(tx);
1214
1215 $this.remove_descendants(&id, &mut $removed);
1217 }
1218 }
1219
1220 )*
1221 };
1222 }
1223
1224 discard_worst!(
1225 self, removed, [
1226 pending_limit => (pending_pool, pending_transactions_evicted),
1227 basefee_limit => (basefee_pool, basefee_transactions_evicted),
1228 blob_limit => (blob_pool, blob_transactions_evicted),
1229 queued_limit => (queued_pool, queued_transactions_evicted),
1230 ]
1231 );
1232
1233 removed
1234 }
1235
1236 pub(crate) fn len(&self) -> usize {
1238 self.all_transactions.len()
1239 }
1240
1241 pub(crate) fn is_empty(&self) -> bool {
1243 self.all_transactions.is_empty()
1244 }
1245
1246 #[cfg(any(test, feature = "test-utils"))]
1254 pub fn assert_invariants(&self) {
1255 let size = self.size();
1256 let actual = size.basefee + size.pending + size.queued + size.blob;
1257 assert_eq!(
1258 size.total, actual,
1259 "total size must be equal to the sum of all sub-pools, basefee:{}, pending:{}, queued:{}, blob:{}",
1260 size.basefee, size.pending, size.queued, size.blob
1261 );
1262 self.all_transactions.assert_invariants();
1263 self.pending_pool.assert_invariants();
1264 self.basefee_pool.assert_invariants();
1265 self.queued_pool.assert_invariants();
1266 self.blob_pool.assert_invariants();
1267 }
1268}
1269
1270#[cfg(any(test, feature = "test-utils"))]
1271impl TxPool<crate::test_utils::MockOrdering> {
1272 pub fn mock() -> Self {
1274 Self::new(crate::test_utils::MockOrdering::default(), PoolConfig::default())
1275 }
1276}
1277
1278#[cfg(test)]
1279impl<T: TransactionOrdering> Drop for TxPool<T> {
1280 fn drop(&mut self) {
1281 self.assert_invariants();
1282 }
1283}
1284
1285impl<T: TransactionOrdering> TxPool<T> {
1286 pub const fn pending(&self) -> &PendingPool<T> {
1288 &self.pending_pool
1289 }
1290
1291 pub const fn base_fee(&self) -> &ParkedPool<BasefeeOrd<T::Transaction>> {
1293 &self.basefee_pool
1294 }
1295
1296 pub const fn queued(&self) -> &ParkedPool<QueuedOrd<T::Transaction>> {
1298 &self.queued_pool
1299 }
1300}
1301
1302impl<T: TransactionOrdering> fmt::Debug for TxPool<T> {
1303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1304 f.debug_struct("TxPool").field("config", &self.config).finish_non_exhaustive()
1305 }
1306}
1307
1308pub(crate) struct AllTransactions<T: PoolTransaction> {
1313 minimal_protocol_basefee: u64,
1317 block_gas_limit: u64,
1319 max_account_slots: usize,
1321 by_hash: HashMap<TxHash, Arc<ValidPoolTransaction<T>>>,
1323 txs: BTreeMap<TransactionId, PoolInternalTransaction<T>>,
1325 tx_counter: FxHashMap<SenderId, usize>,
1327 last_seen_block_number: u64,
1329 last_seen_block_hash: B256,
1331 pending_fees: PendingFees,
1333 price_bumps: PriceBumpConfig,
1335 local_transactions_config: LocalTransactionConfig,
1337 auths: FxHashMap<SenderId, HashSet<TxHash>>,
1339 metrics: AllTransactionsMetrics,
1341}
1342
1343impl<T: PoolTransaction> AllTransactions<T> {
1344 fn new(config: &PoolConfig) -> Self {
1346 Self {
1347 max_account_slots: config.max_account_slots,
1348 price_bumps: config.price_bumps,
1349 local_transactions_config: config.local_transactions_config.clone(),
1350 minimal_protocol_basefee: config.minimal_protocol_basefee,
1351 block_gas_limit: config.gas_limit,
1352 ..Default::default()
1353 }
1354 }
1355
1356 #[expect(dead_code)]
1358 pub(crate) fn hashes_iter(&self) -> impl Iterator<Item = TxHash> + '_ {
1359 self.by_hash.keys().copied()
1360 }
1361
1362 pub(crate) fn transactions_iter(
1364 &self,
1365 ) -> impl Iterator<Item = &Arc<ValidPoolTransaction<T>>> + '_ {
1366 self.by_hash.values()
1367 }
1368
1369 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
1371 self.by_hash.contains_key(tx_hash)
1372 }
1373
1374 pub(crate) fn get(&self, id: &TransactionId) -> Option<&PoolInternalTransaction<T>> {
1376 self.txs.get(id)
1377 }
1378
1379 pub(crate) fn tx_inc(&mut self, sender: SenderId) {
1381 let count = self.tx_counter.entry(sender).or_default();
1382 *count += 1;
1383 self.metrics.all_transactions_by_all_senders.increment(1.0);
1384 }
1385
1386 pub(crate) fn tx_decr(&mut self, sender: SenderId) {
1388 if let hash_map::Entry::Occupied(mut entry) = self.tx_counter.entry(sender) {
1389 let count = entry.get_mut();
1390 if *count == 1 {
1391 entry.remove();
1392 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1393 return
1394 }
1395 *count -= 1;
1396 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1397 }
1398 }
1399
1400 fn set_block_info(&mut self, block_info: BlockInfo) {
1402 let BlockInfo {
1403 block_gas_limit,
1404 last_seen_block_hash,
1405 last_seen_block_number,
1406 pending_basefee,
1407 pending_blob_fee,
1408 } = block_info;
1409 self.last_seen_block_number = last_seen_block_number;
1410 self.last_seen_block_hash = last_seen_block_hash;
1411
1412 self.pending_fees.base_fee = pending_basefee;
1413 self.metrics.base_fee.set(pending_basefee as f64);
1414
1415 self.block_gas_limit = block_gas_limit;
1416
1417 if let Some(pending_blob_fee) = pending_blob_fee {
1418 self.pending_fees.blob_fee = pending_blob_fee;
1419 self.metrics.blob_base_fee.set(pending_blob_fee as f64);
1420 }
1421 }
1422
1423 pub(crate) fn update_size_metrics(&self) {
1425 self.metrics.all_transactions_by_hash.set(self.by_hash.len() as f64);
1426 self.metrics.all_transactions_by_id.set(self.txs.len() as f64);
1427 }
1428
1429 pub(crate) fn update(
1446 &mut self,
1447 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1448 ) -> Vec<PoolUpdate> {
1449 let mut updates = Vec::with_capacity(64);
1451
1452 let mut iter = self.txs.iter_mut().peekable();
1453
1454 'transactions: while let Some((id, tx)) = iter.next() {
1464 macro_rules! next_sender {
1465 ($iter:ident) => {
1466 'this: while let Some((peek, _)) = iter.peek() {
1467 if peek.sender != id.sender {
1468 break 'this
1469 }
1470 iter.next();
1471 }
1472 };
1473 }
1474
1475 let changed_balance = if let Some(info) = changed_accounts.get(&id.sender) {
1478 if id.nonce < info.state_nonce {
1480 updates.push(PoolUpdate {
1481 id: *tx.transaction.id(),
1482 current: tx.subpool,
1483 destination: Destination::Discard,
1484 });
1485 continue 'transactions
1486 }
1487
1488 let ancestor = TransactionId::ancestor(id.nonce, info.state_nonce, id.sender);
1489 if ancestor.is_none() {
1491 tx.state.insert(TxState::NO_NONCE_GAPS);
1492 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1493 tx.cumulative_cost = U256::ZERO;
1494 if tx.transaction.cost() > &info.balance {
1495 tx.state.remove(TxState::ENOUGH_BALANCE);
1497 } else {
1498 tx.state.insert(TxState::ENOUGH_BALANCE);
1499 }
1500 }
1501
1502 Some(&info.balance)
1503 } else {
1504 None
1505 };
1506
1507 if tx.state.has_nonce_gap() {
1509 next_sender!(iter);
1510 continue 'transactions
1511 }
1512
1513 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1515
1516 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1518 Self::record_subpool_update(&mut updates, tx);
1520
1521 let mut has_parked_ancestor = !tx.state.is_pending();
1523
1524 let mut cumulative_cost = tx.next_cumulative_cost();
1525
1526 let mut next_nonce_in_line = tx.transaction.nonce().saturating_add(1);
1528
1529 while let Some((peek, tx)) = iter.peek_mut() {
1531 if peek.sender != id.sender {
1532 continue 'transactions
1534 }
1535
1536 if tx.transaction.nonce() == next_nonce_in_line {
1537 tx.state.insert(TxState::NO_NONCE_GAPS);
1539 } else {
1540 next_sender!(iter);
1542 continue 'transactions
1543 }
1544
1545 next_nonce_in_line = next_nonce_in_line.saturating_add(1);
1547
1548 tx.cumulative_cost = cumulative_cost;
1550 cumulative_cost = tx.next_cumulative_cost();
1552
1553 if let Some(changed_balance) = changed_balance {
1555 if &cumulative_cost > changed_balance {
1556 tx.state.remove(TxState::ENOUGH_BALANCE);
1558 } else {
1559 tx.state.insert(TxState::ENOUGH_BALANCE);
1560 }
1561 }
1562
1563 if has_parked_ancestor {
1565 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1566 } else {
1567 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1568 }
1569 has_parked_ancestor = !tx.state.is_pending();
1570
1571 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1573 Self::record_subpool_update(&mut updates, tx);
1574
1575 iter.next();
1577 }
1578 }
1579
1580 updates
1581 }
1582
1583 fn record_subpool_update(updates: &mut Vec<PoolUpdate>, tx: &mut PoolInternalTransaction<T>) {
1588 let current_pool = tx.subpool;
1589 tx.subpool = tx.state.into();
1590 if current_pool != tx.subpool {
1591 updates.push(PoolUpdate {
1592 id: *tx.transaction.id(),
1593 current: current_pool,
1594 destination: tx.subpool.into(),
1595 })
1596 }
1597 }
1598
1599 fn update_tx_base_fee(pending_block_base_fee: u64, tx: &mut PoolInternalTransaction<T>) {
1601 match tx.transaction.max_fee_per_gas().cmp(&(pending_block_base_fee as u128)) {
1603 Ordering::Greater | Ordering::Equal => {
1604 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1605 }
1606 Ordering::Less => {
1607 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
1608 }
1609 }
1610 }
1611
1612 pub(crate) fn txs_iter(
1615 &self,
1616 sender: SenderId,
1617 ) -> impl Iterator<Item = (&TransactionId, &PoolInternalTransaction<T>)> + '_ {
1618 self.txs
1619 .range((sender.start_bound(), Unbounded))
1620 .take_while(move |(other, _)| sender == other.sender)
1621 }
1622
1623 #[cfg(test)]
1626 #[expect(dead_code)]
1627 pub(crate) fn txs_iter_mut(
1628 &mut self,
1629 sender: SenderId,
1630 ) -> impl Iterator<Item = (&TransactionId, &mut PoolInternalTransaction<T>)> + '_ {
1631 self.txs
1632 .range_mut((sender.start_bound(), Unbounded))
1633 .take_while(move |(other, _)| sender == other.sender)
1634 }
1635
1636 pub(crate) fn descendant_txs_exclusive<'a, 'b: 'a>(
1640 &'a self,
1641 id: &'b TransactionId,
1642 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1643 self.txs.range((Excluded(id), Unbounded)).take_while(|(other, _)| id.sender == other.sender)
1644 }
1645
1646 pub(crate) fn descendant_txs_inclusive<'a, 'b: 'a>(
1651 &'a self,
1652 id: &'b TransactionId,
1653 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1654 self.txs.range(id..).take_while(|(other, _)| id.sender == other.sender)
1655 }
1656
1657 pub(crate) fn descendant_txs_mut<'a, 'b: 'a>(
1662 &'a mut self,
1663 id: &'b TransactionId,
1664 ) -> impl Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)> + 'a {
1665 self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender)
1666 }
1667
1668 pub(crate) fn remove_transaction_by_hash(
1670 &mut self,
1671 tx_hash: &B256,
1672 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1673 let tx = self.by_hash.remove(tx_hash)?;
1674 let internal = self.txs.remove(&tx.transaction_id)?;
1675 self.remove_auths(&internal);
1676 self.tx_decr(tx.sender_id());
1678 Some((tx, internal.subpool))
1679 }
1680
1681 pub(crate) fn remove_transaction_by_id(
1685 &mut self,
1686 tx_id: &TransactionId,
1687 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1688 let internal = self.txs.remove(tx_id)?;
1689 let tx = self.by_hash.remove(internal.transaction.hash())?;
1690 self.remove_auths(&internal);
1691 self.tx_decr(tx.sender_id());
1693 Some((tx, internal.subpool))
1694 }
1695
1696 pub(crate) fn park_descendant_transactions(
1698 &mut self,
1699 tx_id: &TransactionId,
1700 ) -> Vec<PoolUpdate> {
1701 let mut updates = Vec::new();
1702
1703 for (id, tx) in self.descendant_txs_mut(tx_id) {
1704 let current_pool = tx.subpool;
1705
1706 tx.state.remove(TxState::NO_NONCE_GAPS);
1707
1708 tx.subpool = tx.state.into();
1710
1711 if current_pool != tx.subpool {
1713 updates.push(PoolUpdate {
1714 id: *id,
1715 current: current_pool,
1716 destination: tx.subpool.into(),
1717 })
1718 }
1719 }
1720
1721 updates
1722 }
1723
1724 pub(crate) fn remove_transaction(
1730 &mut self,
1731 id: &TransactionId,
1732 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1733 let internal = self.txs.remove(id)?;
1734
1735 self.tx_decr(internal.transaction.sender_id());
1737
1738 let result =
1739 self.by_hash.remove(internal.transaction.hash()).map(|tx| (tx, internal.subpool));
1740
1741 self.remove_auths(&internal);
1742
1743 result
1744 }
1745
1746 fn remove_auths(&mut self, tx: &PoolInternalTransaction<T>) {
1750 let Some(auths) = &tx.transaction.authority_ids else { return };
1751
1752 let tx_hash = tx.transaction.hash();
1753 for auth in auths {
1754 if let Some(list) = self.auths.get_mut(auth) {
1755 list.remove(tx_hash);
1756 if list.is_empty() {
1757 self.auths.remove(auth);
1758 }
1759 }
1760 }
1761 }
1762
1763 #[inline]
1769 fn contains_conflicting_transaction(&self, tx: &ValidPoolTransaction<T>) -> bool {
1770 self.txs_iter(tx.transaction_id.sender)
1771 .next()
1772 .is_some_and(|(_, existing)| tx.tx_type_conflicts_with(&existing.transaction))
1773 }
1774
1775 fn ensure_valid(
1784 &self,
1785 transaction: ValidPoolTransaction<T>,
1786 on_chain_nonce: u64,
1787 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1788 if !self.local_transactions_config.is_local(transaction.origin, transaction.sender_ref()) {
1789 let current_txs =
1790 self.tx_counter.get(&transaction.sender_id()).copied().unwrap_or_default();
1791
1792 if current_txs >= self.max_account_slots && transaction.nonce() > on_chain_nonce {
1795 return Err(InsertErr::ExceededSenderTransactionsCapacity {
1796 transaction: Arc::new(transaction),
1797 })
1798 }
1799 }
1800 if transaction.gas_limit() > self.block_gas_limit {
1801 return Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas {
1802 block_gas_limit: self.block_gas_limit,
1803 tx_gas_limit: transaction.gas_limit(),
1804 transaction: Arc::new(transaction),
1805 })
1806 }
1807
1808 if self.contains_conflicting_transaction(&transaction) {
1809 return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) })
1811 }
1812
1813 Ok(transaction)
1814 }
1815
1816 fn ensure_valid_blob_transaction(
1822 &self,
1823 new_blob_tx: ValidPoolTransaction<T>,
1824 on_chain_balance: U256,
1825 ancestor: Option<TransactionId>,
1826 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1827 if let Some(ancestor) = ancestor {
1828 let Some(ancestor_tx) = self.txs.get(&ancestor) else {
1829 self.metrics.blob_transactions_nonce_gaps.increment(1);
1831 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1832 };
1833 if ancestor_tx.state.has_nonce_gap() {
1834 self.metrics.blob_transactions_nonce_gaps.increment(1);
1837 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1838 }
1839
1840 let mut cumulative_cost = ancestor_tx.next_cumulative_cost() + new_blob_tx.cost();
1842
1843 if cumulative_cost > on_chain_balance {
1845 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1847 }
1848
1849 let id = new_blob_tx.transaction_id;
1852 let mut descendants = self.descendant_txs_inclusive(&id).peekable();
1853 if let Some((maybe_replacement, _)) = descendants.peek() &&
1854 **maybe_replacement == new_blob_tx.transaction_id
1855 {
1856 descendants.next();
1858
1859 for (_, tx) in descendants {
1861 cumulative_cost += tx.transaction.cost();
1862 if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance {
1863 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1865 }
1866 }
1867 }
1868 } else if new_blob_tx.cost() > &on_chain_balance {
1869 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1871 }
1872
1873 Ok(new_blob_tx)
1874 }
1875
1876 pub(crate) fn insert_tx(
1908 &mut self,
1909 transaction: ValidPoolTransaction<T>,
1910 on_chain_balance: U256,
1911 on_chain_nonce: u64,
1912 ) -> InsertResult<T> {
1913 assert!(on_chain_nonce <= transaction.nonce(), "Invalid transaction");
1914
1915 let mut transaction = self.ensure_valid(transaction, on_chain_nonce)?;
1916
1917 let inserted_tx_id = *transaction.id();
1918 let mut state = TxState::default();
1919 let mut cumulative_cost = U256::ZERO;
1920 let mut updates = Vec::new();
1921
1922 state.insert(TxState::NOT_TOO_MUCH_GAS);
1924
1925 let ancestor = TransactionId::ancestor(
1928 transaction.transaction.nonce(),
1929 on_chain_nonce,
1930 inserted_tx_id.sender,
1931 );
1932
1933 if transaction.is_eip4844() {
1936 state.insert(TxState::BLOB_TRANSACTION);
1937
1938 transaction =
1939 self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
1940 let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();
1941 if blob_fee_cap >= self.pending_fees.blob_fee {
1942 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1943 }
1944 } else {
1945 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1947 }
1948
1949 let transaction = Arc::new(transaction);
1950
1951 if ancestor.is_none() {
1953 state.insert(TxState::NO_NONCE_GAPS);
1954 state.insert(TxState::NO_PARKED_ANCESTORS);
1955 }
1956
1957 let fee_cap = transaction.max_fee_per_gas();
1959
1960 if fee_cap < self.minimal_protocol_basefee as u128 {
1961 return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap })
1962 }
1963 if fee_cap >= self.pending_fees.base_fee as u128 {
1964 state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1965 }
1966
1967 let mut replaced_tx = None;
1969
1970 let pool_tx = PoolInternalTransaction {
1971 transaction: Arc::clone(&transaction),
1972 subpool: state.into(),
1973 state,
1974 cumulative_cost,
1975 };
1976
1977 match self.txs.entry(*transaction.id()) {
1979 Entry::Vacant(entry) => {
1980 self.by_hash.insert(*pool_tx.transaction.hash(), pool_tx.transaction.clone());
1982 entry.insert(pool_tx);
1983 }
1984 Entry::Occupied(mut entry) => {
1985 let existing_transaction = entry.get().transaction.as_ref();
1987 let maybe_replacement = transaction.as_ref();
1988
1989 if existing_transaction.is_underpriced(maybe_replacement, &self.price_bumps) {
1991 return Err(InsertErr::Underpriced {
1992 transaction: pool_tx.transaction,
1993 existing: *entry.get().transaction.hash(),
1994 })
1995 }
1996 let new_hash = *pool_tx.transaction.hash();
1997 let new_transaction = pool_tx.transaction.clone();
1998 let replaced = entry.insert(pool_tx);
1999 self.by_hash.remove(replaced.transaction.hash());
2000 self.by_hash.insert(new_hash, new_transaction);
2001
2002 self.remove_auths(&replaced);
2003
2004 replaced_tx = Some((replaced.transaction, replaced.subpool));
2006 }
2007 }
2008
2009 if let Some(auths) = &transaction.authority_ids {
2010 let tx_hash = transaction.hash();
2011 for auth in auths {
2012 self.auths.entry(*auth).or_default().insert(*tx_hash);
2013 }
2014 }
2015
2016 let on_chain_id = TransactionId::new(transaction.sender_id(), on_chain_nonce);
2018 {
2019 let mut next_nonce = on_chain_id.nonce;
2021
2022 let mut has_parked_ancestor = false;
2026
2027 for (id, tx) in self.descendant_txs_mut(&on_chain_id) {
2030 let current_pool = tx.subpool;
2031
2032 if next_nonce != id.nonce {
2034 break
2035 }
2036
2037 tx.state.insert(TxState::NO_NONCE_GAPS);
2039
2040 tx.cumulative_cost = cumulative_cost;
2042
2043 cumulative_cost = tx.next_cumulative_cost();
2045
2046 if cumulative_cost > on_chain_balance {
2047 tx.state.remove(TxState::ENOUGH_BALANCE);
2049 } else {
2050 tx.state.insert(TxState::ENOUGH_BALANCE);
2051 }
2052
2053 if has_parked_ancestor {
2055 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
2056 } else {
2057 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
2058 }
2059 has_parked_ancestor = !tx.state.is_pending();
2060
2061 tx.subpool = tx.state.into();
2063
2064 if inserted_tx_id.eq(id) {
2065 state = tx.state;
2067 } else {
2068 if current_pool != tx.subpool {
2070 updates.push(PoolUpdate {
2071 id: *id,
2072 current: current_pool,
2073 destination: tx.subpool.into(),
2074 })
2075 }
2076 }
2077
2078 next_nonce = id.next_nonce();
2080 }
2081 }
2082
2083 if replaced_tx.is_none() {
2085 self.tx_inc(inserted_tx_id.sender);
2086 }
2087
2088 self.update_size_metrics();
2089
2090 Ok(InsertOk { transaction, move_to: state.into(), state, replaced_tx, updates })
2091 }
2092
2093 pub(crate) fn len(&self) -> usize {
2095 self.txs.len()
2096 }
2097
2098 pub(crate) fn is_empty(&self) -> bool {
2100 self.txs.is_empty()
2101 }
2102
2103 #[cfg(any(test, feature = "test-utils"))]
2105 pub(crate) fn assert_invariants(&self) {
2106 assert_eq!(self.by_hash.len(), self.txs.len(), "by_hash.len() != txs.len()");
2107 assert!(self.auths.len() <= self.txs.len(), "auths.len() > txs.len()");
2108 }
2109}
2110
2111#[cfg(test)]
2112impl<T: PoolTransaction> AllTransactions<T> {
2113 pub(crate) fn tx_count(&self, sender: SenderId) -> usize {
2117 self.tx_counter.get(&sender).copied().unwrap_or_default()
2118 }
2119}
2120
2121impl<T: PoolTransaction> Default for AllTransactions<T> {
2122 fn default() -> Self {
2123 Self {
2124 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
2125 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
2126 block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
2127 by_hash: Default::default(),
2128 txs: Default::default(),
2129 tx_counter: Default::default(),
2130 last_seen_block_number: Default::default(),
2131 last_seen_block_hash: Default::default(),
2132 pending_fees: Default::default(),
2133 price_bumps: Default::default(),
2134 local_transactions_config: Default::default(),
2135 auths: Default::default(),
2136 metrics: Default::default(),
2137 }
2138 }
2139}
2140
2141#[derive(Debug, Clone)]
2143pub(crate) struct PendingFees {
2144 pub(crate) base_fee: u64,
2146 pub(crate) blob_fee: u128,
2148}
2149
2150impl Default for PendingFees {
2151 fn default() -> Self {
2152 Self { base_fee: Default::default(), blob_fee: BLOB_TX_MIN_BLOB_GASPRICE }
2153 }
2154}
2155
2156pub(crate) type InsertResult<T> = Result<InsertOk<T>, InsertErr<T>>;
2158
2159#[derive(Debug)]
2161pub(crate) enum InsertErr<T: PoolTransaction> {
2162 Underpriced {
2164 transaction: Arc<ValidPoolTransaction<T>>,
2165 #[expect(dead_code)]
2166 existing: TxHash,
2167 },
2168 BlobTxHasNonceGap { transaction: Arc<ValidPoolTransaction<T>> },
2170 Overdraft { transaction: Arc<ValidPoolTransaction<T>> },
2173 FeeCapBelowMinimumProtocolFeeCap { transaction: Arc<ValidPoolTransaction<T>>, fee_cap: u128 },
2177 ExceededSenderTransactionsCapacity { transaction: Arc<ValidPoolTransaction<T>> },
2181 TxGasLimitMoreThanAvailableBlockGas {
2183 transaction: Arc<ValidPoolTransaction<T>>,
2184 block_gas_limit: u64,
2185 tx_gas_limit: u64,
2186 },
2187 TxTypeConflict { transaction: Arc<ValidPoolTransaction<T>> },
2189}
2190
2191#[derive(Debug)]
2193pub(crate) struct InsertOk<T: PoolTransaction> {
2194 transaction: Arc<ValidPoolTransaction<T>>,
2196 move_to: SubPool,
2198 state: TxState,
2200 replaced_tx: Option<(Arc<ValidPoolTransaction<T>>, SubPool)>,
2202 updates: Vec<PoolUpdate>,
2204}
2205
2206#[derive(Debug)]
2209pub(crate) struct PoolInternalTransaction<T: PoolTransaction> {
2210 pub(crate) transaction: Arc<ValidPoolTransaction<T>>,
2212 pub(crate) subpool: SubPool,
2214 pub(crate) state: TxState,
2217 pub(crate) cumulative_cost: U256,
2222}
2223
2224impl<T: PoolTransaction> PoolInternalTransaction<T> {
2227 fn next_cumulative_cost(&self) -> U256 {
2228 self.cumulative_cost + self.transaction.cost()
2229 }
2230}
2231
2232#[derive(Debug, Clone, Default)]
2234pub(crate) struct SenderInfo {
2235 pub(crate) state_nonce: u64,
2237 pub(crate) balance: U256,
2239}
2240
2241impl SenderInfo {
2244 const fn update(&mut self, state_nonce: u64, balance: U256) {
2246 *self = Self { state_nonce, balance };
2247 }
2248}
2249
2250#[cfg(test)]
2251mod tests {
2252 use super::*;
2253 use crate::{
2254 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
2255 traits::TransactionOrigin,
2256 SubPoolLimit,
2257 };
2258 use alloy_consensus::{Transaction, TxType};
2259 use alloy_primitives::address;
2260
2261 #[test]
2262 fn test_insert_blob() {
2263 let on_chain_balance = U256::MAX;
2264 let on_chain_nonce = 0;
2265 let mut f = MockTransactionFactory::default();
2266 let mut pool = AllTransactions::default();
2267 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2268 let valid_tx = f.validated(tx);
2269 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2270 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2271 assert!(updates.is_empty());
2272 assert!(replaced_tx.is_none());
2273 assert!(state.contains(TxState::NO_NONCE_GAPS));
2274 assert!(state.contains(TxState::ENOUGH_BALANCE));
2275 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2276 assert_eq!(move_to, SubPool::Pending);
2277
2278 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2279 assert_eq!(inserted.subpool, SubPool::Pending);
2280 }
2281
2282 #[test]
2283 fn test_insert_blob_not_enough_blob_fee() {
2284 let on_chain_balance = U256::MAX;
2285 let on_chain_nonce = 0;
2286 let mut f = MockTransactionFactory::default();
2287 let mut pool = AllTransactions {
2288 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2289 ..Default::default()
2290 };
2291 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2292 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2293 let valid_tx = f.validated(tx);
2294 let InsertOk { state, .. } =
2295 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2296 assert!(state.contains(TxState::NO_NONCE_GAPS));
2297 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2298
2299 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2300 }
2301
2302 #[test]
2303 fn test_valid_tx_with_decreasing_blob_fee() {
2304 let on_chain_balance = U256::MAX;
2305 let on_chain_nonce = 0;
2306 let mut f = MockTransactionFactory::default();
2307 let mut pool = AllTransactions {
2308 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2309 ..Default::default()
2310 };
2311 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2312
2313 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2314 let valid_tx = f.validated(tx.clone());
2315 let InsertOk { state, .. } =
2316 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2317 assert!(state.contains(TxState::NO_NONCE_GAPS));
2318 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2319
2320 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2321 pool.remove_transaction(&valid_tx.transaction_id);
2322
2323 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
2324 let InsertOk { state, .. } =
2325 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2326 assert!(state.contains(TxState::NO_NONCE_GAPS));
2327 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2328 }
2329
2330 #[test]
2331 fn test_demote_valid_tx_with_increasing_blob_fee() {
2332 let on_chain_balance = U256::MAX;
2333 let on_chain_nonce = 0;
2334 let mut f = MockTransactionFactory::default();
2335 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2336 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2337
2338 let mut block_info = pool.block_info();
2340 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2341 pool.set_block_info(block_info);
2342
2343 let validated = f.validated(tx.clone());
2344 let id = *validated.id();
2345 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2346
2347 assert!(pool.blob_pool.is_empty());
2349 assert_eq!(pool.pending_pool.len(), 1);
2350
2351 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2353 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2354 assert_eq!(internal_tx.subpool, SubPool::Pending);
2355
2356 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2358 pool.set_block_info(block_info);
2359
2360 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2362 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2363 assert_eq!(internal_tx.subpool, SubPool::Blob);
2364
2365 assert_eq!(pool.blob_pool.len(), 1);
2367 assert!(pool.pending_pool.is_empty());
2368 }
2369
2370 #[test]
2371 fn test_promote_valid_tx_with_decreasing_blob_fee() {
2372 let on_chain_balance = U256::MAX;
2373 let on_chain_nonce = 0;
2374 let mut f = MockTransactionFactory::default();
2375 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2376 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2377
2378 let mut block_info = pool.block_info();
2380 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2381 pool.set_block_info(block_info);
2382
2383 let validated = f.validated(tx.clone());
2384 let id = *validated.id();
2385 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2386
2387 assert!(pool.pending_pool.is_empty());
2389 assert_eq!(pool.blob_pool.len(), 1);
2390
2391 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2393 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2394 assert_eq!(internal_tx.subpool, SubPool::Blob);
2395
2396 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2398 pool.set_block_info(block_info);
2399
2400 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2402 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2403 assert_eq!(internal_tx.subpool, SubPool::Pending);
2404
2405 assert_eq!(pool.pending_pool.len(), 1);
2407 assert!(pool.blob_pool.is_empty());
2408 }
2409
2410 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
2412 struct PromotionTest {
2413 basefee: u64,
2415 blobfee: u128,
2417 subpool: SubPool,
2419 basefee_update: u64,
2421 blobfee_update: u128,
2423 new_subpool: SubPool,
2425 }
2426
2427 impl PromotionTest {
2428 const fn opposite(&self) -> Self {
2430 Self {
2431 basefee: self.basefee_update,
2432 blobfee: self.blobfee_update,
2433 subpool: self.new_subpool,
2434 blobfee_update: self.blobfee,
2435 basefee_update: self.basefee,
2436 new_subpool: self.subpool,
2437 }
2438 }
2439
2440 fn assert_subpool_lengths<T: TransactionOrdering>(
2441 &self,
2442 pool: &TxPool<T>,
2443 failure_message: String,
2444 check_subpool: SubPool,
2445 ) {
2446 match check_subpool {
2447 SubPool::Blob => {
2448 assert_eq!(pool.blob_pool.len(), 1, "{failure_message}");
2449 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2450 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2451 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2452 }
2453 SubPool::Pending => {
2454 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2455 assert_eq!(pool.pending_pool.len(), 1, "{failure_message}");
2456 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2457 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2458 }
2459 SubPool::BaseFee => {
2460 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2461 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2462 assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}");
2463 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2464 }
2465 SubPool::Queued => {
2466 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2467 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2468 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2469 assert_eq!(pool.queued_pool.len(), 1, "{failure_message}");
2470 }
2471 }
2472 }
2473
2474 fn assert_single_tx_starting_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2478 self.assert_subpool_lengths(
2479 pool,
2480 format!("pool length check failed at start of test: {self:?}"),
2481 self.subpool,
2482 );
2483 }
2484
2485 fn assert_single_tx_ending_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2489 self.assert_subpool_lengths(
2490 pool,
2491 format!("pool length check failed at end of test: {self:?}"),
2492 self.new_subpool,
2493 );
2494 }
2495 }
2496
2497 #[test]
2498 fn test_promote_blob_tx_with_both_pending_fee_updates() {
2499 let on_chain_balance = U256::MAX;
2502 let on_chain_nonce = 0;
2503 let mut f = MockTransactionFactory::default();
2504 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2505
2506 let max_fee_per_blob_gas = tx.max_fee_per_blob_gas().unwrap();
2507 let max_fee_per_gas = tx.max_fee_per_gas() as u64;
2508
2509 let mut expected_promotions = vec![
2511 PromotionTest {
2512 blobfee: max_fee_per_blob_gas + 1,
2513 basefee: max_fee_per_gas + 1,
2514 subpool: SubPool::Blob,
2515 blobfee_update: max_fee_per_blob_gas + 1,
2516 basefee_update: max_fee_per_gas + 1,
2517 new_subpool: SubPool::Blob,
2518 },
2519 PromotionTest {
2520 blobfee: max_fee_per_blob_gas + 1,
2521 basefee: max_fee_per_gas + 1,
2522 subpool: SubPool::Blob,
2523 blobfee_update: max_fee_per_blob_gas,
2524 basefee_update: max_fee_per_gas + 1,
2525 new_subpool: SubPool::Blob,
2526 },
2527 PromotionTest {
2528 blobfee: max_fee_per_blob_gas + 1,
2529 basefee: max_fee_per_gas + 1,
2530 subpool: SubPool::Blob,
2531 blobfee_update: max_fee_per_blob_gas + 1,
2532 basefee_update: max_fee_per_gas,
2533 new_subpool: SubPool::Blob,
2534 },
2535 PromotionTest {
2536 blobfee: max_fee_per_blob_gas + 1,
2537 basefee: max_fee_per_gas + 1,
2538 subpool: SubPool::Blob,
2539 blobfee_update: max_fee_per_blob_gas,
2540 basefee_update: max_fee_per_gas,
2541 new_subpool: SubPool::Pending,
2542 },
2543 PromotionTest {
2544 blobfee: max_fee_per_blob_gas,
2545 basefee: max_fee_per_gas + 1,
2546 subpool: SubPool::Blob,
2547 blobfee_update: max_fee_per_blob_gas,
2548 basefee_update: max_fee_per_gas,
2549 new_subpool: SubPool::Pending,
2550 },
2551 PromotionTest {
2552 blobfee: max_fee_per_blob_gas + 1,
2553 basefee: max_fee_per_gas,
2554 subpool: SubPool::Blob,
2555 blobfee_update: max_fee_per_blob_gas,
2556 basefee_update: max_fee_per_gas,
2557 new_subpool: SubPool::Pending,
2558 },
2559 PromotionTest {
2560 blobfee: max_fee_per_blob_gas,
2561 basefee: max_fee_per_gas,
2562 subpool: SubPool::Pending,
2563 blobfee_update: max_fee_per_blob_gas,
2564 basefee_update: max_fee_per_gas,
2565 new_subpool: SubPool::Pending,
2566 },
2567 ];
2568
2569 let reversed = expected_promotions.iter().map(|test| test.opposite()).collect::<Vec<_>>();
2571 expected_promotions.extend(reversed);
2572
2573 let expected_promotions = expected_promotions.into_iter().collect::<HashSet<_>>();
2575
2576 for promotion_test in &expected_promotions {
2577 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2578
2579 let mut block_info = pool.block_info();
2581
2582 block_info.pending_blob_fee = Some(promotion_test.blobfee);
2583 block_info.pending_basefee = promotion_test.basefee;
2584 pool.set_block_info(block_info);
2585
2586 let validated = f.validated(tx.clone());
2587 let id = *validated.id();
2588 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2589
2590 promotion_test.assert_single_tx_starting_subpool(&pool);
2592
2593 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2595 assert_eq!(
2596 internal_tx.subpool, promotion_test.subpool,
2597 "Subpools do not match at start of test: {promotion_test:?}"
2598 );
2599
2600 block_info.pending_basefee = promotion_test.basefee_update;
2602 block_info.pending_blob_fee = Some(promotion_test.blobfee_update);
2603 pool.set_block_info(block_info);
2604
2605 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2607 assert_eq!(
2608 internal_tx.subpool, promotion_test.new_subpool,
2609 "Subpools do not match at end of test: {promotion_test:?}"
2610 );
2611
2612 promotion_test.assert_single_tx_ending_subpool(&pool);
2614 }
2615 }
2616
2617 #[test]
2618 fn test_insert_pending() {
2619 let on_chain_balance = U256::MAX;
2620 let on_chain_nonce = 0;
2621 let mut f = MockTransactionFactory::default();
2622 let mut pool = AllTransactions::default();
2623 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2624 let valid_tx = f.validated(tx);
2625 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2626 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2627 assert!(updates.is_empty());
2628 assert!(replaced_tx.is_none());
2629 assert!(state.contains(TxState::NO_NONCE_GAPS));
2630 assert!(state.contains(TxState::ENOUGH_BALANCE));
2631 assert_eq!(move_to, SubPool::Pending);
2632
2633 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2634 assert_eq!(inserted.subpool, SubPool::Pending);
2635 }
2636
2637 #[test]
2638 fn test_simple_insert() {
2639 let on_chain_balance = U256::ZERO;
2640 let on_chain_nonce = 0;
2641 let mut f = MockTransactionFactory::default();
2642 let mut pool = AllTransactions::default();
2643 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2644 tx.set_priority_fee(100);
2645 tx.set_max_fee(100);
2646 let valid_tx = f.validated(tx.clone());
2647 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2648 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2649 assert!(updates.is_empty());
2650 assert!(replaced_tx.is_none());
2651 assert!(state.contains(TxState::NO_NONCE_GAPS));
2652 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2653 assert_eq!(move_to, SubPool::Queued);
2654
2655 assert_eq!(pool.len(), 1);
2656 assert!(pool.contains(valid_tx.hash()));
2657 let expected_state = TxState::ENOUGH_FEE_CAP_BLOCK | TxState::NO_NONCE_GAPS;
2658 let inserted = pool.get(valid_tx.id()).unwrap();
2659 assert!(inserted.state.intersects(expected_state));
2660
2661 let res = pool.insert_tx(valid_tx, on_chain_balance, on_chain_nonce);
2663 res.unwrap_err();
2664 assert_eq!(pool.len(), 1);
2665
2666 let valid_tx = f.validated(tx.next());
2667 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2668 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2669
2670 assert!(updates.is_empty());
2671 assert!(replaced_tx.is_none());
2672 assert!(state.contains(TxState::NO_NONCE_GAPS));
2673 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2674 assert_eq!(move_to, SubPool::Queued);
2675
2676 assert!(pool.contains(valid_tx.hash()));
2677 assert_eq!(pool.len(), 2);
2678 let inserted = pool.get(valid_tx.id()).unwrap();
2679 assert!(inserted.state.intersects(expected_state));
2680 }
2681
2682 #[test]
2683 fn test_on_canonical_state_change_no_double_processing() {
2686 let mut tx_factory = MockTransactionFactory::default();
2687 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2688
2689 let tx = MockTransaction::eip1559().with_gas_price(50).with_gas_limit(30_000);
2691 let sender = tx.sender();
2692
2693 let mut block_info = pool.block_info();
2695 block_info.pending_basefee = 100;
2696 pool.set_block_info(block_info);
2697
2698 let validated = tx_factory.validated(tx);
2699 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2700
2701 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2703
2704 assert_eq!(pool.basefee_pool.len(), 1);
2705 assert_eq!(pool.pending_pool.len(), 0);
2706
2707 block_info.pending_basefee = 40;
2711
2712 let mut changed_senders = FxHashMap::default();
2713 changed_senders.insert(
2714 sender_id,
2715 SenderInfo {
2716 state_nonce: 0,
2717 balance: U256::from(20_000_000), },
2719 );
2720
2721 let outcome = pool.on_canonical_state_change(
2722 block_info,
2723 vec![], changed_senders,
2725 PoolUpdateKind::Commit,
2726 );
2727
2728 assert_eq!(pool.pending_pool.len(), 1, "Transaction should be in pending pool");
2730 assert_eq!(pool.basefee_pool.len(), 0, "Transaction should not be in basefee pool");
2731 assert_eq!(outcome.promoted.len(), 1, "Should report exactly one promotion");
2732 }
2733
2734 #[test]
2735 fn test_canonical_state_change_with_basefee_update_regression() {
2738 let mut tx_factory = MockTransactionFactory::default();
2739 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2740
2741 let sender_balance = U256::from(100_000_000);
2743
2744 let tx1 =
2746 MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000).with_nonce(0);
2747 let sender1 = tx1.sender();
2748
2749 let tx2 =
2751 MockTransaction::eip1559().with_gas_price(55).with_gas_limit(21_000).with_nonce(0);
2752 let sender2 = tx2.sender();
2753
2754 let tx3 =
2756 MockTransaction::eip1559().with_gas_price(45).with_gas_limit(21_000).with_nonce(0);
2757 let sender3 = tx3.sender();
2758
2759 let mut block_info = pool.block_info();
2761 block_info.pending_basefee = 70;
2762 pool.set_block_info(block_info);
2763
2764 let validated1 = tx_factory.validated(tx1);
2766 let validated2 = tx_factory.validated(tx2);
2767 let validated3 = tx_factory.validated(tx3);
2768
2769 pool.add_transaction(validated1, sender_balance, 0, None).unwrap();
2770 pool.add_transaction(validated2, sender_balance, 0, None).unwrap();
2771 pool.add_transaction(validated3, sender_balance, 0, None).unwrap();
2772
2773 let sender1_id = tx_factory.ids.sender_id(&sender1).unwrap();
2774 let sender2_id = tx_factory.ids.sender_id(&sender2).unwrap();
2775 let sender3_id = tx_factory.ids.sender_id(&sender3).unwrap();
2776
2777 assert_eq!(pool.basefee_pool.len(), 3, "All txs should be in basefee pool");
2779 assert_eq!(pool.pending_pool.len(), 0, "No txs should be in pending pool");
2780
2781 block_info.pending_basefee = 50;
2784
2785 let mut changed_senders = FxHashMap::default();
2787 changed_senders.insert(
2788 sender1_id,
2789 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2790 );
2791 changed_senders.insert(
2792 sender2_id,
2793 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2794 );
2795 changed_senders.insert(
2796 sender3_id,
2797 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2798 );
2799
2800 let outcome = pool.on_canonical_state_change(
2801 block_info,
2802 vec![],
2803 changed_senders,
2804 PoolUpdateKind::Commit,
2805 );
2806
2807 assert_eq!(pool.pending_pool.len(), 2, "tx1 and tx2 should be promoted");
2809 assert_eq!(pool.basefee_pool.len(), 1, "tx3 should remain in basefee");
2810
2811 assert_eq!(
2814 outcome.promoted.len(),
2815 2,
2816 "Should report exactly 2 promotions, not double-counted"
2817 );
2818
2819 let promoted_prices: Vec<u128> =
2821 outcome.promoted.iter().map(|tx| tx.max_fee_per_gas()).collect();
2822 assert!(promoted_prices.contains(&60));
2823 assert!(promoted_prices.contains(&55));
2824 }
2825
2826 #[test]
2827 fn test_basefee_decrease_with_empty_senders() {
2828 let mut tx_factory = MockTransactionFactory::default();
2831 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2832
2833 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2835
2836 let mut block_info = pool.block_info();
2838 block_info.pending_basefee = 100;
2839 pool.set_block_info(block_info);
2840
2841 let validated = tx_factory.validated(tx);
2843 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2844
2845 assert_eq!(pool.basefee_pool.len(), 1);
2846 assert_eq!(pool.pending_pool.len(), 0);
2847
2848 block_info.pending_basefee = 50;
2850 let outcome = pool.on_canonical_state_change(
2851 block_info,
2852 vec![],
2853 FxHashMap::default(), PoolUpdateKind::Commit,
2855 );
2856
2857 assert_eq!(pool.pending_pool.len(), 1, "Fee decrease should promote tx");
2859 assert_eq!(pool.basefee_pool.len(), 0);
2860 assert_eq!(outcome.promoted.len(), 1, "Should report promotion from fee update");
2861 }
2862
2863 #[test]
2864 fn test_basefee_decrease_account_makes_unfundable() {
2865 let mut tx_factory = MockTransactionFactory::default();
2868 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2869
2870 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2871 let sender = tx.sender();
2872
2873 let mut block_info = pool.block_info();
2875 block_info.pending_basefee = 100;
2876 pool.set_block_info(block_info);
2877
2878 let validated = tx_factory.validated(tx);
2879 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2880 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2881
2882 assert_eq!(pool.basefee_pool.len(), 1);
2883
2884 block_info.pending_basefee = 50;
2886 let mut changed_senders = FxHashMap::default();
2887 changed_senders.insert(
2888 sender_id,
2889 SenderInfo {
2890 state_nonce: 0,
2891 balance: U256::from(100), },
2893 );
2894
2895 let outcome = pool.on_canonical_state_change(
2896 block_info,
2897 vec![],
2898 changed_senders,
2899 PoolUpdateKind::Commit,
2900 );
2901
2902 assert_eq!(pool.pending_pool.len(), 0, "Unfunded tx should not be in pending");
2904 assert_eq!(pool.basefee_pool.len(), 0, "Tx no longer in basefee pool");
2905 assert_eq!(pool.queued_pool.len(), 1, "Unfunded tx should be in queued pool");
2906
2907 let tx_count = pool.all_transactions.txs.len();
2909 assert_eq!(tx_count, 1, "Transaction should still be in pool (in queued)");
2910
2911 assert_eq!(outcome.promoted.len(), 0, "Should not report promotion");
2912 assert_eq!(outcome.discarded.len(), 0, "Queued tx is not reported as discarded");
2913 }
2914
2915 #[test]
2916 fn insert_already_imported() {
2917 let on_chain_balance = U256::ZERO;
2918 let on_chain_nonce = 0;
2919 let mut f = MockTransactionFactory::default();
2920 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2921 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2922 let tx = f.validated(tx);
2923 pool.add_transaction(tx.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
2924 match pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap_err().kind {
2925 PoolErrorKind::AlreadyImported => {}
2926 _ => unreachable!(),
2927 }
2928 }
2929
2930 #[test]
2931 fn insert_replace() {
2932 let on_chain_balance = U256::ZERO;
2933 let on_chain_nonce = 0;
2934 let mut f = MockTransactionFactory::default();
2935 let mut pool = AllTransactions::default();
2936 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2937 let first = f.validated(tx.clone());
2938 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
2939 let replacement = f.validated(tx.rng_hash().inc_price());
2940 let InsertOk { updates, replaced_tx, .. } =
2941 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
2942 assert!(updates.is_empty());
2943 let replaced = replaced_tx.unwrap();
2944 assert_eq!(replaced.0.hash(), first.hash());
2945
2946 assert!(!pool.contains(first.hash()));
2948 assert!(pool.contains(replacement.hash()));
2949 assert_eq!(pool.len(), 1);
2950 }
2951
2952 #[test]
2953 fn insert_replace_txpool() {
2954 let on_chain_balance = U256::ZERO;
2955 let on_chain_nonce = 0;
2956 let mut f = MockTransactionFactory::default();
2957 let mut pool = TxPool::mock();
2958
2959 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2960 let first = f.validated(tx.clone());
2961 let first_added =
2962 pool.add_transaction(first, on_chain_balance, on_chain_nonce, None).unwrap();
2963 let replacement = f.validated(tx.rng_hash().inc_price());
2964 let replacement_added = pool
2965 .add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce, None)
2966 .unwrap();
2967
2968 assert!(!pool.contains(first_added.hash()));
2970 assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));
2972
2973 assert!(pool.contains(replacement.hash()));
2974 let size = pool.size();
2975 assert_eq!(size.total, 1);
2976 size.assert_invariants();
2977 }
2978
2979 #[test]
2980 fn insert_replace_underpriced() {
2981 let on_chain_balance = U256::ZERO;
2982 let on_chain_nonce = 0;
2983 let mut f = MockTransactionFactory::default();
2984 let mut pool = AllTransactions::default();
2985 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2986 let first = f.validated(tx.clone());
2987 let _res = pool.insert_tx(first, on_chain_balance, on_chain_nonce);
2988 let mut replacement = f.validated(tx.rng_hash());
2989 replacement.transaction = replacement.transaction.decr_price();
2990 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
2991 assert!(matches!(err, InsertErr::Underpriced { .. }));
2992 }
2993
2994 #[test]
2995 fn insert_replace_underpriced_not_enough_bump() {
2996 let on_chain_balance = U256::ZERO;
2997 let on_chain_nonce = 0;
2998 let mut f = MockTransactionFactory::default();
2999 let mut pool = AllTransactions::default();
3000 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3001 tx.set_priority_fee(100);
3002 tx.set_max_fee(100);
3003 let first = f.validated(tx.clone());
3004 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3005 let mut replacement = f.validated(tx.rng_hash().inc_price());
3006
3007 replacement.transaction.set_priority_fee(109);
3009 replacement.transaction.set_max_fee(109);
3010 let err =
3011 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3012 assert!(matches!(err, InsertErr::Underpriced { .. }));
3013 assert!(pool.contains(first.hash()));
3015 assert_eq!(pool.len(), 1);
3016
3017 replacement.transaction.set_priority_fee(110);
3019 replacement.transaction.set_max_fee(109);
3020 let err =
3021 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3022 assert!(matches!(err, InsertErr::Underpriced { .. }));
3023 assert!(pool.contains(first.hash()));
3024 assert_eq!(pool.len(), 1);
3025
3026 replacement.transaction.set_priority_fee(109);
3028 replacement.transaction.set_max_fee(110);
3029 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3030 assert!(matches!(err, InsertErr::Underpriced { .. }));
3031 assert!(pool.contains(first.hash()));
3032 assert_eq!(pool.len(), 1);
3033 }
3034
3035 #[test]
3036 fn insert_conflicting_type_normal_to_blob() {
3037 let on_chain_balance = U256::from(10_000);
3038 let on_chain_nonce = 0;
3039 let mut f = MockTransactionFactory::default();
3040 let mut pool = AllTransactions::default();
3041 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3042 let first = f.validated(tx.clone());
3043 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3044 let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3045 let blob = f.validated(tx);
3046 let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
3047 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3048 }
3049
3050 #[test]
3051 fn insert_conflicting_type_blob_to_normal() {
3052 let on_chain_balance = U256::from(10_000);
3053 let on_chain_nonce = 0;
3054 let mut f = MockTransactionFactory::default();
3055 let mut pool = AllTransactions::default();
3056 let tx = MockTransaction::eip4844().inc_price().inc_limit();
3057 let first = f.validated(tx.clone());
3058 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3059 let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3060 let tx = f.validated(tx);
3061 let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
3062 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3063 }
3064
3065 #[test]
3067 fn insert_previous() {
3068 let on_chain_balance = U256::ZERO;
3069 let on_chain_nonce = 0;
3070 let mut f = MockTransactionFactory::default();
3071 let mut pool = AllTransactions::default();
3072 let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
3073 let first = f.validated(tx.clone());
3074 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3075
3076 let first_in_pool = pool.get(first.id()).unwrap();
3077
3078 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3080
3081 let prev = f.validated(tx.prev());
3082 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3083 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3084
3085 assert!(updates.is_empty());
3087 assert!(replaced_tx.is_none());
3088 assert!(state.contains(TxState::NO_NONCE_GAPS));
3089 assert_eq!(move_to, SubPool::Queued);
3090
3091 let first_in_pool = pool.get(first.id()).unwrap();
3092 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3094 }
3095
3096 #[test]
3098 fn insert_with_updates() {
3099 let on_chain_balance = U256::from(10_000);
3100 let on_chain_nonce = 0;
3101 let mut f = MockTransactionFactory::default();
3102 let mut pool = AllTransactions::default();
3103 let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
3104 let first = f.validated(tx.clone());
3105 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3106
3107 let first_in_pool = pool.get(first.id()).unwrap();
3108 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3110 assert_eq!(SubPool::Queued, first_in_pool.subpool);
3111
3112 let prev = f.validated(tx.prev());
3113 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3114 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3115
3116 assert_eq!(updates.len(), 1);
3118 assert!(replaced_tx.is_none());
3119 assert!(state.contains(TxState::NO_NONCE_GAPS));
3120 assert_eq!(move_to, SubPool::Pending);
3121
3122 let first_in_pool = pool.get(first.id()).unwrap();
3123 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3125 assert_eq!(SubPool::Pending, first_in_pool.subpool);
3126 }
3127
3128 #[test]
3129 fn insert_previous_blocking() {
3130 let on_chain_balance = U256::from(1_000);
3131 let on_chain_nonce = 0;
3132 let mut f = MockTransactionFactory::default();
3133 let mut pool = AllTransactions::default();
3134 pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
3135 let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
3136 let first = f.validated(tx.clone());
3137
3138 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3139
3140 let first_in_pool = pool.get(first.id()).unwrap();
3141
3142 assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
3143 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3145
3146 let prev = f.validated(tx.prev());
3147 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3148 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3149
3150 assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3151 assert!(updates.is_empty());
3153 assert!(replaced_tx.is_none());
3154 assert!(state.contains(TxState::NO_NONCE_GAPS));
3155 assert_eq!(move_to, SubPool::BaseFee);
3156
3157 let first_in_pool = pool.get(first.id()).unwrap();
3158 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3160 }
3161
3162 #[test]
3163 fn rejects_spammer() {
3164 let on_chain_balance = U256::from(1_000);
3165 let on_chain_nonce = 0;
3166 let mut f = MockTransactionFactory::default();
3167 let mut pool = AllTransactions::default();
3168
3169 let mut tx = MockTransaction::eip1559();
3170 let unblocked_tx = tx.clone();
3171 for _ in 0..pool.max_account_slots {
3172 tx = tx.next();
3173 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
3174 }
3175
3176 assert_eq!(
3177 pool.max_account_slots,
3178 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3179 );
3180
3181 let err =
3182 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
3183 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
3184
3185 assert!(pool
3186 .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
3187 .is_ok());
3188 }
3189
3190 #[test]
3191 fn allow_local_spamming() {
3192 let on_chain_balance = U256::from(1_000);
3193 let on_chain_nonce = 0;
3194 let mut f = MockTransactionFactory::default();
3195 let mut pool = AllTransactions::default();
3196
3197 let mut tx = MockTransaction::eip1559();
3198 for _ in 0..pool.max_account_slots {
3199 tx = tx.next();
3200 pool.insert_tx(
3201 f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
3202 on_chain_balance,
3203 on_chain_nonce,
3204 )
3205 .unwrap();
3206 }
3207
3208 assert_eq!(
3209 pool.max_account_slots,
3210 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3211 );
3212
3213 pool.insert_tx(
3214 f.validated_with_origin(TransactionOrigin::Local, tx.next()),
3215 on_chain_balance,
3216 on_chain_nonce,
3217 )
3218 .unwrap();
3219 }
3220
3221 #[test]
3222 fn reject_tx_over_gas_limit() {
3223 let on_chain_balance = U256::from(1_000);
3224 let on_chain_nonce = 0;
3225 let mut f = MockTransactionFactory::default();
3226 let mut pool = AllTransactions::default();
3227
3228 let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
3229
3230 assert!(matches!(
3231 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
3232 Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
3233 ));
3234 }
3235
3236 #[test]
3237 fn test_tx_equal_gas_limit() {
3238 let on_chain_balance = U256::from(1_000);
3239 let on_chain_nonce = 0;
3240 let mut f = MockTransactionFactory::default();
3241 let mut pool = AllTransactions::default();
3242
3243 let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
3244
3245 let InsertOk { state, .. } =
3246 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
3247 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
3248 }
3249
3250 #[test]
3251 fn update_basefee_subpools() {
3252 let mut f = MockTransactionFactory::default();
3253 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3254
3255 let tx = MockTransaction::eip1559().inc_price_by(10);
3256 let validated = f.validated(tx.clone());
3257 let id = *validated.id();
3258 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3259
3260 assert_eq!(pool.pending_pool.len(), 1);
3261
3262 pool.update_basefee((tx.max_fee_per_gas() + 1) as u64, |_| {});
3263
3264 assert!(pool.pending_pool.is_empty());
3265 assert_eq!(pool.basefee_pool.len(), 1);
3266
3267 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3268 }
3269
3270 #[test]
3271 fn update_basefee_subpools_setting_block_info() {
3272 let mut f = MockTransactionFactory::default();
3273 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3274
3275 let tx = MockTransaction::eip1559().inc_price_by(10);
3276 let validated = f.validated(tx.clone());
3277 let id = *validated.id();
3278 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3279
3280 assert_eq!(pool.pending_pool.len(), 1);
3281
3282 let mut block_info = pool.block_info();
3284 block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
3285 pool.set_block_info(block_info);
3286
3287 assert!(pool.pending_pool.is_empty());
3288 assert_eq!(pool.basefee_pool.len(), 1);
3289
3290 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3291 }
3292
3293 #[test]
3294 fn basefee_decrease_promotes_affordable_and_keeps_unaffordable() {
3295 use alloy_primitives::address;
3296 let mut f = MockTransactionFactory::default();
3297 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3298
3299 let sender_a = address!("0x000000000000000000000000000000000000000a");
3302 let sender_b = address!("0x000000000000000000000000000000000000000b");
3303 let sender_c = address!("0x000000000000000000000000000000000000000c");
3304
3305 let tx1 = MockTransaction::eip1559()
3306 .set_sender(sender_a)
3307 .set_nonce(0)
3308 .set_max_fee(500)
3309 .inc_limit();
3310 let tx2 = MockTransaction::eip1559()
3311 .set_sender(sender_b)
3312 .set_nonce(0)
3313 .set_max_fee(600)
3314 .inc_limit();
3315 let tx3 = MockTransaction::eip1559()
3316 .set_sender(sender_c)
3317 .set_nonce(0)
3318 .set_max_fee(400)
3319 .inc_limit();
3320
3321 let mut block_info = pool.block_info();
3323 block_info.pending_basefee = 700;
3324 pool.set_block_info(block_info);
3325
3326 let validated1 = f.validated(tx1);
3327 let validated2 = f.validated(tx2);
3328 let validated3 = f.validated(tx3);
3329 let id1 = *validated1.id();
3330 let id2 = *validated2.id();
3331 let id3 = *validated3.id();
3332
3333 pool.add_transaction(validated1, U256::from(10_000), 0, None).unwrap();
3337 pool.add_transaction(validated2, U256::from(10_000), 0, None).unwrap();
3338 pool.add_transaction(validated3, U256::from(10_000), 0, None).unwrap();
3339
3340 println!("Basefee pool len: {}", pool.basefee_pool.len());
3342 println!("Pending pool len: {}", pool.pending_pool.len());
3343 println!("tx1 subpool: {:?}", pool.all_transactions.txs.get(&id1).unwrap().subpool);
3344 println!("tx2 subpool: {:?}", pool.all_transactions.txs.get(&id2).unwrap().subpool);
3345 println!("tx3 subpool: {:?}", pool.all_transactions.txs.get(&id3).unwrap().subpool);
3346
3347 assert_eq!(pool.basefee_pool.len(), 3);
3349 assert_eq!(pool.pending_pool.len(), 0);
3350 assert_eq!(pool.all_transactions.txs.get(&id1).unwrap().subpool, SubPool::BaseFee);
3351 assert_eq!(pool.all_transactions.txs.get(&id2).unwrap().subpool, SubPool::BaseFee);
3352 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3353
3354 let mut block_info = pool.block_info();
3356 block_info.pending_basefee = 450; pool.set_block_info(block_info);
3358
3359 assert_eq!(pool.basefee_pool.len(), 1);
3364 assert_eq!(pool.pending_pool.len(), 2);
3365
3366 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3368
3369 let tx1_meta = pool.all_transactions.txs.get(&id1).unwrap();
3371 let tx2_meta = pool.all_transactions.txs.get(&id2).unwrap();
3372 assert_eq!(tx1_meta.subpool, SubPool::Pending);
3373 assert_eq!(tx2_meta.subpool, SubPool::Pending);
3374 assert!(tx1_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3375 assert!(tx2_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3376
3377 let best: Vec<_> = pool.best_transactions().take(3).collect();
3379 assert_eq!(best.len(), 2); assert!(best.iter().any(|tx| tx.id() == &id1));
3381 assert!(best.iter().any(|tx| tx.id() == &id2));
3382 }
3383
3384 #[test]
3385 fn apply_fee_updates_records_promotions_after_basefee_drop() {
3386 let mut f = MockTransactionFactory::default();
3387 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3388
3389 let tx = MockTransaction::eip1559()
3390 .with_gas_limit(21_000)
3391 .with_max_fee(500)
3392 .with_priority_fee(1);
3393 let validated = f.validated(tx);
3394 let id = *validated.id();
3395 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3396
3397 assert_eq!(pool.pending_pool.len(), 1);
3398
3399 pool.update_basefee(600, |_| {});
3401 assert!(pool.pending_pool.is_empty());
3402 assert_eq!(pool.basefee_pool.len(), 1);
3403
3404 let prev_base_fee = 600;
3405 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3406
3407 pool.all_transactions.pending_fees.base_fee = 400;
3409
3410 let mut outcome = UpdateOutcome::default();
3411 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3412
3413 assert_eq!(pool.pending_pool.len(), 1);
3414 assert!(pool.basefee_pool.is_empty());
3415 assert_eq!(outcome.promoted.len(), 1);
3416 assert_eq!(outcome.promoted[0].id(), &id);
3417 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3418 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3419
3420 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3421 assert_eq!(tx_meta.subpool, SubPool::Pending);
3422 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3423 }
3424
3425 #[test]
3426 fn apply_fee_updates_records_promotions_after_blob_fee_drop() {
3427 let mut f = MockTransactionFactory::default();
3428 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3429
3430 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3431
3432 let tx = MockTransaction::eip4844().with_blob_fee(initial_blob_fee + 100);
3433 let validated = f.validated(tx.clone());
3434 let id = *validated.id();
3435 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3436
3437 assert_eq!(pool.pending_pool.len(), 1);
3438
3439 let increased_blob_fee = tx.max_fee_per_blob_gas().unwrap() + 200;
3441 pool.update_blob_fee(increased_blob_fee, Ordering::Equal, |_| {});
3442 assert!(pool.pending_pool.is_empty());
3443 assert_eq!(pool.blob_pool.len(), 1);
3444
3445 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3446 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3447
3448 pool.all_transactions.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
3450
3451 let mut outcome = UpdateOutcome::default();
3452 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3453
3454 assert_eq!(pool.pending_pool.len(), 1);
3455 assert!(pool.blob_pool.is_empty());
3456 assert_eq!(outcome.promoted.len(), 1);
3457 assert_eq!(outcome.promoted[0].id(), &id);
3458 assert_eq!(pool.all_transactions.pending_fees.base_fee, prev_base_fee);
3459 assert_eq!(pool.all_transactions.pending_fees.blob_fee, tx.max_fee_per_blob_gas().unwrap());
3460
3461 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3462 assert_eq!(tx_meta.subpool, SubPool::Pending);
3463 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3464 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3465 }
3466
3467 #[test]
3468 fn apply_fee_updates_promotes_blob_after_basefee_drop() {
3469 let mut f = MockTransactionFactory::default();
3470 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3471
3472 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3473
3474 let tx = MockTransaction::eip4844()
3475 .with_max_fee(500)
3476 .with_priority_fee(1)
3477 .with_blob_fee(initial_blob_fee + 100);
3478 let validated = f.validated(tx);
3479 let id = *validated.id();
3480 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3481
3482 assert_eq!(pool.pending_pool.len(), 1);
3483
3484 let high_base_fee = 600;
3486 pool.update_basefee(high_base_fee, |_| {});
3487 assert!(pool.pending_pool.is_empty());
3488 assert_eq!(pool.blob_pool.len(), 1);
3489
3490 let prev_base_fee = high_base_fee;
3491 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3492
3493 pool.all_transactions.pending_fees.base_fee = 400;
3495
3496 let mut outcome = UpdateOutcome::default();
3497 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3498
3499 assert_eq!(pool.pending_pool.len(), 1);
3500 assert!(pool.blob_pool.is_empty());
3501 assert_eq!(outcome.promoted.len(), 1);
3502 assert_eq!(outcome.promoted[0].id(), &id);
3503 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3504 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3505
3506 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3507 assert_eq!(tx_meta.subpool, SubPool::Pending);
3508 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3509 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3510 }
3511
3512 #[test]
3513 fn apply_fee_updates_demotes_after_basefee_rise() {
3514 let mut f = MockTransactionFactory::default();
3515 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3516
3517 let tx = MockTransaction::eip1559()
3518 .with_gas_limit(21_000)
3519 .with_max_fee(400)
3520 .with_priority_fee(1);
3521 let validated = f.validated(tx);
3522 let id = *validated.id();
3523 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3524
3525 assert_eq!(pool.pending_pool.len(), 1);
3526
3527 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3528 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3529
3530 let new_base_fee = prev_base_fee + 1_000;
3532 pool.all_transactions.pending_fees.base_fee = new_base_fee;
3533
3534 let mut outcome = UpdateOutcome::default();
3535 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3536
3537 assert!(pool.pending_pool.is_empty());
3538 assert_eq!(pool.basefee_pool.len(), 1);
3539 assert!(outcome.promoted.is_empty());
3540 assert_eq!(pool.all_transactions.pending_fees.base_fee, new_base_fee);
3541 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3542
3543 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3544 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
3545 assert!(!tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3546 }
3547
3548 #[test]
3549 fn get_highest_transaction_by_sender_and_nonce() {
3550 let mut f = MockTransactionFactory::default();
3552 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3553
3554 let tx = MockTransaction::eip1559();
3556 pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0, None).unwrap();
3557
3558 let tx1 = tx.inc_price().next();
3560
3561 let tx1_validated = f.validated(tx1.clone());
3563 pool.add_transaction(tx1_validated, U256::from(1_000), 0, None).unwrap();
3564
3565 assert_eq!(
3567 pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
3568 Some(1)
3569 );
3570
3571 let highest_tx = pool
3573 .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
3574 .expect("Failed to retrieve highest transaction");
3575
3576 assert_eq!(highest_tx.as_ref().transaction, tx1);
3578 }
3579
3580 #[test]
3581 fn get_highest_consecutive_transaction_by_sender() {
3582 let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
3584 let mut f = MockTransactionFactory::default();
3585
3586 let sender = Address::random();
3588 let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
3589 for nonce in txs {
3590 let mut mock_tx = MockTransaction::eip1559();
3591 mock_tx.set_sender(sender);
3592 mock_tx.set_nonce(nonce);
3593
3594 let validated_tx = f.validated(mock_tx);
3595 pool.add_transaction(validated_tx, U256::from(1000), 0, None).unwrap();
3596 }
3597
3598 let sender_id = f.ids.sender_id(&sender).unwrap();
3600 let next_tx =
3601 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
3602 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
3603
3604 let next_tx =
3605 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
3606 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
3607
3608 let next_tx =
3609 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3610 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
3611
3612 let mut info = SenderInfo::default();
3614 info.update(8, U256::ZERO);
3615 pool.sender_info.insert(sender_id, info);
3616 let next_tx =
3617 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3618 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
3619 }
3620
3621 #[test]
3622 fn discard_nonce_too_low() {
3623 let mut f = MockTransactionFactory::default();
3624 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3625
3626 let tx = MockTransaction::eip1559().inc_price_by(10);
3627 let validated = f.validated(tx.clone());
3628 let id = *validated.id();
3629 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3630
3631 let next = tx.next();
3632 let validated = f.validated(next.clone());
3633 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3634
3635 assert_eq!(pool.pending_pool.len(), 2);
3636
3637 let mut changed_senders = HashMap::default();
3638 changed_senders.insert(
3639 id.sender,
3640 SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
3641 );
3642 let outcome = pool.update_accounts(changed_senders);
3643 assert_eq!(outcome.discarded.len(), 1);
3644 assert_eq!(pool.pending_pool.len(), 1);
3645 }
3646
3647 #[test]
3648 fn discard_with_large_blob_txs() {
3649 reth_tracing::init_test_tracing();
3651
3652 let mut f = MockTransactionFactory::default();
3654 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3655 let default_limits = pool.config.blob_limit;
3656
3657 let a_sender = address!("0x000000000000000000000000000000000000000a");
3660
3661 let mut block_info = pool.block_info();
3663 block_info.pending_blob_fee = Some(100);
3664 block_info.pending_basefee = 100;
3665
3666 pool.set_block_info(block_info);
3668
3669 let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
3671 .into_iter()
3672 .map(|mut tx| {
3673 tx.set_size(default_limits.max_size / 2 + 1);
3674 tx.set_max_fee((block_info.pending_basefee - 1).into());
3675 tx
3676 })
3677 .collect::<Vec<_>>();
3678
3679 for tx in a_txs {
3681 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3682 }
3683
3684 let removed = pool.discard_worst();
3686 assert_eq!(removed.len(), 1);
3687 }
3688
3689 #[test]
3690 fn discard_with_parked_large_txs() {
3691 reth_tracing::init_test_tracing();
3693
3694 let mut f = MockTransactionFactory::default();
3696 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3697 let default_limits = pool.config.queued_limit;
3698
3699 let a_sender = address!("0x000000000000000000000000000000000000000a");
3702
3703 let pool_base_fee = 100;
3705 pool.update_basefee(pool_base_fee, |_| {});
3706
3707 let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
3709 .into_iter()
3710 .map(|mut tx| {
3711 tx.set_size(default_limits.max_size / 2 + 1);
3712 tx.set_max_fee((pool_base_fee - 1).into());
3713 tx
3714 })
3715 .collect::<Vec<_>>();
3716
3717 for tx in a_txs {
3719 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3720 }
3721
3722 let removed = pool.discard_worst();
3724 assert_eq!(removed.len(), 1);
3725 }
3726
3727 #[test]
3728 fn discard_at_capacity() {
3729 let mut f = MockTransactionFactory::default();
3730 let queued_limit = SubPoolLimit::new(1000, usize::MAX);
3731 let mut pool =
3732 TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
3733
3734 for _ in 0..queued_limit.max_txs {
3736 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3737 let validated = f.validated(tx.clone());
3738 let _id = *validated.id();
3739 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3740 }
3741
3742 let size = pool.size();
3743 assert_eq!(size.queued, queued_limit.max_txs);
3744
3745 for _ in 0..queued_limit.max_txs {
3746 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3747 let validated = f.validated(tx.clone());
3748 let _id = *validated.id();
3749 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3750
3751 pool.discard_worst();
3752 pool.assert_invariants();
3753 assert!(pool.size().queued <= queued_limit.max_txs);
3754 }
3755 }
3756
3757 #[test]
3758 fn discard_blobs_at_capacity() {
3759 let mut f = MockTransactionFactory::default();
3760 let blob_limit = SubPoolLimit::new(1000, usize::MAX);
3761 let mut pool =
3762 TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
3763 pool.all_transactions.pending_fees.blob_fee = 10000;
3764 for _ in 0..blob_limit.max_txs {
3766 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3767 let validated = f.validated(tx.clone());
3768 let _id = *validated.id();
3769 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3770 }
3771
3772 let size = pool.size();
3773 assert_eq!(size.blob, blob_limit.max_txs);
3774
3775 for _ in 0..blob_limit.max_txs {
3776 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3777 let validated = f.validated(tx.clone());
3778 let _id = *validated.id();
3779 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3780
3781 pool.discard_worst();
3782 pool.assert_invariants();
3783 assert!(pool.size().blob <= blob_limit.max_txs);
3784 }
3785 }
3786
3787 #[test]
3788 fn account_updates_sender_balance() {
3789 let mut on_chain_balance = U256::from(100);
3790 let on_chain_nonce = 0;
3791 let mut f = MockTransactionFactory::default();
3792 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3793
3794 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3795 let tx_1 = tx_0.next();
3796 let tx_2 = tx_1.next();
3797
3798 let v0 = f.validated(tx_0);
3800 let v1 = f.validated(tx_1);
3801 let v2 = f.validated(tx_2);
3802
3803 let _res =
3804 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3805 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3806 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3807
3808 assert_eq!(1, pool.pending_transactions().len());
3810 assert_eq!(2, pool.queued_transactions().len());
3811
3812 let mut updated_accounts = HashMap::default();
3814 on_chain_balance = U256::from(300);
3815 updated_accounts.insert(
3816 v0.sender_id(),
3817 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3818 );
3819 pool.update_accounts(updated_accounts.clone());
3820
3821 assert_eq!(3, pool.pending_transactions().len());
3822 assert!(pool.queued_transactions().is_empty());
3823
3824 updated_accounts.entry(v0.sender_id()).and_modify(|v| v.balance = U256::from(1));
3826 pool.update_accounts(updated_accounts);
3827
3828 assert!(pool.pending_transactions().is_empty());
3829 assert_eq!(3, pool.queued_transactions().len());
3830 }
3831
3832 #[test]
3833 fn account_updates_nonce_gap() {
3834 let on_chain_balance = U256::from(10_000);
3835 let mut on_chain_nonce = 0;
3836 let mut f = MockTransactionFactory::default();
3837 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3838
3839 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3840 let tx_1 = tx_0.next();
3841 let tx_2 = tx_1.next();
3842
3843 let v0 = f.validated(tx_0);
3845 let v1 = f.validated(tx_1);
3846 let v2 = f.validated(tx_2);
3847
3848 let _res =
3850 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3851 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3852
3853 assert!(pool.queued_transactions().is_empty());
3854 assert_eq!(2, pool.pending_transactions().len());
3855
3856 pool.remove_transaction_by_hash(v0.hash());
3858
3859 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3861
3862 assert_eq!(2, pool.queued_transactions().len());
3864 assert!(pool.pending_transactions().is_empty());
3865
3866 let mut updated_accounts = HashMap::default();
3868 on_chain_nonce += 1;
3869 updated_accounts.insert(
3870 v0.sender_id(),
3871 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3872 );
3873 pool.update_accounts(updated_accounts);
3874
3875 assert!(pool.queued_transactions().is_empty());
3877 assert_eq!(2, pool.pending_transactions().len());
3878 }
3879 #[test]
3880 fn test_transaction_removal() {
3881 let on_chain_balance = U256::from(10_000);
3882 let on_chain_nonce = 0;
3883 let mut f = MockTransactionFactory::default();
3884 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3885
3886 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3887 let tx_1 = tx_0.next();
3888
3889 let v0 = f.validated(tx_0);
3891 let v1 = f.validated(tx_1);
3892
3893 let _res =
3895 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3896 let _res =
3897 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3898
3899 assert_eq!(0, pool.queued_transactions().len());
3900 assert_eq!(2, pool.pending_transactions().len());
3901
3902 pool.remove_transaction(v0.id());
3904 let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
3906 assert_eq!(vec![v1.nonce()], pool_txs);
3907 }
3908 #[test]
3909 fn test_remove_transactions() {
3910 let on_chain_balance = U256::from(10_000);
3911 let on_chain_nonce = 0;
3912 let mut f = MockTransactionFactory::default();
3913 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3914
3915 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3916 let tx_1 = tx_0.next();
3917 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3918 let tx_3 = tx_2.next();
3919
3920 let v0 = f.validated(tx_0);
3922 let v1 = f.validated(tx_1);
3923 let v2 = f.validated(tx_2);
3924 let v3 = f.validated(tx_3);
3925
3926 let _res =
3928 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3929 let _res =
3930 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3931 let _res =
3932 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3933 let _res =
3934 pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3935
3936 assert_eq!(0, pool.queued_transactions().len());
3937 assert_eq!(4, pool.pending_transactions().len());
3938
3939 pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
3940
3941 assert_eq!(2, pool.queued_transactions().len());
3942 assert!(pool.pending_transactions().is_empty());
3943 assert!(pool.contains(v1.hash()));
3944 assert!(pool.contains(v3.hash()));
3945 }
3946
3947 #[test]
3948 fn test_remove_transactions_middle_pending_hash() {
3949 let on_chain_balance = U256::from(10_000);
3950 let on_chain_nonce = 0;
3951 let mut f = MockTransactionFactory::default();
3952 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3953
3954 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3955 let tx_1 = tx_0.next();
3956 let tx_2 = tx_1.next();
3957 let tx_3 = tx_2.next();
3958
3959 let v0 = f.validated(tx_0);
3961 let v1 = f.validated(tx_1);
3962 let v2 = f.validated(tx_2);
3963 let v3 = f.validated(tx_3);
3964
3965 let _res = pool.add_transaction(v0, on_chain_balance, on_chain_nonce, None).unwrap();
3967 let _res =
3968 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3969 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3970 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
3971
3972 assert_eq!(0, pool.queued_transactions().len());
3973 assert_eq!(4, pool.pending_transactions().len());
3974
3975 let mut removed_txs = pool.remove_transactions(vec![*v1.hash()]);
3976 assert_eq!(1, removed_txs.len());
3977
3978 assert_eq!(2, pool.queued_transactions().len());
3979 assert_eq!(1, pool.pending_transactions().len());
3980
3981 let removed_tx = removed_txs.pop().unwrap();
3983 let v1 = f.validated(removed_tx.transaction.clone());
3984 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3985 assert_eq!(0, pool.queued_transactions().len());
3986 assert_eq!(4, pool.pending_transactions().len());
3987 }
3988
3989 #[test]
3990 fn test_remove_transactions_and_descendants() {
3991 let on_chain_balance = U256::from(10_000);
3992 let on_chain_nonce = 0;
3993 let mut f = MockTransactionFactory::default();
3994 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3995
3996 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3997 let tx_1 = tx_0.next();
3998 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3999 let tx_3 = tx_2.next();
4000 let tx_4 = tx_3.next();
4001
4002 let v0 = f.validated(tx_0);
4004 let v1 = f.validated(tx_1);
4005 let v2 = f.validated(tx_2);
4006 let v3 = f.validated(tx_3);
4007 let v4 = f.validated(tx_4);
4008
4009 let _res =
4011 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4012 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4013 let _res =
4014 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4015 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4016 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4017
4018 assert_eq!(0, pool.queued_transactions().len());
4019 assert_eq!(5, pool.pending_transactions().len());
4020
4021 pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
4022
4023 assert_eq!(0, pool.queued_transactions().len());
4024 assert_eq!(0, pool.pending_transactions().len());
4025 }
4026 #[test]
4027 fn test_remove_descendants() {
4028 let on_chain_balance = U256::from(10_000);
4029 let on_chain_nonce = 0;
4030 let mut f = MockTransactionFactory::default();
4031 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4032
4033 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4034 let tx_1 = tx_0.next();
4035 let tx_2 = tx_1.next();
4036 let tx_3 = tx_2.next();
4037
4038 let v0 = f.validated(tx_0);
4040 let v1 = f.validated(tx_1);
4041 let v2 = f.validated(tx_2);
4042 let v3 = f.validated(tx_3);
4043
4044 let _res =
4046 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4047 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4048 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4049 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4050
4051 assert_eq!(0, pool.queued_transactions().len());
4052 assert_eq!(4, pool.pending_transactions().len());
4053
4054 let mut removed = Vec::new();
4055 pool.remove_transaction(v0.id());
4056 pool.remove_descendants(v0.id(), &mut removed);
4057
4058 assert_eq!(0, pool.queued_transactions().len());
4059 assert_eq!(0, pool.pending_transactions().len());
4060 assert_eq!(3, removed.len());
4061 }
4062 #[test]
4063 fn test_remove_transactions_by_sender() {
4064 let on_chain_balance = U256::from(10_000);
4065 let on_chain_nonce = 0;
4066 let mut f = MockTransactionFactory::default();
4067 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4068
4069 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4070 let tx_1 = tx_0.next();
4071 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4072 let tx_3 = tx_2.next();
4073 let tx_4 = tx_3.next();
4074
4075 let v0 = f.validated(tx_0);
4077 let v1 = f.validated(tx_1);
4078 let v2 = f.validated(tx_2);
4079 let v3 = f.validated(tx_3);
4080 let v4 = f.validated(tx_4);
4081
4082 let _res =
4084 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4085 let _res =
4086 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4087 let _res =
4088 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4089 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4090 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4091
4092 assert_eq!(0, pool.queued_transactions().len());
4093 assert_eq!(5, pool.pending_transactions().len());
4094
4095 pool.remove_transactions_by_sender(v2.sender_id());
4096
4097 assert_eq!(0, pool.queued_transactions().len());
4098 assert_eq!(2, pool.pending_transactions().len());
4099 assert!(pool.contains(v0.hash()));
4100 assert!(pool.contains(v1.hash()));
4101 }
4102 #[test]
4103 fn wrong_best_order_of_transactions() {
4104 let on_chain_balance = U256::from(10_000);
4105 let mut on_chain_nonce = 0;
4106 let mut f = MockTransactionFactory::default();
4107 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4108
4109 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4110 let tx_1 = tx_0.next();
4111 let tx_2 = tx_1.next();
4112 let tx_3 = tx_2.next();
4113
4114 let v0 = f.validated(tx_0);
4116 let v1 = f.validated(tx_1);
4117 let v2 = f.validated(tx_2);
4118 let v3 = f.validated(tx_3);
4119
4120 let _res =
4122 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4123 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4124
4125 assert_eq!(0, pool.queued_transactions().len());
4126 assert_eq!(2, pool.pending_transactions().len());
4127
4128 pool.remove_transaction(v0.id());
4130
4131 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4133
4134 assert_eq!(1, pool.queued_transactions().len());
4136 assert_eq!(1, pool.pending_transactions().len());
4137
4138 let mut updated_accounts = HashMap::default();
4140 on_chain_nonce += 1;
4141 updated_accounts.insert(
4142 v0.sender_id(),
4143 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4144 );
4145 pool.update_accounts(updated_accounts);
4146
4147 assert_eq!(0, pool.queued_transactions().len());
4150 assert_eq!(2, pool.pending_transactions().len());
4151
4152 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4154 assert_eq!(0, pool.queued_transactions().len());
4155 assert_eq!(3, pool.pending_transactions().len());
4156
4157 assert_eq!(
4160 pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
4161 vec![1, 2, 3]
4162 );
4163 }
4164
4165 #[test]
4166 fn test_best_with_attributes() {
4167 let on_chain_balance = U256::MAX;
4168 let on_chain_nonce = 0;
4169 let mut f = MockTransactionFactory::default();
4170 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4171
4172 let base_fee: u128 = 100;
4173 let blob_fee: u128 = 100;
4174
4175 let mut block_info = pool.block_info();
4177 block_info.pending_basefee = base_fee as u64;
4178 block_info.pending_blob_fee = Some(blob_fee);
4179 pool.set_block_info(block_info);
4180
4181 let tx1 = MockTransaction::eip4844()
4183 .with_sender(Address::with_last_byte(1))
4184 .with_max_fee(base_fee + 10)
4185 .with_blob_fee(blob_fee + 10);
4186 let tx2 = MockTransaction::eip4844()
4187 .with_sender(Address::with_last_byte(2))
4188 .with_max_fee(base_fee + 10)
4189 .with_blob_fee(blob_fee);
4190 let tx3 = MockTransaction::eip4844()
4191 .with_sender(Address::with_last_byte(3))
4192 .with_max_fee(base_fee)
4193 .with_blob_fee(blob_fee + 10);
4194 let tx4 = MockTransaction::eip4844()
4195 .with_sender(Address::with_last_byte(4))
4196 .with_max_fee(base_fee)
4197 .with_blob_fee(blob_fee);
4198 let tx5 = MockTransaction::eip4844()
4199 .with_sender(Address::with_last_byte(5))
4200 .with_max_fee(base_fee)
4201 .with_blob_fee(blob_fee - 10);
4202 let tx6 = MockTransaction::eip4844()
4203 .with_sender(Address::with_last_byte(6))
4204 .with_max_fee(base_fee - 10)
4205 .with_blob_fee(blob_fee);
4206 let tx7 = MockTransaction::eip4844()
4207 .with_sender(Address::with_last_byte(7))
4208 .with_max_fee(base_fee - 10)
4209 .with_blob_fee(blob_fee - 10);
4210
4211 for tx in vec![
4212 tx1.clone(),
4213 tx2.clone(),
4214 tx3.clone(),
4215 tx4.clone(),
4216 tx5.clone(),
4217 tx6.clone(),
4218 tx7.clone(),
4219 ] {
4220 pool.add_transaction(f.validated(tx.clone()), on_chain_balance, on_chain_nonce, None)
4221 .unwrap();
4222 }
4223
4224 let base_fee = base_fee as u64;
4225 let blob_fee = blob_fee as u64;
4226
4227 let cases = vec![
4228 (BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee + 5)), vec![tx1.clone()]),
4230 (
4232 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee)),
4233 vec![tx1.clone(), tx2.clone()],
4234 ),
4235 (
4237 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee - 5)),
4238 vec![tx1.clone(), tx2.clone()],
4239 ),
4240 (
4242 BestTransactionsAttributes::new(base_fee, Some(blob_fee + 5)),
4243 vec![tx1.clone(), tx3.clone()],
4244 ),
4245 (
4247 BestTransactionsAttributes::new(base_fee, Some(blob_fee)),
4248 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
4249 ),
4250 (
4252 BestTransactionsAttributes::new(base_fee, Some(blob_fee - 10)),
4253 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx5.clone()],
4254 ),
4255 (
4257 BestTransactionsAttributes::new(base_fee - 5, Some(blob_fee + 5)),
4258 vec![tx1.clone(), tx3.clone()],
4259 ),
4260 (
4262 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee)),
4263 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx6.clone()],
4264 ),
4265 (
4267 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee - 10)),
4268 vec![tx1, tx2, tx5, tx3, tx4, tx6, tx7],
4269 ),
4270 ];
4271
4272 for (idx, (attribute, expected)) in cases.into_iter().enumerate() {
4273 let mut best = pool.best_transactions_with_attributes(attribute);
4274
4275 for (tx_idx, expected_tx) in expected.into_iter().enumerate() {
4276 let tx = best.next().expect("Transaction should be returned");
4277 assert_eq!(
4278 tx.transaction,
4279 expected_tx,
4280 "Failed tx {} in case {}",
4281 tx_idx + 1,
4282 idx + 1
4283 );
4284 }
4285
4286 assert!(best.next().is_none());
4288 }
4289 }
4290
4291 #[test]
4292 fn test_pending_ordering() {
4293 let mut f = MockTransactionFactory::default();
4294 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4295
4296 let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
4297 let tx_1 = tx_0.next();
4298
4299 let v0 = f.validated(tx_0);
4300 let v1 = f.validated(tx_1);
4301
4302 pool.add_transaction(v0.clone(), U256::MAX, 0, None).unwrap();
4304 assert_eq!(1, pool.queued_transactions().len());
4305
4306 pool.add_transaction(v1, U256::MAX, 1, None).unwrap();
4308
4309 assert_eq!(2, pool.pending_transactions().len());
4310 assert_eq!(0, pool.queued_transactions().len());
4311
4312 assert_eq!(
4313 pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
4314 v0.nonce()
4315 );
4316 }
4317
4318 #[test]
4320 fn one_sender_one_independent_transaction() {
4321 let mut on_chain_balance = U256::from(4_999); let mut on_chain_nonce = 40;
4323 let mut f = MockTransactionFactory::default();
4324 let mut pool = TxPool::mock();
4325 let mut submitted_txs = Vec::new();
4326
4327 let template =
4329 MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
4330
4331 for tx_nonce in 40..48 {
4334 let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
4335 submitted_txs.push(*tx.id());
4336 pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap();
4337 }
4338
4339 on_chain_balance = U256::from(999_999);
4342 on_chain_nonce = 42;
4343 pool.remove_transaction(&submitted_txs[0]);
4344 pool.remove_transaction(&submitted_txs[1]);
4345
4346 for tx_nonce in 48..52 {
4348 pool.add_transaction(
4349 f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
4350 on_chain_balance,
4351 on_chain_nonce,
4352 None,
4353 )
4354 .unwrap();
4355 }
4356
4357 let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
4358 assert_eq!(best_txs.len(), 10); assert_eq!(pool.pending_pool.independent().len(), 1);
4361 }
4362
4363 #[test]
4364 fn test_insertion_disorder() {
4365 let mut f = MockTransactionFactory::default();
4366 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4367
4368 let sender = address!("0x1234567890123456789012345678901234567890");
4369 let tx0 = f.validated_arc(
4370 MockTransaction::legacy().with_sender(sender).with_nonce(0).with_gas_price(10),
4371 );
4372 let tx1 = f.validated_arc(
4373 MockTransaction::eip1559()
4374 .with_sender(sender)
4375 .with_nonce(1)
4376 .with_gas_limit(1000)
4377 .with_gas_price(10),
4378 );
4379 let tx2 = f.validated_arc(
4380 MockTransaction::legacy().with_sender(sender).with_nonce(2).with_gas_price(10),
4381 );
4382 let tx3 = f.validated_arc(
4383 MockTransaction::legacy().with_sender(sender).with_nonce(3).with_gas_price(10),
4384 );
4385
4386 pool.add_transaction((*tx0).clone(), U256::from(1000), 0, None).unwrap();
4388 let mut best = pool.best_transactions();
4389 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4390 assert_eq!(t0.id(), tx0.id());
4391 pool.add_transaction((*tx1).clone(), U256::from(1000), 0, None).unwrap();
4393 let mut best = pool.best_transactions();
4394 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4395 assert_eq!(t0.id(), tx0.id());
4396 assert!(best.next().is_none());
4397
4398 pool.add_transaction((*tx2).clone(), U256::MAX, 0, None).unwrap();
4400
4401 let mut best = pool.best_transactions();
4402
4403 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4404 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4405 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4406 assert_eq!(t0.id(), tx0.id());
4407 assert_eq!(t1.id(), tx1.id());
4408 assert_eq!(t2.id(), tx2.id());
4409
4410 pool.add_transaction((*tx3).clone(), U256::MAX, 0, None).unwrap();
4412 let mut best = pool.best_transactions();
4413 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4414 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4415 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4416 let t3 = best.next().expect("tx3 should be put in the pending subpool");
4417 assert_eq!(t0.id(), tx0.id());
4418 assert_eq!(t1.id(), tx1.id());
4419 assert_eq!(t2.id(), tx2.id());
4420 assert_eq!(t3.id(), tx3.id());
4421 }
4422
4423 #[test]
4424 fn test_non_4844_blob_fee_bit_invariant() {
4425 let mut f = MockTransactionFactory::default();
4426 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4427
4428 let non_4844_tx = MockTransaction::eip1559().set_max_fee(200).inc_limit();
4429 let validated = f.validated(non_4844_tx.clone());
4430
4431 assert!(!non_4844_tx.is_eip4844());
4432 pool.add_transaction(validated.clone(), U256::from(10_000), 0, None).unwrap();
4433
4434 let tx_meta = pool.all_transactions.txs.get(validated.id()).unwrap();
4436 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4437 assert_eq!(tx_meta.subpool, SubPool::Pending);
4438 }
4439
4440 #[test]
4441 fn test_blob_fee_enforcement_only_applies_to_eip4844() {
4442 let mut f = MockTransactionFactory::default();
4443 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4444
4445 let mut block_info = pool.block_info();
4447 block_info.pending_blob_fee = Some(160);
4448 block_info.pending_basefee = 100;
4449 pool.set_block_info(block_info);
4450
4451 let eip4844_tx = MockTransaction::eip4844()
4452 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4453 .with_max_fee(200)
4454 .with_blob_fee(150) .inc_limit();
4456
4457 let non_4844_tx = MockTransaction::eip1559()
4458 .with_sender(address!("0x000000000000000000000000000000000000000b"))
4459 .set_max_fee(200)
4460 .inc_limit();
4461
4462 let validated_4844 = f.validated(eip4844_tx);
4463 let validated_non_4844 = f.validated(non_4844_tx);
4464
4465 pool.add_transaction(validated_4844.clone(), U256::from(10_000), 0, None).unwrap();
4466 pool.add_transaction(validated_non_4844.clone(), U256::from(10_000), 0, None).unwrap();
4467
4468 let tx_4844_meta = pool.all_transactions.txs.get(validated_4844.id()).unwrap();
4469 let tx_non_4844_meta = pool.all_transactions.txs.get(validated_non_4844.id()).unwrap();
4470
4471 assert!(!tx_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4473 assert_eq!(tx_4844_meta.subpool, SubPool::Blob);
4474
4475 assert!(tx_non_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4477 assert_eq!(tx_non_4844_meta.subpool, SubPool::Pending);
4478 }
4479
4480 #[test]
4481 fn test_basefee_decrease_preserves_non_4844_blob_fee_bit() {
4482 let mut f = MockTransactionFactory::default();
4483 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4484
4485 let non_4844_tx = MockTransaction::eip1559()
4487 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4488 .set_max_fee(500) .inc_limit();
4490
4491 pool.update_basefee(600, |_| {});
4493
4494 let validated = f.validated(non_4844_tx);
4495 let tx_id = *validated.id();
4496 pool.add_transaction(validated, U256::from(10_000), 0, None).unwrap();
4497
4498 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4500 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
4501 assert!(
4502 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4503 "Non-4844 tx in BaseFee pool must retain ENOUGH_BLOB_FEE_CAP_BLOCK bit"
4504 );
4505
4506 pool.update_basefee(400, |_| {});
4509
4510 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4512 assert_eq!(
4513 tx_meta.subpool,
4514 SubPool::Pending,
4515 "Non-4844 tx should be promoted from BaseFee to Pending after basefee decrease"
4516 );
4517 assert!(
4518 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4519 "Non-4844 tx must NEVER lose ENOUGH_BLOB_FEE_CAP_BLOCK bit during basefee promotion"
4520 );
4521 assert!(
4522 tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK),
4523 "Non-4844 tx should gain ENOUGH_FEE_CAP_BLOCK bit after basefee decrease"
4524 );
4525 }
4526}