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 pending_pool: PendingPool<T>,
92 config: PoolConfig,
94 queued_pool: ParkedPool<QueuedOrd<T::Transaction>>,
101 basefee_pool: ParkedPool<BasefeeOrd<T::Transaction>>,
106 blob_pool: BlobTransactions<T::Transaction>,
113 all_transactions: AllTransactions<T::Transaction>,
115 metrics: TxPoolMetrics,
117}
118
119impl<T: TransactionOrdering> TxPool<T> {
122 pub fn new(ordering: T, config: PoolConfig) -> Self {
124 Self {
125 pending_pool: PendingPool::with_buffer(
126 ordering,
127 config.max_new_pending_txs_notifications,
128 ),
129 queued_pool: Default::default(),
130 basefee_pool: Default::default(),
131 blob_pool: Default::default(),
132 all_transactions: AllTransactions::new(&config),
133 config,
134 metrics: Default::default(),
135 }
136 }
137
138 pub fn get_highest_nonce_by_sender(&self, sender: SenderId) -> Option<u64> {
140 self.all().txs_iter(sender).last().map(|(_, tx)| tx.transaction.nonce())
141 }
142
143 pub fn get_highest_transaction_by_sender(
146 &self,
147 sender: SenderId,
148 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
149 self.all().txs_iter(sender).last().map(|(_, tx)| Arc::clone(&tx.transaction))
150 }
151
152 pub(crate) fn get_highest_consecutive_transaction_by_sender(
159 &self,
160 mut on_chain: TransactionId,
161 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
162 let mut last_consecutive_tx = None;
163
164 if let Some(current) = self.all_transactions.sender_info.get(&on_chain.sender) {
166 on_chain.nonce = on_chain.nonce.max(current.state_nonce);
167 }
168
169 let mut next_expected_nonce = on_chain.nonce;
170 for (id, tx) in self.all().descendant_txs_inclusive(&on_chain) {
171 if next_expected_nonce != id.nonce {
172 break
173 }
174 next_expected_nonce = id.next_nonce();
175 last_consecutive_tx = Some(tx);
176 }
177
178 last_consecutive_tx.map(|tx| Arc::clone(&tx.transaction))
179 }
180
181 pub(crate) const fn all(&self) -> &AllTransactions<T::Transaction> {
183 &self.all_transactions
184 }
185
186 pub(crate) fn unique_senders(&self) -> HashSet<Address> {
188 self.all_transactions.txs.values().map(|tx| tx.transaction.sender()).collect()
189 }
190
191 pub fn size(&self) -> PoolSize {
193 PoolSize {
194 pending: self.pending_pool.len(),
195 pending_size: self.pending_pool.size(),
196 basefee: self.basefee_pool.len(),
197 basefee_size: self.basefee_pool.size(),
198 queued: self.queued_pool.len(),
199 queued_size: self.queued_pool.size(),
200 blob: self.blob_pool.len(),
201 blob_size: self.blob_pool.size(),
202 total: self.all_transactions.len(),
203 }
204 }
205
206 pub const fn block_info(&self) -> BlockInfo {
208 BlockInfo {
209 block_gas_limit: self.all_transactions.block_gas_limit,
210 last_seen_block_hash: self.all_transactions.last_seen_block_hash,
211 last_seen_block_number: self.all_transactions.last_seen_block_number,
212 pending_basefee: self.all_transactions.pending_fees.base_fee,
213 pending_blob_fee: Some(self.all_transactions.pending_fees.blob_fee),
214 }
215 }
216
217 fn update_blob_fee<F>(
219 &mut self,
220 mut pending_blob_fee: u128,
221 base_fee_update: Ordering,
222 mut on_promoted: F,
223 ) where
224 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
225 {
226 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut pending_blob_fee);
227 match (self.all_transactions.pending_fees.blob_fee.cmp(&pending_blob_fee), base_fee_update)
228 {
229 (Ordering::Equal, Ordering::Equal | Ordering::Greater) => {
230 }
232 (Ordering::Greater, Ordering::Equal | Ordering::Greater) => {
233 let removed =
235 self.pending_pool.update_blob_fee(self.all_transactions.pending_fees.blob_fee);
236 for tx in removed {
237 let to = {
238 let tx =
239 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
240
241 tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
243 tx.subpool = tx.state.into();
244 tx.subpool
245 };
246 self.add_transaction_to_subpool(to, tx);
247 }
248 }
249 (Ordering::Less, _) | (_, Ordering::Less) => {
250 let removed =
252 self.blob_pool.enforce_pending_fees(&self.all_transactions.pending_fees);
253 for tx in removed {
254 let subpool = {
255 let tx_meta =
256 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
257 tx_meta.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
258 tx_meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
259 tx_meta.subpool = tx_meta.state.into();
260 tx_meta.subpool
261 };
262
263 if subpool == SubPool::Pending {
264 on_promoted(&tx);
265 }
266
267 self.add_transaction_to_subpool(subpool, tx);
268 }
269 }
270 }
271 }
272
273 fn update_basefee<F>(&mut self, mut pending_basefee: u64, mut on_promoted: F) -> Ordering
278 where
279 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
280 {
281 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut pending_basefee);
282 match self.all_transactions.pending_fees.base_fee.cmp(&pending_basefee) {
283 Ordering::Equal => {
284 Ordering::Equal
286 }
287 Ordering::Greater => {
288 let removed =
290 self.pending_pool.update_base_fee(self.all_transactions.pending_fees.base_fee);
291 for tx in removed {
292 let to = {
293 let tx =
294 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
295 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
296 tx.subpool = tx.state.into();
297 tx.subpool
298 };
299 self.add_transaction_to_subpool(to, tx);
300 }
301
302 Ordering::Greater
303 }
304 Ordering::Less => {
305 let current_base_fee = self.all_transactions.pending_fees.base_fee;
314 self.basefee_pool.enforce_basefee_with(current_base_fee, |tx| {
315 let subpool = {
317 let meta =
318 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
319 meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
320 meta.subpool = meta.state.into();
321 meta.subpool
322 };
323
324 if subpool == SubPool::Pending {
325 on_promoted(&tx);
326 }
327
328 trace!(target: "txpool", hash=%tx.transaction.hash(), pool=?subpool, "Adding transaction to a subpool");
329 match subpool {
330 SubPool::Queued => self.queued_pool.add_transaction(tx),
331 SubPool::Pending => {
332 self.pending_pool.add_transaction(tx, current_base_fee);
333 }
334 SubPool::Blob => {
335 self.blob_pool.add_transaction(tx);
336 }
337 SubPool::BaseFee => {
338 warn!(target: "txpool", "BaseFee transactions should become Pending after basefee decrease");
341 }
342 }
343 });
344
345 Ordering::Less
346 }
347 }
348 }
349
350 pub fn set_block_info(&mut self, info: BlockInfo) {
354 let basefee_ordering = self.update_basefee(info.pending_basefee, |_| {});
356 if let Some(blob_fee) = info.pending_blob_fee {
357 self.update_blob_fee(blob_fee, basefee_ordering, |_| {})
358 }
359 self.all_transactions.set_block_info(info);
361 }
362
363 pub(crate) fn best_transactions(&self) -> BestTransactions<T> {
366 self.pending_pool.best()
367 }
368
369 pub(crate) fn best_transactions_with_attributes(
376 &self,
377 best_transactions_attributes: BestTransactionsAttributes,
378 ) -> Box<dyn crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T::Transaction>>>>
379 {
380 match best_transactions_attributes.basefee.cmp(&self.all_transactions.pending_fees.base_fee)
383 {
384 Ordering::Equal => {
385 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
389 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
390 Ordering::Less => {
391 let unlocked =
393 self.blob_pool.satisfy_attributes(best_transactions_attributes);
394 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
395 unlocked,
396 best_transactions_attributes.basefee,
397 new_blob_fee,
398 ))
399 }
400 Ordering::Equal => Box::new(self.pending_pool.best()),
401 Ordering::Greater => {
402 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
404 best_transactions_attributes.basefee,
405 best_transactions_attributes.blob_fee.unwrap_or_default(),
406 ))
407 }
408 }
409 }
410 Ordering::Greater => {
411 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
413 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
414 Ordering::Less => {
415 let unlocked =
417 self.blob_pool.satisfy_attributes(best_transactions_attributes);
418 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
419 unlocked,
420 best_transactions_attributes.basefee,
421 new_blob_fee,
422 ))
423 }
424 Ordering::Equal | Ordering::Greater => {
425 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
427 best_transactions_attributes.basefee,
428 new_blob_fee,
429 ))
430 }
431 }
432 }
433 Ordering::Less => {
434 let mut unlocked = self
437 .basefee_pool
438 .satisfy_base_fee_transactions(best_transactions_attributes.basefee);
439
440 unlocked.extend(self.blob_pool.satisfy_attributes(best_transactions_attributes));
442
443 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
444 unlocked,
445 best_transactions_attributes.basefee,
446 best_transactions_attributes.blob_fee.unwrap_or_default(),
447 ))
448 }
449 }
450 }
451
452 pub(crate) fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
454 self.pending_pool.all().collect()
455 }
456 pub(crate) fn pending_transactions_iter(
458 &self,
459 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
460 self.pending_pool.all()
461 }
462
463 pub(crate) fn pending_transactions_count(&self) -> usize {
465 self.pending_pool.len()
466 }
467
468 pub(crate) fn pending_transactions_with_predicate(
470 &self,
471 mut predicate: impl FnMut(&ValidPoolTransaction<T::Transaction>) -> bool,
472 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
473 self.pending_transactions_iter().filter(|tx| predicate(tx)).collect()
474 }
475
476 pub(crate) fn pending_txs_by_sender(
478 &self,
479 sender: SenderId,
480 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
481 self.pending_transactions_iter().filter(|tx| tx.sender_id() == sender).collect()
482 }
483
484 pub(crate) fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
486 self.basefee_pool.all().chain(self.queued_pool.all()).collect()
487 }
488
489 pub(crate) fn queued_transactions_iter(
491 &self,
492 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
493 self.basefee_pool.all().chain(self.queued_pool.all())
494 }
495
496 pub(crate) fn queued_transactions_count(&self) -> usize {
498 self.basefee_pool.len() + self.queued_pool.len()
499 }
500
501 pub fn queued_and_pending_txs_by_sender(
503 &self,
504 sender: SenderId,
505 ) -> (SmallVec<[TransactionId; TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER]>, Vec<TransactionId>) {
506 (self.queued_pool.get_txs_by_sender(sender), self.pending_pool.get_txs_by_sender(sender))
507 }
508
509 pub(crate) fn queued_txs_by_sender(
511 &self,
512 sender: SenderId,
513 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
514 self.queued_transactions_iter().filter(|tx| tx.sender_id() == sender).collect()
515 }
516
517 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
519 self.all_transactions.contains(tx_hash)
520 }
521
522 #[cfg(test)]
524 pub(crate) fn subpool_contains(&self, subpool: SubPool, id: &TransactionId) -> bool {
525 match subpool {
526 SubPool::Queued => self.queued_pool.contains(id),
527 SubPool::Pending => self.pending_pool.contains(id),
528 SubPool::BaseFee => self.basefee_pool.contains(id),
529 SubPool::Blob => self.blob_pool.contains(id),
530 }
531 }
532
533 #[inline]
535 pub(crate) fn is_exceeded(&self) -> bool {
536 self.config.is_exceeded(self.size())
537 }
538
539 pub(crate) fn get(
541 &self,
542 tx_hash: &TxHash,
543 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
544 self.all_transactions.by_hash.get(tx_hash).cloned()
545 }
546
547 pub(crate) fn get_all(
549 &self,
550 txs: Vec<TxHash>,
551 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
552 txs.into_iter().filter_map(|tx| self.get(&tx))
553 }
554
555 pub(crate) fn get_transactions_by_sender(
557 &self,
558 sender: SenderId,
559 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
560 self.all_transactions.txs_iter(sender).map(|(_, tx)| Arc::clone(&tx.transaction)).collect()
561 }
562
563 const fn update_pending_fees_only(
566 &mut self,
567 mut new_base_fee: u64,
568 new_blob_fee: Option<u128>,
569 ) -> (u64, u128) {
570 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut new_base_fee);
571
572 let prev_blob_fee = if let Some(mut blob_fee) = new_blob_fee {
573 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut blob_fee);
574 blob_fee
575 } else {
576 self.all_transactions.pending_fees.blob_fee
577 };
578
579 (new_base_fee, prev_blob_fee)
580 }
581
582 fn apply_fee_updates(
589 &mut self,
590 prev_base_fee: u64,
591 prev_blob_fee: u128,
592 outcome: &mut UpdateOutcome<T::Transaction>,
593 ) {
594 let new_base_fee = self.all_transactions.pending_fees.base_fee;
595 let new_blob_fee = self.all_transactions.pending_fees.blob_fee;
596
597 if new_base_fee == prev_base_fee && new_blob_fee == prev_blob_fee {
598 return;
600 }
601
602 self.all_transactions.pending_fees.base_fee = prev_base_fee;
605 self.all_transactions.pending_fees.blob_fee = prev_blob_fee;
606
607 let base_fee_ordering = self.update_basefee(new_base_fee, |tx| {
608 outcome.promoted.push(tx.clone());
609 });
610
611 self.update_blob_fee(new_blob_fee, base_fee_ordering, |tx| {
612 outcome.promoted.push(tx.clone());
613 });
614 }
615
616 pub(crate) fn update_accounts(
618 &mut self,
619 changed_senders: FxHashMap<SenderId, SenderInfo>,
620 ) -> UpdateOutcome<T::Transaction> {
621 let updates = self.all_transactions.update(&changed_senders);
623
624 self.all_transactions.sender_info.extend(changed_senders);
626
627 let update = self.process_updates(updates);
629 self.update_size_metrics();
631 update
632 }
633
634 pub(crate) fn on_canonical_state_change(
639 &mut self,
640 block_info: BlockInfo,
641 mined_transactions: Vec<TxHash>,
642 changed_senders: FxHashMap<SenderId, SenderInfo>,
643 _update_kind: PoolUpdateKind,
644 ) -> OnNewCanonicalStateOutcome<T::Transaction> {
645 let block_hash = block_info.last_seen_block_hash;
647
648 let mut removed_txs_count = 0;
650 for tx_hash in &mined_transactions {
651 if self.prune_transaction_by_hash(tx_hash).is_some() {
652 removed_txs_count += 1;
653 }
654 }
655
656 self.metrics.removed_transactions.increment(removed_txs_count);
658
659 let (prev_base_fee, prev_blob_fee) =
664 self.update_pending_fees_only(block_info.pending_basefee, block_info.pending_blob_fee);
665
666 let mut outcome = self.update_accounts(changed_senders);
668
669 self.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
672
673 self.all_transactions.set_block_info(block_info);
675
676 self.update_transaction_type_metrics();
677 self.metrics.performed_state_updates.increment(1);
678
679 OnNewCanonicalStateOutcome {
680 block_hash,
681 mined: mined_transactions,
682 promoted: outcome.promoted,
683 discarded: outcome.discarded,
684 }
685 }
686
687 pub(crate) fn update_size_metrics(&self) {
689 let stats = self.size();
690 self.metrics.pending_pool_transactions.set(stats.pending as f64);
691 self.metrics.pending_pool_size_bytes.set(stats.pending_size as f64);
692 self.metrics.basefee_pool_transactions.set(stats.basefee as f64);
693 self.metrics.basefee_pool_size_bytes.set(stats.basefee_size as f64);
694 self.metrics.queued_pool_transactions.set(stats.queued as f64);
695 self.metrics.queued_pool_size_bytes.set(stats.queued_size as f64);
696 self.metrics.blob_pool_transactions.set(stats.blob as f64);
697 self.metrics.blob_pool_size_bytes.set(stats.blob_size as f64);
698 self.metrics.total_transactions.set(stats.total as f64);
699 }
700
701 pub(crate) fn update_transaction_type_metrics(&self) {
703 let mut legacy_count = 0;
704 let mut eip2930_count = 0;
705 let mut eip1559_count = 0;
706 let mut eip4844_count = 0;
707 let mut eip7702_count = 0;
708 let mut other_count = 0;
709
710 for tx in self.all_transactions.transactions_iter() {
711 match tx.transaction.ty() {
712 LEGACY_TX_TYPE_ID => legacy_count += 1,
713 EIP2930_TX_TYPE_ID => eip2930_count += 1,
714 EIP1559_TX_TYPE_ID => eip1559_count += 1,
715 EIP4844_TX_TYPE_ID => eip4844_count += 1,
716 EIP7702_TX_TYPE_ID => eip7702_count += 1,
717 _ => other_count += 1,
718 }
719 }
720
721 self.metrics.total_legacy_transactions.set(legacy_count as f64);
722 self.metrics.total_eip2930_transactions.set(eip2930_count as f64);
723 self.metrics.total_eip1559_transactions.set(eip1559_count as f64);
724 self.metrics.total_eip4844_transactions.set(eip4844_count as f64);
725 self.metrics.total_eip7702_transactions.set(eip7702_count as f64);
726 self.metrics.total_other_transactions.set(other_count as f64);
727 }
728
729 pub(crate) fn add_transaction(
730 &mut self,
731 tx: ValidPoolTransaction<T::Transaction>,
732 on_chain_balance: U256,
733 on_chain_nonce: u64,
734 on_chain_code_hash: Option<B256>,
735 ) -> PoolResult<AddedTransaction<T::Transaction>> {
736 if self.contains(tx.hash()) {
737 return Err(PoolError::new(*tx.hash(), PoolErrorKind::AlreadyImported))
738 }
739
740 self.validate_auth(&tx, on_chain_nonce, on_chain_code_hash)?;
741
742 self.all_transactions
744 .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 let mut removed = 0;
940 for PoolUpdate { id, current, destination } in updates {
941 match destination {
942 Destination::Discard => {
943 if let Some(tx) = self.prune_transaction_by_id(&id) {
945 outcome.discarded.push(tx);
946 }
947 removed += 1;
948 }
949 Destination::Pool(move_to) => {
950 debug_assert_ne!(&move_to, ¤t, "destination must be different");
951 let moved = self.move_transaction(current, move_to, &id);
952 if matches!(move_to, SubPool::Pending) &&
953 let Some(tx) = moved
954 {
955 trace!(target: "txpool", hash=%tx.transaction.hash(), "Promoted transaction to pending");
956 outcome.promoted.push(tx);
957 }
958 }
959 }
960 }
961
962 if removed > 0 {
963 self.metrics.removed_transactions.increment(removed);
964 }
965
966 outcome
967 }
968
969 fn move_transaction(
974 &mut self,
975 from: SubPool,
976 to: SubPool,
977 id: &TransactionId,
978 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
979 let tx = self.remove_from_subpool(from, id)?;
980 self.add_transaction_to_subpool(to, tx.clone());
981 Some(tx)
982 }
983
984 pub(crate) fn remove_transactions(
989 &mut self,
990 hashes: Vec<TxHash>,
991 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
992 let txs =
993 hashes.into_iter().filter_map(|hash| self.remove_transaction_by_hash(&hash)).collect();
994 self.update_size_metrics();
995 txs
996 }
997
998 pub(crate) fn remove_transactions_and_descendants(
1000 &mut self,
1001 hashes: Vec<TxHash>,
1002 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1003 let mut removed = Vec::new();
1004 for hash in hashes {
1005 if let Some(tx) = self.remove_transaction_by_hash(&hash) {
1006 removed.push(tx.clone());
1007 self.remove_descendants(tx.id(), &mut removed);
1008 }
1009 }
1010 self.update_size_metrics();
1011 removed
1012 }
1013
1014 pub(crate) fn remove_transactions_by_sender(
1016 &mut self,
1017 sender_id: SenderId,
1018 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1019 let mut removed = Vec::new();
1020 let txs = self.get_transactions_by_sender(sender_id);
1021 for tx in txs {
1022 if let Some(tx) = self.remove_transaction(tx.id()) {
1023 removed.push(tx);
1024 }
1025 }
1026 self.update_size_metrics();
1027 removed
1028 }
1029
1030 fn remove_transaction(
1034 &mut self,
1035 id: &TransactionId,
1036 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1037 let (tx, pool) = self.all_transactions.remove_transaction(id)?;
1038 self.remove_from_subpool(pool, tx.id())
1039 }
1040
1041 fn remove_transaction_by_hash(
1047 &mut self,
1048 tx_hash: &B256,
1049 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1050 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1051
1052 let updates = self.all_transactions.park_descendant_transactions(tx.id());
1054 self.process_updates(updates);
1055 self.remove_from_subpool(pool, tx.id())
1056 }
1057
1058 fn prune_transaction_by_hash(
1065 &mut self,
1066 tx_hash: &B256,
1067 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1068 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1069 self.remove_from_subpool(pool, tx.id())
1070 }
1071 fn prune_transaction_by_id(
1076 &mut self,
1077 tx_id: &TransactionId,
1078 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1079 let (tx, pool) = self.all_transactions.remove_transaction_by_id(tx_id)?;
1080 self.remove_from_subpool(pool, tx.id())
1081 }
1082
1083 fn remove_from_subpool(
1087 &mut self,
1088 pool: SubPool,
1089 tx: &TransactionId,
1090 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1091 let tx = match pool {
1092 SubPool::Queued => self.queued_pool.remove_transaction(tx),
1093 SubPool::Pending => self.pending_pool.remove_transaction(tx),
1094 SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
1095 SubPool::Blob => self.blob_pool.remove_transaction(tx),
1096 };
1097
1098 if let Some(ref tx) = tx {
1099 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Removed transaction from a subpool");
1103 }
1104
1105 tx
1106 }
1107
1108 fn remove_descendants(
1112 &mut self,
1113 tx: &TransactionId,
1114 removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
1115 ) {
1116 let mut id = *tx;
1117
1118 loop {
1120 let descendant =
1121 self.all_transactions.descendant_txs_exclusive(&id).map(|(id, _)| *id).next();
1122 if let Some(descendant) = descendant {
1123 if let Some(tx) = self.remove_transaction(&descendant) {
1124 removed.push(tx)
1125 }
1126 id = descendant;
1127 } else {
1128 return
1129 }
1130 }
1131 }
1132
1133 fn add_transaction_to_subpool(
1135 &mut self,
1136 pool: SubPool,
1137 tx: Arc<ValidPoolTransaction<T::Transaction>>,
1138 ) {
1139 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Adding transaction to a subpool");
1143 match pool {
1144 SubPool::Queued => self.queued_pool.add_transaction(tx),
1145 SubPool::Pending => {
1146 self.pending_pool.add_transaction(tx, self.all_transactions.pending_fees.base_fee);
1147 }
1148 SubPool::BaseFee => {
1149 self.basefee_pool.add_transaction(tx);
1150 }
1151 SubPool::Blob => {
1152 self.blob_pool.add_transaction(tx);
1153 }
1154 }
1155 }
1156
1157 fn add_new_transaction(
1160 &mut self,
1161 transaction: Arc<ValidPoolTransaction<T::Transaction>>,
1162 replaced: Option<(Arc<ValidPoolTransaction<T::Transaction>>, SubPool)>,
1163 pool: SubPool,
1164 ) {
1165 if let Some((replaced, replaced_pool)) = replaced {
1166 self.remove_from_subpool(replaced_pool, replaced.id());
1168 }
1169
1170 self.add_transaction_to_subpool(pool, transaction)
1171 }
1172
1173 pub(crate) fn discard_worst(&mut self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1180 let mut removed = Vec::new();
1181
1182 macro_rules! discard_worst {
1184 ($this:ident, $removed:ident, [$($limit:ident => ($pool:ident, $metric:ident)),* $(,)*]) => {
1185 $ (
1186 while $this.$pool.exceeds(&$this.config.$limit)
1187 {
1188 trace!(
1189 target: "txpool",
1190 "discarding transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1191 stringify!($pool),
1192 $this.config.$limit,
1193 $this.$pool.size(),
1194 $this.$pool.len(),
1195 );
1196
1197 let removed_from_subpool = $this.$pool.truncate_pool($this.config.$limit.clone());
1199
1200 trace!(
1201 target: "txpool",
1202 "removed {} transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1203 removed_from_subpool.len(),
1204 stringify!($pool),
1205 $this.config.$limit,
1206 $this.$pool.size(),
1207 $this.$pool.len()
1208 );
1209 $this.metrics.$metric.increment(removed_from_subpool.len() as u64);
1210
1211 for tx in removed_from_subpool {
1213 $this.all_transactions.remove_transaction(tx.id());
1214
1215 let id = *tx.id();
1216
1217 removed.push(tx);
1219
1220 $this.remove_descendants(&id, &mut $removed);
1222 }
1223 }
1224
1225 )*
1226 };
1227 }
1228
1229 discard_worst!(
1230 self, removed, [
1231 pending_limit => (pending_pool, pending_transactions_evicted),
1232 basefee_limit => (basefee_pool, basefee_transactions_evicted),
1233 blob_limit => (blob_pool, blob_transactions_evicted),
1234 queued_limit => (queued_pool, queued_transactions_evicted),
1235 ]
1236 );
1237
1238 removed
1239 }
1240
1241 pub(crate) fn len(&self) -> usize {
1243 self.all_transactions.len()
1244 }
1245
1246 pub(crate) fn is_empty(&self) -> bool {
1248 self.all_transactions.is_empty()
1249 }
1250
1251 #[cfg(any(test, feature = "test-utils"))]
1259 pub fn assert_invariants(&self) {
1260 let size = self.size();
1261 let actual = size.basefee + size.pending + size.queued + size.blob;
1262 assert_eq!(
1263 size.total, actual,
1264 "total size must be equal to the sum of all sub-pools, basefee:{}, pending:{}, queued:{}, blob:{}",
1265 size.basefee, size.pending, size.queued, size.blob
1266 );
1267 self.all_transactions.assert_invariants();
1268 self.pending_pool.assert_invariants();
1269 self.basefee_pool.assert_invariants();
1270 self.queued_pool.assert_invariants();
1271 self.blob_pool.assert_invariants();
1272 }
1273}
1274
1275#[cfg(any(test, feature = "test-utils"))]
1276impl TxPool<crate::test_utils::MockOrdering> {
1277 pub fn mock() -> Self {
1279 Self::new(crate::test_utils::MockOrdering::default(), PoolConfig::default())
1280 }
1281}
1282
1283#[cfg(test)]
1284impl<T: TransactionOrdering> Drop for TxPool<T> {
1285 fn drop(&mut self) {
1286 self.assert_invariants();
1287 }
1288}
1289
1290impl<T: TransactionOrdering> TxPool<T> {
1291 pub const fn pending(&self) -> &PendingPool<T> {
1293 &self.pending_pool
1294 }
1295
1296 pub const fn base_fee(&self) -> &ParkedPool<BasefeeOrd<T::Transaction>> {
1298 &self.basefee_pool
1299 }
1300
1301 pub const fn queued(&self) -> &ParkedPool<QueuedOrd<T::Transaction>> {
1303 &self.queued_pool
1304 }
1305}
1306
1307impl<T: TransactionOrdering> fmt::Debug for TxPool<T> {
1308 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1309 f.debug_struct("TxPool").field("config", &self.config).finish_non_exhaustive()
1310 }
1311}
1312
1313pub(crate) struct AllTransactions<T: PoolTransaction> {
1318 minimal_protocol_basefee: u64,
1322 block_gas_limit: u64,
1324 max_account_slots: usize,
1326 by_hash: HashMap<TxHash, Arc<ValidPoolTransaction<T>>>,
1328 txs: BTreeMap<TransactionId, PoolInternalTransaction<T>>,
1330 sender_info: FxHashMap<SenderId, SenderInfo>,
1332 tx_counter: FxHashMap<SenderId, usize>,
1334 last_seen_block_number: u64,
1336 last_seen_block_hash: B256,
1338 pending_fees: PendingFees,
1340 price_bumps: PriceBumpConfig,
1342 local_transactions_config: LocalTransactionConfig,
1344 auths: FxHashMap<SenderId, HashSet<TxHash>>,
1346 metrics: AllTransactionsMetrics,
1348}
1349
1350impl<T: PoolTransaction> AllTransactions<T> {
1351 fn new(config: &PoolConfig) -> Self {
1353 Self {
1354 max_account_slots: config.max_account_slots,
1355 price_bumps: config.price_bumps,
1356 local_transactions_config: config.local_transactions_config.clone(),
1357 minimal_protocol_basefee: config.minimal_protocol_basefee,
1358 block_gas_limit: config.gas_limit,
1359 ..Default::default()
1360 }
1361 }
1362
1363 #[expect(dead_code)]
1365 pub(crate) fn hashes_iter(&self) -> impl Iterator<Item = TxHash> + '_ {
1366 self.by_hash.keys().copied()
1367 }
1368
1369 pub(crate) fn transactions_iter(
1371 &self,
1372 ) -> impl Iterator<Item = &Arc<ValidPoolTransaction<T>>> + '_ {
1373 self.by_hash.values()
1374 }
1375
1376 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
1378 self.by_hash.contains_key(tx_hash)
1379 }
1380
1381 pub(crate) fn get(&self, id: &TransactionId) -> Option<&PoolInternalTransaction<T>> {
1383 self.txs.get(id)
1384 }
1385
1386 pub(crate) fn tx_inc(&mut self, sender: SenderId) {
1388 let count = self.tx_counter.entry(sender).or_default();
1389 *count += 1;
1390 self.metrics.all_transactions_by_all_senders.increment(1.0);
1391 }
1392
1393 pub(crate) fn tx_decr(&mut self, sender: SenderId) {
1395 if let hash_map::Entry::Occupied(mut entry) = self.tx_counter.entry(sender) {
1396 let count = entry.get_mut();
1397 if *count == 1 {
1398 entry.remove();
1399 self.sender_info.remove(&sender);
1400 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1401 return
1402 }
1403 *count -= 1;
1404 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1405 }
1406 }
1407
1408 fn set_block_info(&mut self, block_info: BlockInfo) {
1410 let BlockInfo {
1411 block_gas_limit,
1412 last_seen_block_hash,
1413 last_seen_block_number,
1414 pending_basefee,
1415 pending_blob_fee,
1416 } = block_info;
1417 self.last_seen_block_number = last_seen_block_number;
1418 self.last_seen_block_hash = last_seen_block_hash;
1419
1420 self.pending_fees.base_fee = pending_basefee;
1421 self.metrics.base_fee.set(pending_basefee as f64);
1422
1423 self.block_gas_limit = block_gas_limit;
1424
1425 if let Some(pending_blob_fee) = pending_blob_fee {
1426 self.pending_fees.blob_fee = pending_blob_fee;
1427 self.metrics.blob_base_fee.set(pending_blob_fee as f64);
1428 }
1429 }
1430
1431 pub(crate) fn update_size_metrics(&self) {
1433 self.metrics.all_transactions_by_hash.set(self.by_hash.len() as f64);
1434 self.metrics.all_transactions_by_id.set(self.txs.len() as f64);
1435 }
1436
1437 pub(crate) fn update(
1454 &mut self,
1455 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1456 ) -> Vec<PoolUpdate> {
1457 let mut updates = Vec::with_capacity(64);
1459
1460 let mut iter = self.txs.iter_mut().peekable();
1461
1462 'transactions: while let Some((id, tx)) = iter.next() {
1472 macro_rules! next_sender {
1473 ($iter:ident) => {
1474 'this: while let Some((peek, _)) = iter.peek() {
1475 if peek.sender != id.sender {
1476 break 'this
1477 }
1478 iter.next();
1479 }
1480 };
1481 }
1482
1483 let changed_balance = if let Some(info) = changed_accounts.get(&id.sender) {
1486 if id.nonce < info.state_nonce {
1488 updates.push(PoolUpdate {
1489 id: *tx.transaction.id(),
1490 current: tx.subpool,
1491 destination: Destination::Discard,
1492 });
1493 continue 'transactions
1494 }
1495
1496 let ancestor = TransactionId::ancestor(id.nonce, info.state_nonce, id.sender);
1497 if ancestor.is_none() {
1499 tx.state.insert(TxState::NO_NONCE_GAPS);
1500 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1501 tx.cumulative_cost = U256::ZERO;
1502 if tx.transaction.cost() > &info.balance {
1503 tx.state.remove(TxState::ENOUGH_BALANCE);
1505 } else {
1506 tx.state.insert(TxState::ENOUGH_BALANCE);
1507 }
1508 }
1509
1510 Some(&info.balance)
1511 } else {
1512 None
1513 };
1514
1515 if tx.state.has_nonce_gap() {
1517 next_sender!(iter);
1518 continue 'transactions
1519 }
1520
1521 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1523
1524 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1526 Self::record_subpool_update(&mut updates, tx);
1528
1529 let mut has_parked_ancestor = !tx.state.is_pending();
1531
1532 let mut cumulative_cost = tx.next_cumulative_cost();
1533
1534 let mut next_nonce_in_line = tx.transaction.nonce().saturating_add(1);
1536
1537 while let Some((peek, tx)) = iter.peek_mut() {
1539 if peek.sender != id.sender {
1540 continue 'transactions
1542 }
1543
1544 if tx.transaction.nonce() == next_nonce_in_line {
1545 tx.state.insert(TxState::NO_NONCE_GAPS);
1547 } else {
1548 next_sender!(iter);
1550 continue 'transactions
1551 }
1552
1553 next_nonce_in_line = next_nonce_in_line.saturating_add(1);
1555
1556 tx.cumulative_cost = cumulative_cost;
1558 cumulative_cost = tx.next_cumulative_cost();
1560
1561 if let Some(changed_balance) = changed_balance {
1563 if &cumulative_cost > changed_balance {
1564 tx.state.remove(TxState::ENOUGH_BALANCE);
1566 } else {
1567 tx.state.insert(TxState::ENOUGH_BALANCE);
1568 }
1569 }
1570
1571 if has_parked_ancestor {
1573 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1574 } else {
1575 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1576 }
1577 has_parked_ancestor = !tx.state.is_pending();
1578
1579 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1581 Self::record_subpool_update(&mut updates, tx);
1582
1583 iter.next();
1585 }
1586 }
1587
1588 updates
1589 }
1590
1591 fn record_subpool_update(updates: &mut Vec<PoolUpdate>, tx: &mut PoolInternalTransaction<T>) {
1596 let current_pool = tx.subpool;
1597 tx.subpool = tx.state.into();
1598 if current_pool != tx.subpool {
1599 updates.push(PoolUpdate {
1600 id: *tx.transaction.id(),
1601 current: current_pool,
1602 destination: tx.subpool.into(),
1603 })
1604 }
1605 }
1606
1607 fn update_tx_base_fee(pending_block_base_fee: u64, tx: &mut PoolInternalTransaction<T>) {
1609 match tx.transaction.max_fee_per_gas().cmp(&(pending_block_base_fee as u128)) {
1611 Ordering::Greater | Ordering::Equal => {
1612 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1613 }
1614 Ordering::Less => {
1615 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
1616 }
1617 }
1618 }
1619
1620 pub(crate) fn txs_iter(
1623 &self,
1624 sender: SenderId,
1625 ) -> impl Iterator<Item = (&TransactionId, &PoolInternalTransaction<T>)> + '_ {
1626 self.txs
1627 .range((sender.start_bound(), Unbounded))
1628 .take_while(move |(other, _)| sender == other.sender)
1629 }
1630
1631 #[cfg(test)]
1634 #[expect(dead_code)]
1635 pub(crate) fn txs_iter_mut(
1636 &mut self,
1637 sender: SenderId,
1638 ) -> impl Iterator<Item = (&TransactionId, &mut PoolInternalTransaction<T>)> + '_ {
1639 self.txs
1640 .range_mut((sender.start_bound(), Unbounded))
1641 .take_while(move |(other, _)| sender == other.sender)
1642 }
1643
1644 pub(crate) fn descendant_txs_exclusive<'a, 'b: 'a>(
1648 &'a self,
1649 id: &'b TransactionId,
1650 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1651 self.txs.range((Excluded(id), Unbounded)).take_while(|(other, _)| id.sender == other.sender)
1652 }
1653
1654 pub(crate) fn descendant_txs_inclusive<'a, 'b: 'a>(
1659 &'a self,
1660 id: &'b TransactionId,
1661 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1662 self.txs.range(id..).take_while(|(other, _)| id.sender == other.sender)
1663 }
1664
1665 pub(crate) fn descendant_txs_mut<'a, 'b: 'a>(
1670 &'a mut self,
1671 id: &'b TransactionId,
1672 ) -> impl Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)> + 'a {
1673 self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender)
1674 }
1675
1676 pub(crate) fn remove_transaction_by_hash(
1678 &mut self,
1679 tx_hash: &B256,
1680 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1681 let tx = self.by_hash.remove(tx_hash)?;
1682 let internal = self.txs.remove(&tx.transaction_id)?;
1683 self.remove_auths(&internal);
1684 self.tx_decr(tx.sender_id());
1686 Some((tx, internal.subpool))
1687 }
1688
1689 pub(crate) fn remove_transaction_by_id(
1693 &mut self,
1694 tx_id: &TransactionId,
1695 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1696 let internal = self.txs.remove(tx_id)?;
1697 let tx = self.by_hash.remove(internal.transaction.hash())?;
1698 self.remove_auths(&internal);
1699 self.tx_decr(tx.sender_id());
1701 Some((tx, internal.subpool))
1702 }
1703
1704 pub(crate) fn park_descendant_transactions(
1706 &mut self,
1707 tx_id: &TransactionId,
1708 ) -> Vec<PoolUpdate> {
1709 let mut updates = Vec::new();
1710
1711 for (id, tx) in self.descendant_txs_mut(tx_id) {
1712 let current_pool = tx.subpool;
1713
1714 tx.state.remove(TxState::NO_NONCE_GAPS);
1715
1716 tx.subpool = tx.state.into();
1718
1719 if current_pool != tx.subpool {
1721 updates.push(PoolUpdate {
1722 id: *id,
1723 current: current_pool,
1724 destination: tx.subpool.into(),
1725 })
1726 }
1727 }
1728
1729 updates
1730 }
1731
1732 pub(crate) fn remove_transaction(
1738 &mut self,
1739 id: &TransactionId,
1740 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1741 let internal = self.txs.remove(id)?;
1742
1743 self.tx_decr(internal.transaction.sender_id());
1745
1746 let result =
1747 self.by_hash.remove(internal.transaction.hash()).map(|tx| (tx, internal.subpool));
1748
1749 self.remove_auths(&internal);
1750
1751 result
1752 }
1753
1754 fn remove_auths(&mut self, tx: &PoolInternalTransaction<T>) {
1758 let Some(auths) = &tx.transaction.authority_ids else { return };
1759
1760 let tx_hash = tx.transaction.hash();
1761 for auth in auths {
1762 if let Some(list) = self.auths.get_mut(auth) {
1763 list.remove(tx_hash);
1764 if list.is_empty() {
1765 self.auths.remove(auth);
1766 }
1767 }
1768 }
1769 }
1770
1771 #[inline]
1777 fn contains_conflicting_transaction(&self, tx: &ValidPoolTransaction<T>) -> bool {
1778 self.txs_iter(tx.transaction_id.sender)
1779 .next()
1780 .is_some_and(|(_, existing)| tx.tx_type_conflicts_with(&existing.transaction))
1781 }
1782
1783 fn ensure_valid(
1792 &self,
1793 transaction: ValidPoolTransaction<T>,
1794 on_chain_nonce: u64,
1795 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1796 if !self.local_transactions_config.is_local(transaction.origin, transaction.sender_ref()) {
1797 let current_txs =
1798 self.tx_counter.get(&transaction.sender_id()).copied().unwrap_or_default();
1799
1800 if current_txs >= self.max_account_slots && transaction.nonce() > on_chain_nonce {
1803 return Err(InsertErr::ExceededSenderTransactionsCapacity {
1804 transaction: Arc::new(transaction),
1805 })
1806 }
1807 }
1808 if transaction.gas_limit() > self.block_gas_limit {
1809 return Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas {
1810 block_gas_limit: self.block_gas_limit,
1811 tx_gas_limit: transaction.gas_limit(),
1812 transaction: Arc::new(transaction),
1813 })
1814 }
1815
1816 if self.contains_conflicting_transaction(&transaction) {
1817 return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) })
1819 }
1820
1821 Ok(transaction)
1822 }
1823
1824 fn ensure_valid_blob_transaction(
1830 &self,
1831 new_blob_tx: ValidPoolTransaction<T>,
1832 on_chain_balance: U256,
1833 ancestor: Option<TransactionId>,
1834 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1835 if let Some(ancestor) = ancestor {
1836 let Some(ancestor_tx) = self.txs.get(&ancestor) else {
1837 self.metrics.blob_transactions_nonce_gaps.increment(1);
1839 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1840 };
1841 if ancestor_tx.state.has_nonce_gap() {
1842 self.metrics.blob_transactions_nonce_gaps.increment(1);
1845 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1846 }
1847
1848 let mut cumulative_cost = ancestor_tx.next_cumulative_cost() + new_blob_tx.cost();
1850
1851 if cumulative_cost > on_chain_balance {
1853 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1855 }
1856
1857 let id = new_blob_tx.transaction_id;
1860 let mut descendants = self.descendant_txs_inclusive(&id).peekable();
1861 if let Some((maybe_replacement, _)) = descendants.peek() &&
1862 **maybe_replacement == new_blob_tx.transaction_id
1863 {
1864 descendants.next();
1866
1867 for (_, tx) in descendants {
1869 cumulative_cost += tx.transaction.cost();
1870 if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance {
1871 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1873 }
1874 }
1875 }
1876 } else if new_blob_tx.cost() > &on_chain_balance {
1877 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1879 }
1880
1881 Ok(new_blob_tx)
1882 }
1883
1884 pub(crate) fn insert_tx(
1916 &mut self,
1917 transaction: ValidPoolTransaction<T>,
1918 on_chain_balance: U256,
1919 on_chain_nonce: u64,
1920 ) -> InsertResult<T> {
1921 assert!(on_chain_nonce <= transaction.nonce(), "Invalid transaction");
1922
1923 let mut transaction = self.ensure_valid(transaction, on_chain_nonce)?;
1924
1925 let inserted_tx_id = *transaction.id();
1926 let mut state = TxState::default();
1927 let mut cumulative_cost = U256::ZERO;
1928 let mut updates = Vec::new();
1929
1930 state.insert(TxState::NOT_TOO_MUCH_GAS);
1932
1933 let ancestor = TransactionId::ancestor(
1936 transaction.transaction.nonce(),
1937 on_chain_nonce,
1938 inserted_tx_id.sender,
1939 );
1940
1941 if transaction.is_eip4844() {
1944 state.insert(TxState::BLOB_TRANSACTION);
1945
1946 transaction =
1947 self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
1948 let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();
1949 if blob_fee_cap >= self.pending_fees.blob_fee {
1950 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1951 }
1952 } else {
1953 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1955 }
1956
1957 let transaction = Arc::new(transaction);
1958
1959 if ancestor.is_none() {
1961 state.insert(TxState::NO_NONCE_GAPS);
1962 state.insert(TxState::NO_PARKED_ANCESTORS);
1963 }
1964
1965 let fee_cap = transaction.max_fee_per_gas();
1967
1968 if fee_cap < self.minimal_protocol_basefee as u128 {
1969 return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap })
1970 }
1971 if fee_cap >= self.pending_fees.base_fee as u128 {
1972 state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1973 }
1974
1975 let mut replaced_tx = None;
1977
1978 let pool_tx = PoolInternalTransaction {
1979 transaction: Arc::clone(&transaction),
1980 subpool: state.into(),
1981 state,
1982 cumulative_cost,
1983 };
1984
1985 match self.txs.entry(*transaction.id()) {
1987 Entry::Vacant(entry) => {
1988 self.by_hash.insert(*pool_tx.transaction.hash(), pool_tx.transaction.clone());
1990 entry.insert(pool_tx);
1991 }
1992 Entry::Occupied(mut entry) => {
1993 let existing_transaction = entry.get().transaction.as_ref();
1995 let maybe_replacement = transaction.as_ref();
1996
1997 if existing_transaction.is_underpriced(maybe_replacement, &self.price_bumps) {
1999 return Err(InsertErr::Underpriced {
2000 transaction: pool_tx.transaction,
2001 existing: *entry.get().transaction.hash(),
2002 })
2003 }
2004 let new_hash = *pool_tx.transaction.hash();
2005 let new_transaction = pool_tx.transaction.clone();
2006 let replaced = entry.insert(pool_tx);
2007 self.by_hash.remove(replaced.transaction.hash());
2008 self.by_hash.insert(new_hash, new_transaction);
2009
2010 self.remove_auths(&replaced);
2011
2012 replaced_tx = Some((replaced.transaction, replaced.subpool));
2014 }
2015 }
2016
2017 if let Some(auths) = &transaction.authority_ids {
2018 let tx_hash = transaction.hash();
2019 for auth in auths {
2020 self.auths.entry(*auth).or_default().insert(*tx_hash);
2021 }
2022 }
2023
2024 let on_chain_id = TransactionId::new(transaction.sender_id(), on_chain_nonce);
2026 {
2027 let mut next_nonce = on_chain_id.nonce;
2029
2030 let mut has_parked_ancestor = false;
2034
2035 for (id, tx) in self.descendant_txs_mut(&on_chain_id) {
2038 let current_pool = tx.subpool;
2039
2040 if next_nonce != id.nonce {
2042 break
2043 }
2044
2045 tx.state.insert(TxState::NO_NONCE_GAPS);
2047
2048 tx.cumulative_cost = cumulative_cost;
2050
2051 cumulative_cost = tx.next_cumulative_cost();
2053
2054 if cumulative_cost > on_chain_balance {
2055 tx.state.remove(TxState::ENOUGH_BALANCE);
2057 } else {
2058 tx.state.insert(TxState::ENOUGH_BALANCE);
2059 }
2060
2061 if has_parked_ancestor {
2063 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
2064 } else {
2065 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
2066 }
2067 has_parked_ancestor = !tx.state.is_pending();
2068
2069 tx.subpool = tx.state.into();
2071
2072 if inserted_tx_id.eq(id) {
2073 state = tx.state;
2075 } else {
2076 if current_pool != tx.subpool {
2078 updates.push(PoolUpdate {
2079 id: *id,
2080 current: current_pool,
2081 destination: tx.subpool.into(),
2082 })
2083 }
2084 }
2085
2086 next_nonce = id.next_nonce();
2088 }
2089 }
2090
2091 if replaced_tx.is_none() {
2093 self.tx_inc(inserted_tx_id.sender);
2094 }
2095
2096 self.update_size_metrics();
2097
2098 Ok(InsertOk { transaction, move_to: state.into(), state, replaced_tx, updates })
2099 }
2100
2101 pub(crate) fn len(&self) -> usize {
2103 self.txs.len()
2104 }
2105
2106 pub(crate) fn is_empty(&self) -> bool {
2108 self.txs.is_empty()
2109 }
2110
2111 #[cfg(any(test, feature = "test-utils"))]
2113 pub(crate) fn assert_invariants(&self) {
2114 assert_eq!(self.by_hash.len(), self.txs.len(), "by_hash.len() != txs.len()");
2115 assert!(self.auths.len() <= self.txs.len(), "auths.len() > txs.len()");
2116 }
2117}
2118
2119#[cfg(test)]
2120impl<T: PoolTransaction> AllTransactions<T> {
2121 pub(crate) fn tx_count(&self, sender: SenderId) -> usize {
2125 self.tx_counter.get(&sender).copied().unwrap_or_default()
2126 }
2127}
2128
2129impl<T: PoolTransaction> Default for AllTransactions<T> {
2130 fn default() -> Self {
2131 Self {
2132 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
2133 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
2134 block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
2135 by_hash: Default::default(),
2136 txs: Default::default(),
2137 sender_info: Default::default(),
2138 tx_counter: Default::default(),
2139 last_seen_block_number: Default::default(),
2140 last_seen_block_hash: Default::default(),
2141 pending_fees: Default::default(),
2142 price_bumps: Default::default(),
2143 local_transactions_config: Default::default(),
2144 auths: Default::default(),
2145 metrics: Default::default(),
2146 }
2147 }
2148}
2149
2150#[derive(Debug, Clone)]
2152pub(crate) struct PendingFees {
2153 pub(crate) base_fee: u64,
2155 pub(crate) blob_fee: u128,
2157}
2158
2159impl Default for PendingFees {
2160 fn default() -> Self {
2161 Self { base_fee: Default::default(), blob_fee: BLOB_TX_MIN_BLOB_GASPRICE }
2162 }
2163}
2164
2165pub(crate) type InsertResult<T> = Result<InsertOk<T>, InsertErr<T>>;
2167
2168#[derive(Debug)]
2170pub(crate) enum InsertErr<T: PoolTransaction> {
2171 Underpriced {
2173 transaction: Arc<ValidPoolTransaction<T>>,
2174 #[expect(dead_code)]
2175 existing: TxHash,
2176 },
2177 BlobTxHasNonceGap { transaction: Arc<ValidPoolTransaction<T>> },
2179 Overdraft { transaction: Arc<ValidPoolTransaction<T>> },
2182 FeeCapBelowMinimumProtocolFeeCap { transaction: Arc<ValidPoolTransaction<T>>, fee_cap: u128 },
2186 ExceededSenderTransactionsCapacity { transaction: Arc<ValidPoolTransaction<T>> },
2190 TxGasLimitMoreThanAvailableBlockGas {
2192 transaction: Arc<ValidPoolTransaction<T>>,
2193 block_gas_limit: u64,
2194 tx_gas_limit: u64,
2195 },
2196 TxTypeConflict { transaction: Arc<ValidPoolTransaction<T>> },
2198}
2199
2200#[derive(Debug)]
2202pub(crate) struct InsertOk<T: PoolTransaction> {
2203 transaction: Arc<ValidPoolTransaction<T>>,
2205 move_to: SubPool,
2207 state: TxState,
2209 replaced_tx: Option<(Arc<ValidPoolTransaction<T>>, SubPool)>,
2211 updates: Vec<PoolUpdate>,
2213}
2214
2215#[derive(Debug)]
2218pub(crate) struct PoolInternalTransaction<T: PoolTransaction> {
2219 pub(crate) transaction: Arc<ValidPoolTransaction<T>>,
2221 pub(crate) subpool: SubPool,
2223 pub(crate) state: TxState,
2226 pub(crate) cumulative_cost: U256,
2231}
2232
2233impl<T: PoolTransaction> PoolInternalTransaction<T> {
2236 fn next_cumulative_cost(&self) -> U256 {
2237 self.cumulative_cost + self.transaction.cost()
2238 }
2239}
2240
2241#[derive(Debug, Clone, Default)]
2243pub(crate) struct SenderInfo {
2244 pub(crate) state_nonce: u64,
2246 pub(crate) balance: U256,
2248}
2249
2250impl SenderInfo {
2253 const fn update(&mut self, state_nonce: u64, balance: U256) {
2255 *self = Self { state_nonce, balance };
2256 }
2257}
2258
2259#[cfg(test)]
2260mod tests {
2261 use super::*;
2262 use crate::{
2263 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
2264 traits::TransactionOrigin,
2265 SubPoolLimit,
2266 };
2267 use alloy_consensus::{Transaction, TxType};
2268 use alloy_primitives::address;
2269
2270 #[test]
2271 fn test_insert_blob() {
2272 let on_chain_balance = U256::MAX;
2273 let on_chain_nonce = 0;
2274 let mut f = MockTransactionFactory::default();
2275 let mut pool = AllTransactions::default();
2276 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2277 let valid_tx = f.validated(tx);
2278 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2279 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2280 assert!(updates.is_empty());
2281 assert!(replaced_tx.is_none());
2282 assert!(state.contains(TxState::NO_NONCE_GAPS));
2283 assert!(state.contains(TxState::ENOUGH_BALANCE));
2284 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2285 assert_eq!(move_to, SubPool::Pending);
2286
2287 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2288 assert_eq!(inserted.subpool, SubPool::Pending);
2289 }
2290
2291 #[test]
2292 fn test_insert_blob_not_enough_blob_fee() {
2293 let on_chain_balance = U256::MAX;
2294 let on_chain_nonce = 0;
2295 let mut f = MockTransactionFactory::default();
2296 let mut pool = AllTransactions {
2297 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2298 ..Default::default()
2299 };
2300 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2301 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2302 let valid_tx = f.validated(tx);
2303 let InsertOk { state, .. } =
2304 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2305 assert!(state.contains(TxState::NO_NONCE_GAPS));
2306 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2307
2308 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2309 }
2310
2311 #[test]
2312 fn test_valid_tx_with_decreasing_blob_fee() {
2313 let on_chain_balance = U256::MAX;
2314 let on_chain_nonce = 0;
2315 let mut f = MockTransactionFactory::default();
2316 let mut pool = AllTransactions {
2317 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2318 ..Default::default()
2319 };
2320 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2321
2322 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2323 let valid_tx = f.validated(tx.clone());
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 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2330 pool.remove_transaction(&valid_tx.transaction_id);
2331
2332 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
2333 let InsertOk { state, .. } =
2334 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2335 assert!(state.contains(TxState::NO_NONCE_GAPS));
2336 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2337 }
2338
2339 #[test]
2340 fn test_demote_valid_tx_with_increasing_blob_fee() {
2341 let on_chain_balance = U256::MAX;
2342 let on_chain_nonce = 0;
2343 let mut f = MockTransactionFactory::default();
2344 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2345 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2346
2347 let mut block_info = pool.block_info();
2349 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2350 pool.set_block_info(block_info);
2351
2352 let validated = f.validated(tx.clone());
2353 let id = *validated.id();
2354 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2355
2356 assert!(pool.blob_pool.is_empty());
2358 assert_eq!(pool.pending_pool.len(), 1);
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::Pending);
2364
2365 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2367 pool.set_block_info(block_info);
2368
2369 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2371 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2372 assert_eq!(internal_tx.subpool, SubPool::Blob);
2373
2374 assert_eq!(pool.blob_pool.len(), 1);
2376 assert!(pool.pending_pool.is_empty());
2377 }
2378
2379 #[test]
2380 fn test_promote_valid_tx_with_decreasing_blob_fee() {
2381 let on_chain_balance = U256::MAX;
2382 let on_chain_nonce = 0;
2383 let mut f = MockTransactionFactory::default();
2384 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2385 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2386
2387 let mut block_info = pool.block_info();
2389 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2390 pool.set_block_info(block_info);
2391
2392 let validated = f.validated(tx.clone());
2393 let id = *validated.id();
2394 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2395
2396 assert!(pool.pending_pool.is_empty());
2398 assert_eq!(pool.blob_pool.len(), 1);
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::Blob);
2404
2405 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2407 pool.set_block_info(block_info);
2408
2409 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2411 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2412 assert_eq!(internal_tx.subpool, SubPool::Pending);
2413
2414 assert_eq!(pool.pending_pool.len(), 1);
2416 assert!(pool.blob_pool.is_empty());
2417 }
2418
2419 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
2421 struct PromotionTest {
2422 basefee: u64,
2424 blobfee: u128,
2426 subpool: SubPool,
2428 basefee_update: u64,
2430 blobfee_update: u128,
2432 new_subpool: SubPool,
2434 }
2435
2436 impl PromotionTest {
2437 const fn opposite(&self) -> Self {
2439 Self {
2440 basefee: self.basefee_update,
2441 blobfee: self.blobfee_update,
2442 subpool: self.new_subpool,
2443 blobfee_update: self.blobfee,
2444 basefee_update: self.basefee,
2445 new_subpool: self.subpool,
2446 }
2447 }
2448
2449 fn assert_subpool_lengths<T: TransactionOrdering>(
2450 &self,
2451 pool: &TxPool<T>,
2452 failure_message: String,
2453 check_subpool: SubPool,
2454 ) {
2455 match check_subpool {
2456 SubPool::Blob => {
2457 assert_eq!(pool.blob_pool.len(), 1, "{failure_message}");
2458 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2459 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2460 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2461 }
2462 SubPool::Pending => {
2463 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2464 assert_eq!(pool.pending_pool.len(), 1, "{failure_message}");
2465 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2466 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2467 }
2468 SubPool::BaseFee => {
2469 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2470 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2471 assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}");
2472 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2473 }
2474 SubPool::Queued => {
2475 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2476 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2477 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2478 assert_eq!(pool.queued_pool.len(), 1, "{failure_message}");
2479 }
2480 }
2481 }
2482
2483 fn assert_single_tx_starting_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2487 self.assert_subpool_lengths(
2488 pool,
2489 format!("pool length check failed at start of test: {self:?}"),
2490 self.subpool,
2491 );
2492 }
2493
2494 fn assert_single_tx_ending_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2498 self.assert_subpool_lengths(
2499 pool,
2500 format!("pool length check failed at end of test: {self:?}"),
2501 self.new_subpool,
2502 );
2503 }
2504 }
2505
2506 #[test]
2507 fn test_promote_blob_tx_with_both_pending_fee_updates() {
2508 let on_chain_balance = U256::MAX;
2511 let on_chain_nonce = 0;
2512 let mut f = MockTransactionFactory::default();
2513 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2514
2515 let max_fee_per_blob_gas = tx.max_fee_per_blob_gas().unwrap();
2516 let max_fee_per_gas = tx.max_fee_per_gas() as u64;
2517
2518 let mut expected_promotions = vec![
2520 PromotionTest {
2521 blobfee: max_fee_per_blob_gas + 1,
2522 basefee: max_fee_per_gas + 1,
2523 subpool: SubPool::Blob,
2524 blobfee_update: max_fee_per_blob_gas + 1,
2525 basefee_update: max_fee_per_gas + 1,
2526 new_subpool: SubPool::Blob,
2527 },
2528 PromotionTest {
2529 blobfee: max_fee_per_blob_gas + 1,
2530 basefee: max_fee_per_gas + 1,
2531 subpool: SubPool::Blob,
2532 blobfee_update: max_fee_per_blob_gas,
2533 basefee_update: max_fee_per_gas + 1,
2534 new_subpool: SubPool::Blob,
2535 },
2536 PromotionTest {
2537 blobfee: max_fee_per_blob_gas + 1,
2538 basefee: max_fee_per_gas + 1,
2539 subpool: SubPool::Blob,
2540 blobfee_update: max_fee_per_blob_gas + 1,
2541 basefee_update: max_fee_per_gas,
2542 new_subpool: SubPool::Blob,
2543 },
2544 PromotionTest {
2545 blobfee: max_fee_per_blob_gas + 1,
2546 basefee: max_fee_per_gas + 1,
2547 subpool: SubPool::Blob,
2548 blobfee_update: max_fee_per_blob_gas,
2549 basefee_update: max_fee_per_gas,
2550 new_subpool: SubPool::Pending,
2551 },
2552 PromotionTest {
2553 blobfee: max_fee_per_blob_gas,
2554 basefee: max_fee_per_gas + 1,
2555 subpool: SubPool::Blob,
2556 blobfee_update: max_fee_per_blob_gas,
2557 basefee_update: max_fee_per_gas,
2558 new_subpool: SubPool::Pending,
2559 },
2560 PromotionTest {
2561 blobfee: max_fee_per_blob_gas + 1,
2562 basefee: max_fee_per_gas,
2563 subpool: SubPool::Blob,
2564 blobfee_update: max_fee_per_blob_gas,
2565 basefee_update: max_fee_per_gas,
2566 new_subpool: SubPool::Pending,
2567 },
2568 PromotionTest {
2569 blobfee: max_fee_per_blob_gas,
2570 basefee: max_fee_per_gas,
2571 subpool: SubPool::Pending,
2572 blobfee_update: max_fee_per_blob_gas,
2573 basefee_update: max_fee_per_gas,
2574 new_subpool: SubPool::Pending,
2575 },
2576 ];
2577
2578 let reversed = expected_promotions.iter().map(|test| test.opposite()).collect::<Vec<_>>();
2580 expected_promotions.extend(reversed);
2581
2582 let expected_promotions = expected_promotions.into_iter().collect::<HashSet<_>>();
2584
2585 for promotion_test in &expected_promotions {
2586 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2587
2588 let mut block_info = pool.block_info();
2590
2591 block_info.pending_blob_fee = Some(promotion_test.blobfee);
2592 block_info.pending_basefee = promotion_test.basefee;
2593 pool.set_block_info(block_info);
2594
2595 let validated = f.validated(tx.clone());
2596 let id = *validated.id();
2597 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2598
2599 promotion_test.assert_single_tx_starting_subpool(&pool);
2601
2602 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2604 assert_eq!(
2605 internal_tx.subpool, promotion_test.subpool,
2606 "Subpools do not match at start of test: {promotion_test:?}"
2607 );
2608
2609 block_info.pending_basefee = promotion_test.basefee_update;
2611 block_info.pending_blob_fee = Some(promotion_test.blobfee_update);
2612 pool.set_block_info(block_info);
2613
2614 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2616 assert_eq!(
2617 internal_tx.subpool, promotion_test.new_subpool,
2618 "Subpools do not match at end of test: {promotion_test:?}"
2619 );
2620
2621 promotion_test.assert_single_tx_ending_subpool(&pool);
2623 }
2624 }
2625
2626 #[test]
2627 fn test_insert_pending() {
2628 let on_chain_balance = U256::MAX;
2629 let on_chain_nonce = 0;
2630 let mut f = MockTransactionFactory::default();
2631 let mut pool = AllTransactions::default();
2632 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2633 let valid_tx = f.validated(tx);
2634 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2635 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2636 assert!(updates.is_empty());
2637 assert!(replaced_tx.is_none());
2638 assert!(state.contains(TxState::NO_NONCE_GAPS));
2639 assert!(state.contains(TxState::ENOUGH_BALANCE));
2640 assert_eq!(move_to, SubPool::Pending);
2641
2642 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2643 assert_eq!(inserted.subpool, SubPool::Pending);
2644 }
2645
2646 #[test]
2647 fn test_simple_insert() {
2648 let on_chain_balance = U256::ZERO;
2649 let on_chain_nonce = 0;
2650 let mut f = MockTransactionFactory::default();
2651 let mut pool = AllTransactions::default();
2652 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2653 tx.set_priority_fee(100);
2654 tx.set_max_fee(100);
2655 let valid_tx = f.validated(tx.clone());
2656 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2657 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2658 assert!(updates.is_empty());
2659 assert!(replaced_tx.is_none());
2660 assert!(state.contains(TxState::NO_NONCE_GAPS));
2661 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2662 assert_eq!(move_to, SubPool::Queued);
2663
2664 assert_eq!(pool.len(), 1);
2665 assert!(pool.contains(valid_tx.hash()));
2666 let expected_state = TxState::ENOUGH_FEE_CAP_BLOCK | TxState::NO_NONCE_GAPS;
2667 let inserted = pool.get(valid_tx.id()).unwrap();
2668 assert!(inserted.state.intersects(expected_state));
2669
2670 let res = pool.insert_tx(valid_tx, on_chain_balance, on_chain_nonce);
2672 res.unwrap_err();
2673 assert_eq!(pool.len(), 1);
2674
2675 let valid_tx = f.validated(tx.next());
2676 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2677 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2678
2679 assert!(updates.is_empty());
2680 assert!(replaced_tx.is_none());
2681 assert!(state.contains(TxState::NO_NONCE_GAPS));
2682 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2683 assert_eq!(move_to, SubPool::Queued);
2684
2685 assert!(pool.contains(valid_tx.hash()));
2686 assert_eq!(pool.len(), 2);
2687 let inserted = pool.get(valid_tx.id()).unwrap();
2688 assert!(inserted.state.intersects(expected_state));
2689 }
2690
2691 #[test]
2692 fn test_on_canonical_state_change_no_double_processing() {
2695 let mut tx_factory = MockTransactionFactory::default();
2696 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2697
2698 let tx = MockTransaction::eip1559().with_gas_price(50).with_gas_limit(30_000);
2700 let sender = tx.sender();
2701
2702 let mut block_info = pool.block_info();
2704 block_info.pending_basefee = 100;
2705 pool.set_block_info(block_info);
2706
2707 let validated = tx_factory.validated(tx);
2708 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2709
2710 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2712
2713 assert_eq!(pool.basefee_pool.len(), 1);
2714 assert_eq!(pool.pending_pool.len(), 0);
2715
2716 block_info.pending_basefee = 40;
2720
2721 let mut changed_senders = FxHashMap::default();
2722 changed_senders.insert(
2723 sender_id,
2724 SenderInfo {
2725 state_nonce: 0,
2726 balance: U256::from(20_000_000), },
2728 );
2729
2730 let outcome = pool.on_canonical_state_change(
2731 block_info,
2732 vec![], changed_senders,
2734 PoolUpdateKind::Commit,
2735 );
2736
2737 assert_eq!(pool.pending_pool.len(), 1, "Transaction should be in pending pool");
2739 assert_eq!(pool.basefee_pool.len(), 0, "Transaction should not be in basefee pool");
2740 assert_eq!(outcome.promoted.len(), 1, "Should report exactly one promotion");
2741 }
2742
2743 #[test]
2744 fn test_canonical_state_change_with_basefee_update_regression() {
2747 let mut tx_factory = MockTransactionFactory::default();
2748 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2749
2750 let sender_balance = U256::from(100_000_000);
2752
2753 let tx1 =
2755 MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000).with_nonce(0);
2756 let sender1 = tx1.sender();
2757
2758 let tx2 =
2760 MockTransaction::eip1559().with_gas_price(55).with_gas_limit(21_000).with_nonce(0);
2761 let sender2 = tx2.sender();
2762
2763 let tx3 =
2765 MockTransaction::eip1559().with_gas_price(45).with_gas_limit(21_000).with_nonce(0);
2766 let sender3 = tx3.sender();
2767
2768 let mut block_info = pool.block_info();
2770 block_info.pending_basefee = 70;
2771 pool.set_block_info(block_info);
2772
2773 let validated1 = tx_factory.validated(tx1);
2775 let validated2 = tx_factory.validated(tx2);
2776 let validated3 = tx_factory.validated(tx3);
2777
2778 pool.add_transaction(validated1, sender_balance, 0, None).unwrap();
2779 pool.add_transaction(validated2, sender_balance, 0, None).unwrap();
2780 pool.add_transaction(validated3, sender_balance, 0, None).unwrap();
2781
2782 let sender1_id = tx_factory.ids.sender_id(&sender1).unwrap();
2783 let sender2_id = tx_factory.ids.sender_id(&sender2).unwrap();
2784 let sender3_id = tx_factory.ids.sender_id(&sender3).unwrap();
2785
2786 assert_eq!(pool.basefee_pool.len(), 3, "All txs should be in basefee pool");
2788 assert_eq!(pool.pending_pool.len(), 0, "No txs should be in pending pool");
2789
2790 block_info.pending_basefee = 50;
2793
2794 let mut changed_senders = FxHashMap::default();
2796 changed_senders.insert(
2797 sender1_id,
2798 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2799 );
2800 changed_senders.insert(
2801 sender2_id,
2802 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2803 );
2804 changed_senders.insert(
2805 sender3_id,
2806 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2807 );
2808
2809 let outcome = pool.on_canonical_state_change(
2810 block_info,
2811 vec![],
2812 changed_senders,
2813 PoolUpdateKind::Commit,
2814 );
2815
2816 assert_eq!(pool.pending_pool.len(), 2, "tx1 and tx2 should be promoted");
2818 assert_eq!(pool.basefee_pool.len(), 1, "tx3 should remain in basefee");
2819
2820 assert_eq!(
2823 outcome.promoted.len(),
2824 2,
2825 "Should report exactly 2 promotions, not double-counted"
2826 );
2827
2828 let promoted_prices: Vec<u128> =
2830 outcome.promoted.iter().map(|tx| tx.max_fee_per_gas()).collect();
2831 assert!(promoted_prices.contains(&60));
2832 assert!(promoted_prices.contains(&55));
2833 }
2834
2835 #[test]
2836 fn test_basefee_decrease_with_empty_senders() {
2837 let mut tx_factory = MockTransactionFactory::default();
2840 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2841
2842 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2844
2845 let mut block_info = pool.block_info();
2847 block_info.pending_basefee = 100;
2848 pool.set_block_info(block_info);
2849
2850 let validated = tx_factory.validated(tx);
2852 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2853
2854 assert_eq!(pool.basefee_pool.len(), 1);
2855 assert_eq!(pool.pending_pool.len(), 0);
2856
2857 block_info.pending_basefee = 50;
2859 let outcome = pool.on_canonical_state_change(
2860 block_info,
2861 vec![],
2862 FxHashMap::default(), PoolUpdateKind::Commit,
2864 );
2865
2866 assert_eq!(pool.pending_pool.len(), 1, "Fee decrease should promote tx");
2868 assert_eq!(pool.basefee_pool.len(), 0);
2869 assert_eq!(outcome.promoted.len(), 1, "Should report promotion from fee update");
2870 }
2871
2872 #[test]
2873 fn test_basefee_decrease_account_makes_unfundable() {
2874 let mut tx_factory = MockTransactionFactory::default();
2877 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2878
2879 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2880 let sender = tx.sender();
2881
2882 let mut block_info = pool.block_info();
2884 block_info.pending_basefee = 100;
2885 pool.set_block_info(block_info);
2886
2887 let validated = tx_factory.validated(tx);
2888 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2889 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2890
2891 assert_eq!(pool.basefee_pool.len(), 1);
2892
2893 block_info.pending_basefee = 50;
2895 let mut changed_senders = FxHashMap::default();
2896 changed_senders.insert(
2897 sender_id,
2898 SenderInfo {
2899 state_nonce: 0,
2900 balance: U256::from(100), },
2902 );
2903
2904 let outcome = pool.on_canonical_state_change(
2905 block_info,
2906 vec![],
2907 changed_senders,
2908 PoolUpdateKind::Commit,
2909 );
2910
2911 assert_eq!(pool.pending_pool.len(), 0, "Unfunded tx should not be in pending");
2913 assert_eq!(pool.basefee_pool.len(), 0, "Tx no longer in basefee pool");
2914 assert_eq!(pool.queued_pool.len(), 1, "Unfunded tx should be in queued pool");
2915
2916 let tx_count = pool.all_transactions.txs.len();
2918 assert_eq!(tx_count, 1, "Transaction should still be in pool (in queued)");
2919
2920 assert_eq!(outcome.promoted.len(), 0, "Should not report promotion");
2921 assert_eq!(outcome.discarded.len(), 0, "Queued tx is not reported as discarded");
2922 }
2923
2924 #[test]
2925 fn insert_already_imported() {
2926 let on_chain_balance = U256::ZERO;
2927 let on_chain_nonce = 0;
2928 let mut f = MockTransactionFactory::default();
2929 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2930 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2931 let tx = f.validated(tx);
2932 pool.add_transaction(tx.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
2933 match pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap_err().kind {
2934 PoolErrorKind::AlreadyImported => {}
2935 _ => unreachable!(),
2936 }
2937 }
2938
2939 #[test]
2940 fn insert_replace() {
2941 let on_chain_balance = U256::ZERO;
2942 let on_chain_nonce = 0;
2943 let mut f = MockTransactionFactory::default();
2944 let mut pool = AllTransactions::default();
2945 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2946 let first = f.validated(tx.clone());
2947 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
2948 let replacement = f.validated(tx.rng_hash().inc_price());
2949 let InsertOk { updates, replaced_tx, .. } =
2950 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
2951 assert!(updates.is_empty());
2952 let replaced = replaced_tx.unwrap();
2953 assert_eq!(replaced.0.hash(), first.hash());
2954
2955 assert!(!pool.contains(first.hash()));
2957 assert!(pool.contains(replacement.hash()));
2958 assert_eq!(pool.len(), 1);
2959 }
2960
2961 #[test]
2962 fn insert_replace_txpool() {
2963 let on_chain_balance = U256::ZERO;
2964 let on_chain_nonce = 0;
2965 let mut f = MockTransactionFactory::default();
2966 let mut pool = TxPool::mock();
2967
2968 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2969 let first = f.validated(tx.clone());
2970 let first_added =
2971 pool.add_transaction(first, on_chain_balance, on_chain_nonce, None).unwrap();
2972 let replacement = f.validated(tx.rng_hash().inc_price());
2973 let replacement_added = pool
2974 .add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce, None)
2975 .unwrap();
2976
2977 assert!(!pool.contains(first_added.hash()));
2979 assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));
2981
2982 assert!(pool.contains(replacement.hash()));
2983 let size = pool.size();
2984 assert_eq!(size.total, 1);
2985 size.assert_invariants();
2986 }
2987
2988 #[test]
2989 fn insert_replace_underpriced() {
2990 let on_chain_balance = U256::ZERO;
2991 let on_chain_nonce = 0;
2992 let mut f = MockTransactionFactory::default();
2993 let mut pool = AllTransactions::default();
2994 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2995 let first = f.validated(tx.clone());
2996 let _res = pool.insert_tx(first, on_chain_balance, on_chain_nonce);
2997 let mut replacement = f.validated(tx.rng_hash());
2998 replacement.transaction = replacement.transaction.decr_price();
2999 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3000 assert!(matches!(err, InsertErr::Underpriced { .. }));
3001 }
3002
3003 #[test]
3004 fn insert_replace_underpriced_not_enough_bump() {
3005 let on_chain_balance = U256::ZERO;
3006 let on_chain_nonce = 0;
3007 let mut f = MockTransactionFactory::default();
3008 let mut pool = AllTransactions::default();
3009 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3010 tx.set_priority_fee(100);
3011 tx.set_max_fee(100);
3012 let first = f.validated(tx.clone());
3013 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3014 let mut replacement = f.validated(tx.rng_hash().inc_price());
3015
3016 replacement.transaction.set_priority_fee(109);
3018 replacement.transaction.set_max_fee(109);
3019 let err =
3020 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3021 assert!(matches!(err, InsertErr::Underpriced { .. }));
3022 assert!(pool.contains(first.hash()));
3024 assert_eq!(pool.len(), 1);
3025
3026 replacement.transaction.set_priority_fee(110);
3028 replacement.transaction.set_max_fee(109);
3029 let err =
3030 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3031 assert!(matches!(err, InsertErr::Underpriced { .. }));
3032 assert!(pool.contains(first.hash()));
3033 assert_eq!(pool.len(), 1);
3034
3035 replacement.transaction.set_priority_fee(109);
3037 replacement.transaction.set_max_fee(110);
3038 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3039 assert!(matches!(err, InsertErr::Underpriced { .. }));
3040 assert!(pool.contains(first.hash()));
3041 assert_eq!(pool.len(), 1);
3042 }
3043
3044 #[test]
3045 fn insert_conflicting_type_normal_to_blob() {
3046 let on_chain_balance = U256::from(10_000);
3047 let on_chain_nonce = 0;
3048 let mut f = MockTransactionFactory::default();
3049 let mut pool = AllTransactions::default();
3050 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3051 let first = f.validated(tx.clone());
3052 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3053 let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3054 let blob = f.validated(tx);
3055 let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
3056 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3057 }
3058
3059 #[test]
3060 fn insert_conflicting_type_blob_to_normal() {
3061 let on_chain_balance = U256::from(10_000);
3062 let on_chain_nonce = 0;
3063 let mut f = MockTransactionFactory::default();
3064 let mut pool = AllTransactions::default();
3065 let tx = MockTransaction::eip4844().inc_price().inc_limit();
3066 let first = f.validated(tx.clone());
3067 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3068 let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3069 let tx = f.validated(tx);
3070 let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
3071 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3072 }
3073
3074 #[test]
3076 fn insert_previous() {
3077 let on_chain_balance = U256::ZERO;
3078 let on_chain_nonce = 0;
3079 let mut f = MockTransactionFactory::default();
3080 let mut pool = AllTransactions::default();
3081 let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
3082 let first = f.validated(tx.clone());
3083 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3084
3085 let first_in_pool = pool.get(first.id()).unwrap();
3086
3087 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3089
3090 let prev = f.validated(tx.prev());
3091 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3092 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3093
3094 assert!(updates.is_empty());
3096 assert!(replaced_tx.is_none());
3097 assert!(state.contains(TxState::NO_NONCE_GAPS));
3098 assert_eq!(move_to, SubPool::Queued);
3099
3100 let first_in_pool = pool.get(first.id()).unwrap();
3101 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3103 }
3104
3105 #[test]
3107 fn insert_with_updates() {
3108 let on_chain_balance = U256::from(10_000);
3109 let on_chain_nonce = 0;
3110 let mut f = MockTransactionFactory::default();
3111 let mut pool = AllTransactions::default();
3112 let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
3113 let first = f.validated(tx.clone());
3114 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3115
3116 let first_in_pool = pool.get(first.id()).unwrap();
3117 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3119 assert_eq!(SubPool::Queued, first_in_pool.subpool);
3120
3121 let prev = f.validated(tx.prev());
3122 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3123 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3124
3125 assert_eq!(updates.len(), 1);
3127 assert!(replaced_tx.is_none());
3128 assert!(state.contains(TxState::NO_NONCE_GAPS));
3129 assert_eq!(move_to, SubPool::Pending);
3130
3131 let first_in_pool = pool.get(first.id()).unwrap();
3132 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3134 assert_eq!(SubPool::Pending, first_in_pool.subpool);
3135 }
3136
3137 #[test]
3138 fn insert_previous_blocking() {
3139 let on_chain_balance = U256::from(1_000);
3140 let on_chain_nonce = 0;
3141 let mut f = MockTransactionFactory::default();
3142 let mut pool = AllTransactions::default();
3143 pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
3144 let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
3145 let first = f.validated(tx.clone());
3146
3147 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3148
3149 let first_in_pool = pool.get(first.id()).unwrap();
3150
3151 assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
3152 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3154
3155 let prev = f.validated(tx.prev());
3156 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3157 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3158
3159 assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3160 assert!(updates.is_empty());
3162 assert!(replaced_tx.is_none());
3163 assert!(state.contains(TxState::NO_NONCE_GAPS));
3164 assert_eq!(move_to, SubPool::BaseFee);
3165
3166 let first_in_pool = pool.get(first.id()).unwrap();
3167 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3169 }
3170
3171 #[test]
3172 fn rejects_spammer() {
3173 let on_chain_balance = U256::from(1_000);
3174 let on_chain_nonce = 0;
3175 let mut f = MockTransactionFactory::default();
3176 let mut pool = AllTransactions::default();
3177
3178 let mut tx = MockTransaction::eip1559();
3179 let unblocked_tx = tx.clone();
3180 for _ in 0..pool.max_account_slots {
3181 tx = tx.next();
3182 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
3183 }
3184
3185 assert_eq!(
3186 pool.max_account_slots,
3187 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3188 );
3189
3190 let err =
3191 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
3192 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
3193
3194 assert!(pool
3195 .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
3196 .is_ok());
3197 }
3198
3199 #[test]
3200 fn allow_local_spamming() {
3201 let on_chain_balance = U256::from(1_000);
3202 let on_chain_nonce = 0;
3203 let mut f = MockTransactionFactory::default();
3204 let mut pool = AllTransactions::default();
3205
3206 let mut tx = MockTransaction::eip1559();
3207 for _ in 0..pool.max_account_slots {
3208 tx = tx.next();
3209 pool.insert_tx(
3210 f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
3211 on_chain_balance,
3212 on_chain_nonce,
3213 )
3214 .unwrap();
3215 }
3216
3217 assert_eq!(
3218 pool.max_account_slots,
3219 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3220 );
3221
3222 pool.insert_tx(
3223 f.validated_with_origin(TransactionOrigin::Local, tx.next()),
3224 on_chain_balance,
3225 on_chain_nonce,
3226 )
3227 .unwrap();
3228 }
3229
3230 #[test]
3231 fn reject_tx_over_gas_limit() {
3232 let on_chain_balance = U256::from(1_000);
3233 let on_chain_nonce = 0;
3234 let mut f = MockTransactionFactory::default();
3235 let mut pool = AllTransactions::default();
3236
3237 let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
3238
3239 assert!(matches!(
3240 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
3241 Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
3242 ));
3243 }
3244
3245 #[test]
3246 fn test_tx_equal_gas_limit() {
3247 let on_chain_balance = U256::from(1_000);
3248 let on_chain_nonce = 0;
3249 let mut f = MockTransactionFactory::default();
3250 let mut pool = AllTransactions::default();
3251
3252 let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
3253
3254 let InsertOk { state, .. } =
3255 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
3256 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
3257 }
3258
3259 #[test]
3260 fn update_basefee_subpools() {
3261 let mut f = MockTransactionFactory::default();
3262 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3263
3264 let tx = MockTransaction::eip1559().inc_price_by(10);
3265 let validated = f.validated(tx.clone());
3266 let id = *validated.id();
3267 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3268
3269 assert_eq!(pool.pending_pool.len(), 1);
3270
3271 pool.update_basefee((tx.max_fee_per_gas() + 1) as u64, |_| {});
3272
3273 assert!(pool.pending_pool.is_empty());
3274 assert_eq!(pool.basefee_pool.len(), 1);
3275
3276 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3277 }
3278
3279 #[test]
3280 fn update_basefee_subpools_setting_block_info() {
3281 let mut f = MockTransactionFactory::default();
3282 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3283
3284 let tx = MockTransaction::eip1559().inc_price_by(10);
3285 let validated = f.validated(tx.clone());
3286 let id = *validated.id();
3287 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3288
3289 assert_eq!(pool.pending_pool.len(), 1);
3290
3291 let mut block_info = pool.block_info();
3293 block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
3294 pool.set_block_info(block_info);
3295
3296 assert!(pool.pending_pool.is_empty());
3297 assert_eq!(pool.basefee_pool.len(), 1);
3298
3299 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3300 }
3301
3302 #[test]
3303 fn basefee_decrease_promotes_affordable_and_keeps_unaffordable() {
3304 use alloy_primitives::address;
3305 let mut f = MockTransactionFactory::default();
3306 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3307
3308 let sender_a = address!("0x000000000000000000000000000000000000000a");
3311 let sender_b = address!("0x000000000000000000000000000000000000000b");
3312 let sender_c = address!("0x000000000000000000000000000000000000000c");
3313
3314 let tx1 = MockTransaction::eip1559()
3315 .set_sender(sender_a)
3316 .set_nonce(0)
3317 .set_max_fee(500)
3318 .inc_limit();
3319 let tx2 = MockTransaction::eip1559()
3320 .set_sender(sender_b)
3321 .set_nonce(0)
3322 .set_max_fee(600)
3323 .inc_limit();
3324 let tx3 = MockTransaction::eip1559()
3325 .set_sender(sender_c)
3326 .set_nonce(0)
3327 .set_max_fee(400)
3328 .inc_limit();
3329
3330 let mut block_info = pool.block_info();
3332 block_info.pending_basefee = 700;
3333 pool.set_block_info(block_info);
3334
3335 let validated1 = f.validated(tx1);
3336 let validated2 = f.validated(tx2);
3337 let validated3 = f.validated(tx3);
3338 let id1 = *validated1.id();
3339 let id2 = *validated2.id();
3340 let id3 = *validated3.id();
3341
3342 pool.add_transaction(validated1, U256::from(10_000), 0, None).unwrap();
3346 pool.add_transaction(validated2, U256::from(10_000), 0, None).unwrap();
3347 pool.add_transaction(validated3, U256::from(10_000), 0, None).unwrap();
3348
3349 println!("Basefee pool len: {}", pool.basefee_pool.len());
3351 println!("Pending pool len: {}", pool.pending_pool.len());
3352 println!("tx1 subpool: {:?}", pool.all_transactions.txs.get(&id1).unwrap().subpool);
3353 println!("tx2 subpool: {:?}", pool.all_transactions.txs.get(&id2).unwrap().subpool);
3354 println!("tx3 subpool: {:?}", pool.all_transactions.txs.get(&id3).unwrap().subpool);
3355
3356 assert_eq!(pool.basefee_pool.len(), 3);
3358 assert_eq!(pool.pending_pool.len(), 0);
3359 assert_eq!(pool.all_transactions.txs.get(&id1).unwrap().subpool, SubPool::BaseFee);
3360 assert_eq!(pool.all_transactions.txs.get(&id2).unwrap().subpool, SubPool::BaseFee);
3361 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3362
3363 let mut block_info = pool.block_info();
3365 block_info.pending_basefee = 450; pool.set_block_info(block_info);
3367
3368 assert_eq!(pool.basefee_pool.len(), 1);
3373 assert_eq!(pool.pending_pool.len(), 2);
3374
3375 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3377
3378 let tx1_meta = pool.all_transactions.txs.get(&id1).unwrap();
3380 let tx2_meta = pool.all_transactions.txs.get(&id2).unwrap();
3381 assert_eq!(tx1_meta.subpool, SubPool::Pending);
3382 assert_eq!(tx2_meta.subpool, SubPool::Pending);
3383 assert!(tx1_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3384 assert!(tx2_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3385
3386 let best: Vec<_> = pool.best_transactions().take(3).collect();
3388 assert_eq!(best.len(), 2); assert!(best.iter().any(|tx| tx.id() == &id1));
3390 assert!(best.iter().any(|tx| tx.id() == &id2));
3391 }
3392
3393 #[test]
3394 fn apply_fee_updates_records_promotions_after_basefee_drop() {
3395 let mut f = MockTransactionFactory::default();
3396 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3397
3398 let tx = MockTransaction::eip1559()
3399 .with_gas_limit(21_000)
3400 .with_max_fee(500)
3401 .with_priority_fee(1);
3402 let validated = f.validated(tx);
3403 let id = *validated.id();
3404 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3405
3406 assert_eq!(pool.pending_pool.len(), 1);
3407
3408 pool.update_basefee(600, |_| {});
3410 assert!(pool.pending_pool.is_empty());
3411 assert_eq!(pool.basefee_pool.len(), 1);
3412
3413 let prev_base_fee = 600;
3414 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3415
3416 pool.all_transactions.pending_fees.base_fee = 400;
3418
3419 let mut outcome = UpdateOutcome::default();
3420 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3421
3422 assert_eq!(pool.pending_pool.len(), 1);
3423 assert!(pool.basefee_pool.is_empty());
3424 assert_eq!(outcome.promoted.len(), 1);
3425 assert_eq!(outcome.promoted[0].id(), &id);
3426 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3427 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3428
3429 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3430 assert_eq!(tx_meta.subpool, SubPool::Pending);
3431 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3432 }
3433
3434 #[test]
3435 fn apply_fee_updates_records_promotions_after_blob_fee_drop() {
3436 let mut f = MockTransactionFactory::default();
3437 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3438
3439 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3440
3441 let tx = MockTransaction::eip4844().with_blob_fee(initial_blob_fee + 100);
3442 let validated = f.validated(tx.clone());
3443 let id = *validated.id();
3444 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3445
3446 assert_eq!(pool.pending_pool.len(), 1);
3447
3448 let increased_blob_fee = tx.max_fee_per_blob_gas().unwrap() + 200;
3450 pool.update_blob_fee(increased_blob_fee, Ordering::Equal, |_| {});
3451 assert!(pool.pending_pool.is_empty());
3452 assert_eq!(pool.blob_pool.len(), 1);
3453
3454 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3455 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3456
3457 pool.all_transactions.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
3459
3460 let mut outcome = UpdateOutcome::default();
3461 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3462
3463 assert_eq!(pool.pending_pool.len(), 1);
3464 assert!(pool.blob_pool.is_empty());
3465 assert_eq!(outcome.promoted.len(), 1);
3466 assert_eq!(outcome.promoted[0].id(), &id);
3467 assert_eq!(pool.all_transactions.pending_fees.base_fee, prev_base_fee);
3468 assert_eq!(pool.all_transactions.pending_fees.blob_fee, tx.max_fee_per_blob_gas().unwrap());
3469
3470 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3471 assert_eq!(tx_meta.subpool, SubPool::Pending);
3472 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3473 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3474 }
3475
3476 #[test]
3477 fn apply_fee_updates_promotes_blob_after_basefee_drop() {
3478 let mut f = MockTransactionFactory::default();
3479 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3480
3481 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3482
3483 let tx = MockTransaction::eip4844()
3484 .with_max_fee(500)
3485 .with_priority_fee(1)
3486 .with_blob_fee(initial_blob_fee + 100);
3487 let validated = f.validated(tx);
3488 let id = *validated.id();
3489 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3490
3491 assert_eq!(pool.pending_pool.len(), 1);
3492
3493 let high_base_fee = 600;
3495 pool.update_basefee(high_base_fee, |_| {});
3496 assert!(pool.pending_pool.is_empty());
3497 assert_eq!(pool.blob_pool.len(), 1);
3498
3499 let prev_base_fee = high_base_fee;
3500 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3501
3502 pool.all_transactions.pending_fees.base_fee = 400;
3504
3505 let mut outcome = UpdateOutcome::default();
3506 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3507
3508 assert_eq!(pool.pending_pool.len(), 1);
3509 assert!(pool.blob_pool.is_empty());
3510 assert_eq!(outcome.promoted.len(), 1);
3511 assert_eq!(outcome.promoted[0].id(), &id);
3512 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3513 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3514
3515 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3516 assert_eq!(tx_meta.subpool, SubPool::Pending);
3517 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3518 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3519 }
3520
3521 #[test]
3522 fn apply_fee_updates_demotes_after_basefee_rise() {
3523 let mut f = MockTransactionFactory::default();
3524 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3525
3526 let tx = MockTransaction::eip1559()
3527 .with_gas_limit(21_000)
3528 .with_max_fee(400)
3529 .with_priority_fee(1);
3530 let validated = f.validated(tx);
3531 let id = *validated.id();
3532 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3533
3534 assert_eq!(pool.pending_pool.len(), 1);
3535
3536 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3537 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3538
3539 let new_base_fee = prev_base_fee + 1_000;
3541 pool.all_transactions.pending_fees.base_fee = new_base_fee;
3542
3543 let mut outcome = UpdateOutcome::default();
3544 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3545
3546 assert!(pool.pending_pool.is_empty());
3547 assert_eq!(pool.basefee_pool.len(), 1);
3548 assert!(outcome.promoted.is_empty());
3549 assert_eq!(pool.all_transactions.pending_fees.base_fee, new_base_fee);
3550 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3551
3552 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3553 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
3554 assert!(!tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3555 }
3556
3557 #[test]
3558 fn get_highest_transaction_by_sender_and_nonce() {
3559 let mut f = MockTransactionFactory::default();
3561 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3562
3563 let tx = MockTransaction::eip1559();
3565 pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0, None).unwrap();
3566
3567 let tx1 = tx.inc_price().next();
3569
3570 let tx1_validated = f.validated(tx1.clone());
3572 pool.add_transaction(tx1_validated, U256::from(1_000), 0, None).unwrap();
3573
3574 assert_eq!(
3576 pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
3577 Some(1)
3578 );
3579
3580 let highest_tx = pool
3582 .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
3583 .expect("Failed to retrieve highest transaction");
3584
3585 assert_eq!(highest_tx.as_ref().transaction, tx1);
3587 }
3588
3589 #[test]
3590 fn get_highest_consecutive_transaction_by_sender() {
3591 let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
3593 let mut f = MockTransactionFactory::default();
3594
3595 let sender = Address::random();
3597 let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
3598 for nonce in txs {
3599 let mut mock_tx = MockTransaction::eip1559();
3600 mock_tx.set_sender(sender);
3601 mock_tx.set_nonce(nonce);
3602
3603 let validated_tx = f.validated(mock_tx);
3604 pool.add_transaction(validated_tx, U256::from(1000), 0, None).unwrap();
3605 }
3606
3607 let sender_id = f.ids.sender_id(&sender).unwrap();
3609 let next_tx =
3610 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
3611 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
3612
3613 let next_tx =
3614 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
3615 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
3616
3617 let next_tx =
3618 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3619 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
3620
3621 let mut info = SenderInfo::default();
3623 info.update(8, U256::ZERO);
3624 pool.all_transactions.sender_info.insert(sender_id, info);
3625 let next_tx =
3626 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3627 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
3628 }
3629
3630 #[test]
3631 fn discard_nonce_too_low() {
3632 let mut f = MockTransactionFactory::default();
3633 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3634
3635 let tx = MockTransaction::eip1559().inc_price_by(10);
3636 let validated = f.validated(tx.clone());
3637 let id = *validated.id();
3638 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3639
3640 let next = tx.next();
3641 let validated = f.validated(next.clone());
3642 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3643
3644 assert_eq!(pool.pending_pool.len(), 2);
3645
3646 let mut changed_senders = HashMap::default();
3647 changed_senders.insert(
3648 id.sender,
3649 SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
3650 );
3651 let outcome = pool.update_accounts(changed_senders);
3652 assert_eq!(outcome.discarded.len(), 1);
3653 assert_eq!(pool.pending_pool.len(), 1);
3654 }
3655
3656 #[test]
3657 fn discard_with_large_blob_txs() {
3658 reth_tracing::init_test_tracing();
3660
3661 let mut f = MockTransactionFactory::default();
3663 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3664 let default_limits = pool.config.blob_limit;
3665
3666 let a_sender = address!("0x000000000000000000000000000000000000000a");
3669
3670 let mut block_info = pool.block_info();
3672 block_info.pending_blob_fee = Some(100);
3673 block_info.pending_basefee = 100;
3674
3675 pool.set_block_info(block_info);
3677
3678 let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
3680 .into_iter()
3681 .map(|mut tx| {
3682 tx.set_size(default_limits.max_size / 2 + 1);
3683 tx.set_max_fee((block_info.pending_basefee - 1).into());
3684 tx
3685 })
3686 .collect::<Vec<_>>();
3687
3688 for tx in a_txs {
3690 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3691 }
3692
3693 let removed = pool.discard_worst();
3695 assert_eq!(removed.len(), 1);
3696 }
3697
3698 #[test]
3699 fn discard_with_parked_large_txs() {
3700 reth_tracing::init_test_tracing();
3702
3703 let mut f = MockTransactionFactory::default();
3705 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3706 let default_limits = pool.config.queued_limit;
3707
3708 let a_sender = address!("0x000000000000000000000000000000000000000a");
3711
3712 let pool_base_fee = 100;
3714 pool.update_basefee(pool_base_fee, |_| {});
3715
3716 let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
3718 .into_iter()
3719 .map(|mut tx| {
3720 tx.set_size(default_limits.max_size / 2 + 1);
3721 tx.set_max_fee((pool_base_fee - 1).into());
3722 tx
3723 })
3724 .collect::<Vec<_>>();
3725
3726 for tx in a_txs {
3728 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3729 }
3730
3731 let removed = pool.discard_worst();
3733 assert_eq!(removed.len(), 1);
3734 }
3735
3736 #[test]
3737 fn discard_at_capacity() {
3738 let mut f = MockTransactionFactory::default();
3739 let queued_limit = SubPoolLimit::new(1000, usize::MAX);
3740 let mut pool =
3741 TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
3742
3743 for _ in 0..queued_limit.max_txs {
3745 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3746 let validated = f.validated(tx.clone());
3747 let _id = *validated.id();
3748 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3749 }
3750
3751 let size = pool.size();
3752 assert_eq!(size.queued, queued_limit.max_txs);
3753
3754 for _ in 0..queued_limit.max_txs {
3755 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3756 let validated = f.validated(tx.clone());
3757 let _id = *validated.id();
3758 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3759
3760 pool.discard_worst();
3761 pool.assert_invariants();
3762 assert!(pool.size().queued <= queued_limit.max_txs);
3763 }
3764 }
3765
3766 #[test]
3767 fn discard_blobs_at_capacity() {
3768 let mut f = MockTransactionFactory::default();
3769 let blob_limit = SubPoolLimit::new(1000, usize::MAX);
3770 let mut pool =
3771 TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
3772 pool.all_transactions.pending_fees.blob_fee = 10000;
3773 for _ in 0..blob_limit.max_txs {
3775 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3776 let validated = f.validated(tx.clone());
3777 let _id = *validated.id();
3778 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3779 }
3780
3781 let size = pool.size();
3782 assert_eq!(size.blob, blob_limit.max_txs);
3783
3784 for _ in 0..blob_limit.max_txs {
3785 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3786 let validated = f.validated(tx.clone());
3787 let _id = *validated.id();
3788 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3789
3790 pool.discard_worst();
3791 pool.assert_invariants();
3792 assert!(pool.size().blob <= blob_limit.max_txs);
3793 }
3794 }
3795
3796 #[test]
3797 fn account_updates_sender_balance() {
3798 let mut on_chain_balance = U256::from(100);
3799 let on_chain_nonce = 0;
3800 let mut f = MockTransactionFactory::default();
3801 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3802
3803 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3804 let tx_1 = tx_0.next();
3805 let tx_2 = tx_1.next();
3806
3807 let v0 = f.validated(tx_0);
3809 let v1 = f.validated(tx_1);
3810 let v2 = f.validated(tx_2);
3811
3812 let _res =
3813 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3814 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3815 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3816
3817 assert_eq!(1, pool.pending_transactions().len());
3819 assert_eq!(2, pool.queued_transactions().len());
3820
3821 let mut updated_accounts = HashMap::default();
3823 on_chain_balance = U256::from(300);
3824 updated_accounts.insert(
3825 v0.sender_id(),
3826 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3827 );
3828 pool.update_accounts(updated_accounts.clone());
3829
3830 assert_eq!(3, pool.pending_transactions().len());
3831 assert!(pool.queued_transactions().is_empty());
3832
3833 updated_accounts.entry(v0.sender_id()).and_modify(|v| v.balance = U256::from(1));
3835 pool.update_accounts(updated_accounts);
3836
3837 assert!(pool.pending_transactions().is_empty());
3838 assert_eq!(3, pool.queued_transactions().len());
3839 }
3840
3841 #[test]
3842 fn account_updates_nonce_gap() {
3843 let on_chain_balance = U256::from(10_000);
3844 let mut on_chain_nonce = 0;
3845 let mut f = MockTransactionFactory::default();
3846 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3847
3848 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3849 let tx_1 = tx_0.next();
3850 let tx_2 = tx_1.next();
3851
3852 let v0 = f.validated(tx_0);
3854 let v1 = f.validated(tx_1);
3855 let v2 = f.validated(tx_2);
3856
3857 let _res =
3859 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3860 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3861
3862 assert!(pool.queued_transactions().is_empty());
3863 assert_eq!(2, pool.pending_transactions().len());
3864
3865 pool.remove_transaction_by_hash(v0.hash());
3867
3868 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3870
3871 assert_eq!(2, pool.queued_transactions().len());
3873 assert!(pool.pending_transactions().is_empty());
3874
3875 let mut updated_accounts = HashMap::default();
3877 on_chain_nonce += 1;
3878 updated_accounts.insert(
3879 v0.sender_id(),
3880 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3881 );
3882 pool.update_accounts(updated_accounts);
3883
3884 assert!(pool.queued_transactions().is_empty());
3886 assert_eq!(2, pool.pending_transactions().len());
3887 }
3888 #[test]
3889 fn test_transaction_removal() {
3890 let on_chain_balance = U256::from(10_000);
3891 let on_chain_nonce = 0;
3892 let mut f = MockTransactionFactory::default();
3893 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3894
3895 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3896 let tx_1 = tx_0.next();
3897
3898 let v0 = f.validated(tx_0);
3900 let v1 = f.validated(tx_1);
3901
3902 let _res =
3904 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3905 let _res =
3906 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3907
3908 assert_eq!(0, pool.queued_transactions().len());
3909 assert_eq!(2, pool.pending_transactions().len());
3910
3911 pool.remove_transaction(v0.id());
3913 let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
3915 assert_eq!(vec![v1.nonce()], pool_txs);
3916 }
3917 #[test]
3918 fn test_remove_transactions() {
3919 let on_chain_balance = U256::from(10_000);
3920 let on_chain_nonce = 0;
3921 let mut f = MockTransactionFactory::default();
3922 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3923
3924 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3925 let tx_1 = tx_0.next();
3926 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3927 let tx_3 = tx_2.next();
3928
3929 let v0 = f.validated(tx_0);
3931 let v1 = f.validated(tx_1);
3932 let v2 = f.validated(tx_2);
3933 let v3 = f.validated(tx_3);
3934
3935 let _res =
3937 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3938 let _res =
3939 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3940 let _res =
3941 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3942 let _res =
3943 pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3944
3945 assert_eq!(0, pool.queued_transactions().len());
3946 assert_eq!(4, pool.pending_transactions().len());
3947
3948 pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
3949
3950 assert_eq!(2, pool.queued_transactions().len());
3951 assert!(pool.pending_transactions().is_empty());
3952 assert!(pool.contains(v1.hash()));
3953 assert!(pool.contains(v3.hash()));
3954 }
3955
3956 #[test]
3957 fn test_remove_transactions_middle_pending_hash() {
3958 let on_chain_balance = U256::from(10_000);
3959 let on_chain_nonce = 0;
3960 let mut f = MockTransactionFactory::default();
3961 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3962
3963 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3964 let tx_1 = tx_0.next();
3965 let tx_2 = tx_1.next();
3966 let tx_3 = tx_2.next();
3967
3968 let v0 = f.validated(tx_0);
3970 let v1 = f.validated(tx_1);
3971 let v2 = f.validated(tx_2);
3972 let v3 = f.validated(tx_3);
3973
3974 let _res = pool.add_transaction(v0, on_chain_balance, on_chain_nonce, None).unwrap();
3976 let _res =
3977 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3978 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3979 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
3980
3981 assert_eq!(0, pool.queued_transactions().len());
3982 assert_eq!(4, pool.pending_transactions().len());
3983
3984 let mut removed_txs = pool.remove_transactions(vec![*v1.hash()]);
3985 assert_eq!(1, removed_txs.len());
3986
3987 assert_eq!(2, pool.queued_transactions().len());
3988 assert_eq!(1, pool.pending_transactions().len());
3989
3990 let removed_tx = removed_txs.pop().unwrap();
3992 let v1 = f.validated(removed_tx.transaction.clone());
3993 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3994 assert_eq!(0, pool.queued_transactions().len());
3995 assert_eq!(4, pool.pending_transactions().len());
3996 }
3997
3998 #[test]
3999 fn test_remove_transactions_and_descendants() {
4000 let on_chain_balance = U256::from(10_000);
4001 let on_chain_nonce = 0;
4002 let mut f = MockTransactionFactory::default();
4003 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4004
4005 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4006 let tx_1 = tx_0.next();
4007 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4008 let tx_3 = tx_2.next();
4009 let tx_4 = tx_3.next();
4010
4011 let v0 = f.validated(tx_0);
4013 let v1 = f.validated(tx_1);
4014 let v2 = f.validated(tx_2);
4015 let v3 = f.validated(tx_3);
4016 let v4 = f.validated(tx_4);
4017
4018 let _res =
4020 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4021 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4022 let _res =
4023 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4024 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4025 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4026
4027 assert_eq!(0, pool.queued_transactions().len());
4028 assert_eq!(5, pool.pending_transactions().len());
4029
4030 pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
4031
4032 assert_eq!(0, pool.queued_transactions().len());
4033 assert_eq!(0, pool.pending_transactions().len());
4034 }
4035 #[test]
4036 fn test_remove_descendants() {
4037 let on_chain_balance = U256::from(10_000);
4038 let on_chain_nonce = 0;
4039 let mut f = MockTransactionFactory::default();
4040 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4041
4042 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4043 let tx_1 = tx_0.next();
4044 let tx_2 = tx_1.next();
4045 let tx_3 = tx_2.next();
4046
4047 let v0 = f.validated(tx_0);
4049 let v1 = f.validated(tx_1);
4050 let v2 = f.validated(tx_2);
4051 let v3 = f.validated(tx_3);
4052
4053 let _res =
4055 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4056 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4057 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4058 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4059
4060 assert_eq!(0, pool.queued_transactions().len());
4061 assert_eq!(4, pool.pending_transactions().len());
4062
4063 let mut removed = Vec::new();
4064 pool.remove_transaction(v0.id());
4065 pool.remove_descendants(v0.id(), &mut removed);
4066
4067 assert_eq!(0, pool.queued_transactions().len());
4068 assert_eq!(0, pool.pending_transactions().len());
4069 assert_eq!(3, removed.len());
4070 }
4071 #[test]
4072 fn test_remove_transactions_by_sender() {
4073 let on_chain_balance = U256::from(10_000);
4074 let on_chain_nonce = 0;
4075 let mut f = MockTransactionFactory::default();
4076 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4077
4078 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4079 let tx_1 = tx_0.next();
4080 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4081 let tx_3 = tx_2.next();
4082 let tx_4 = tx_3.next();
4083
4084 let v0 = f.validated(tx_0);
4086 let v1 = f.validated(tx_1);
4087 let v2 = f.validated(tx_2);
4088 let v3 = f.validated(tx_3);
4089 let v4 = f.validated(tx_4);
4090
4091 let _res =
4093 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4094 let _res =
4095 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4096 let _res =
4097 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4098 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4099 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4100
4101 assert_eq!(0, pool.queued_transactions().len());
4102 assert_eq!(5, pool.pending_transactions().len());
4103
4104 pool.remove_transactions_by_sender(v2.sender_id());
4105
4106 assert_eq!(0, pool.queued_transactions().len());
4107 assert_eq!(2, pool.pending_transactions().len());
4108 assert!(pool.contains(v0.hash()));
4109 assert!(pool.contains(v1.hash()));
4110 }
4111 #[test]
4112 fn wrong_best_order_of_transactions() {
4113 let on_chain_balance = U256::from(10_000);
4114 let mut on_chain_nonce = 0;
4115 let mut f = MockTransactionFactory::default();
4116 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4117
4118 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4119 let tx_1 = tx_0.next();
4120 let tx_2 = tx_1.next();
4121 let tx_3 = tx_2.next();
4122
4123 let v0 = f.validated(tx_0);
4125 let v1 = f.validated(tx_1);
4126 let v2 = f.validated(tx_2);
4127 let v3 = f.validated(tx_3);
4128
4129 let _res =
4131 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4132 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4133
4134 assert_eq!(0, pool.queued_transactions().len());
4135 assert_eq!(2, pool.pending_transactions().len());
4136
4137 pool.remove_transaction(v0.id());
4139
4140 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4142
4143 assert_eq!(1, pool.queued_transactions().len());
4145 assert_eq!(1, pool.pending_transactions().len());
4146
4147 let mut updated_accounts = HashMap::default();
4149 on_chain_nonce += 1;
4150 updated_accounts.insert(
4151 v0.sender_id(),
4152 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4153 );
4154 pool.update_accounts(updated_accounts);
4155
4156 assert_eq!(0, pool.queued_transactions().len());
4159 assert_eq!(2, pool.pending_transactions().len());
4160
4161 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4163 assert_eq!(0, pool.queued_transactions().len());
4164 assert_eq!(3, pool.pending_transactions().len());
4165
4166 assert_eq!(
4169 pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
4170 vec![1, 2, 3]
4171 );
4172 }
4173
4174 #[test]
4175 fn test_best_with_attributes() {
4176 let on_chain_balance = U256::MAX;
4177 let on_chain_nonce = 0;
4178 let mut f = MockTransactionFactory::default();
4179 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4180
4181 let base_fee: u128 = 100;
4182 let blob_fee: u128 = 100;
4183
4184 let mut block_info = pool.block_info();
4186 block_info.pending_basefee = base_fee as u64;
4187 block_info.pending_blob_fee = Some(blob_fee);
4188 pool.set_block_info(block_info);
4189
4190 let tx1 = MockTransaction::eip4844()
4192 .with_sender(Address::with_last_byte(1))
4193 .with_max_fee(base_fee + 10)
4194 .with_blob_fee(blob_fee + 10);
4195 let tx2 = MockTransaction::eip4844()
4196 .with_sender(Address::with_last_byte(2))
4197 .with_max_fee(base_fee + 10)
4198 .with_blob_fee(blob_fee);
4199 let tx3 = MockTransaction::eip4844()
4200 .with_sender(Address::with_last_byte(3))
4201 .with_max_fee(base_fee)
4202 .with_blob_fee(blob_fee + 10);
4203 let tx4 = MockTransaction::eip4844()
4204 .with_sender(Address::with_last_byte(4))
4205 .with_max_fee(base_fee)
4206 .with_blob_fee(blob_fee);
4207 let tx5 = MockTransaction::eip4844()
4208 .with_sender(Address::with_last_byte(5))
4209 .with_max_fee(base_fee)
4210 .with_blob_fee(blob_fee - 10);
4211 let tx6 = MockTransaction::eip4844()
4212 .with_sender(Address::with_last_byte(6))
4213 .with_max_fee(base_fee - 10)
4214 .with_blob_fee(blob_fee);
4215 let tx7 = MockTransaction::eip4844()
4216 .with_sender(Address::with_last_byte(7))
4217 .with_max_fee(base_fee - 10)
4218 .with_blob_fee(blob_fee - 10);
4219
4220 for tx in vec![
4221 tx1.clone(),
4222 tx2.clone(),
4223 tx3.clone(),
4224 tx4.clone(),
4225 tx5.clone(),
4226 tx6.clone(),
4227 tx7.clone(),
4228 ] {
4229 pool.add_transaction(f.validated(tx.clone()), on_chain_balance, on_chain_nonce, None)
4230 .unwrap();
4231 }
4232
4233 let base_fee = base_fee as u64;
4234 let blob_fee = blob_fee as u64;
4235
4236 let cases = vec![
4237 (BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee + 5)), vec![tx1.clone()]),
4239 (
4241 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee)),
4242 vec![tx1.clone(), tx2.clone()],
4243 ),
4244 (
4246 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee - 5)),
4247 vec![tx1.clone(), tx2.clone()],
4248 ),
4249 (
4251 BestTransactionsAttributes::new(base_fee, Some(blob_fee + 5)),
4252 vec![tx1.clone(), tx3.clone()],
4253 ),
4254 (
4256 BestTransactionsAttributes::new(base_fee, Some(blob_fee)),
4257 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
4258 ),
4259 (
4261 BestTransactionsAttributes::new(base_fee, Some(blob_fee - 10)),
4262 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx5.clone()],
4263 ),
4264 (
4266 BestTransactionsAttributes::new(base_fee - 5, Some(blob_fee + 5)),
4267 vec![tx1.clone(), tx3.clone()],
4268 ),
4269 (
4271 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee)),
4272 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx6.clone()],
4273 ),
4274 (
4276 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee - 10)),
4277 vec![tx1, tx2, tx5, tx3, tx4, tx6, tx7],
4278 ),
4279 ];
4280
4281 for (idx, (attribute, expected)) in cases.into_iter().enumerate() {
4282 let mut best = pool.best_transactions_with_attributes(attribute);
4283
4284 for (tx_idx, expected_tx) in expected.into_iter().enumerate() {
4285 let tx = best.next().expect("Transaction should be returned");
4286 assert_eq!(
4287 tx.transaction,
4288 expected_tx,
4289 "Failed tx {} in case {}",
4290 tx_idx + 1,
4291 idx + 1
4292 );
4293 }
4294
4295 assert!(best.next().is_none());
4297 }
4298 }
4299
4300 #[test]
4301 fn test_pending_ordering() {
4302 let mut f = MockTransactionFactory::default();
4303 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4304
4305 let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
4306 let tx_1 = tx_0.next();
4307
4308 let v0 = f.validated(tx_0);
4309 let v1 = f.validated(tx_1);
4310
4311 pool.add_transaction(v0.clone(), U256::MAX, 0, None).unwrap();
4313 assert_eq!(1, pool.queued_transactions().len());
4314
4315 pool.add_transaction(v1, U256::MAX, 1, None).unwrap();
4317
4318 assert_eq!(2, pool.pending_transactions().len());
4319 assert_eq!(0, pool.queued_transactions().len());
4320
4321 assert_eq!(
4322 pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
4323 v0.nonce()
4324 );
4325 }
4326
4327 #[test]
4329 fn one_sender_one_independent_transaction() {
4330 let mut on_chain_balance = U256::from(4_999); let mut on_chain_nonce = 40;
4332 let mut f = MockTransactionFactory::default();
4333 let mut pool = TxPool::mock();
4334 let mut submitted_txs = Vec::new();
4335
4336 let template =
4338 MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
4339
4340 for tx_nonce in 40..48 {
4343 let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
4344 submitted_txs.push(*tx.id());
4345 pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap();
4346 }
4347
4348 on_chain_balance = U256::from(999_999);
4351 on_chain_nonce = 42;
4352 pool.remove_transaction(&submitted_txs[0]);
4353 pool.remove_transaction(&submitted_txs[1]);
4354
4355 for tx_nonce in 48..52 {
4357 pool.add_transaction(
4358 f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
4359 on_chain_balance,
4360 on_chain_nonce,
4361 None,
4362 )
4363 .unwrap();
4364 }
4365
4366 let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
4367 assert_eq!(best_txs.len(), 10); assert_eq!(pool.pending_pool.independent().len(), 1);
4370 }
4371
4372 #[test]
4373 fn test_insertion_disorder() {
4374 let mut f = MockTransactionFactory::default();
4375 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4376
4377 let sender = address!("0x1234567890123456789012345678901234567890");
4378 let tx0 = f.validated_arc(
4379 MockTransaction::legacy().with_sender(sender).with_nonce(0).with_gas_price(10),
4380 );
4381 let tx1 = f.validated_arc(
4382 MockTransaction::eip1559()
4383 .with_sender(sender)
4384 .with_nonce(1)
4385 .with_gas_limit(1000)
4386 .with_gas_price(10),
4387 );
4388 let tx2 = f.validated_arc(
4389 MockTransaction::legacy().with_sender(sender).with_nonce(2).with_gas_price(10),
4390 );
4391 let tx3 = f.validated_arc(
4392 MockTransaction::legacy().with_sender(sender).with_nonce(3).with_gas_price(10),
4393 );
4394
4395 pool.add_transaction((*tx0).clone(), U256::from(1000), 0, None).unwrap();
4397 let mut best = pool.best_transactions();
4398 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4399 assert_eq!(t0.id(), tx0.id());
4400 pool.add_transaction((*tx1).clone(), U256::from(1000), 0, None).unwrap();
4402 let mut best = pool.best_transactions();
4403 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4404 assert_eq!(t0.id(), tx0.id());
4405 assert!(best.next().is_none());
4406
4407 pool.add_transaction((*tx2).clone(), U256::MAX, 0, None).unwrap();
4409
4410 let mut best = pool.best_transactions();
4411
4412 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4413 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4414 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4415 assert_eq!(t0.id(), tx0.id());
4416 assert_eq!(t1.id(), tx1.id());
4417 assert_eq!(t2.id(), tx2.id());
4418
4419 pool.add_transaction((*tx3).clone(), U256::MAX, 0, None).unwrap();
4421 let mut best = pool.best_transactions();
4422 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4423 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4424 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4425 let t3 = best.next().expect("tx3 should be put in the pending subpool");
4426 assert_eq!(t0.id(), tx0.id());
4427 assert_eq!(t1.id(), tx1.id());
4428 assert_eq!(t2.id(), tx2.id());
4429 assert_eq!(t3.id(), tx3.id());
4430 }
4431
4432 #[test]
4433 fn test_non_4844_blob_fee_bit_invariant() {
4434 let mut f = MockTransactionFactory::default();
4435 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4436
4437 let non_4844_tx = MockTransaction::eip1559().set_max_fee(200).inc_limit();
4438 let validated = f.validated(non_4844_tx.clone());
4439
4440 assert!(!non_4844_tx.is_eip4844());
4441 pool.add_transaction(validated.clone(), U256::from(10_000), 0, None).unwrap();
4442
4443 let tx_meta = pool.all_transactions.txs.get(validated.id()).unwrap();
4445 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4446 assert_eq!(tx_meta.subpool, SubPool::Pending);
4447 }
4448
4449 #[test]
4450 fn test_blob_fee_enforcement_only_applies_to_eip4844() {
4451 let mut f = MockTransactionFactory::default();
4452 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4453
4454 let mut block_info = pool.block_info();
4456 block_info.pending_blob_fee = Some(160);
4457 block_info.pending_basefee = 100;
4458 pool.set_block_info(block_info);
4459
4460 let eip4844_tx = MockTransaction::eip4844()
4461 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4462 .with_max_fee(200)
4463 .with_blob_fee(150) .inc_limit();
4465
4466 let non_4844_tx = MockTransaction::eip1559()
4467 .with_sender(address!("0x000000000000000000000000000000000000000b"))
4468 .set_max_fee(200)
4469 .inc_limit();
4470
4471 let validated_4844 = f.validated(eip4844_tx);
4472 let validated_non_4844 = f.validated(non_4844_tx);
4473
4474 pool.add_transaction(validated_4844.clone(), U256::from(10_000), 0, None).unwrap();
4475 pool.add_transaction(validated_non_4844.clone(), U256::from(10_000), 0, None).unwrap();
4476
4477 let tx_4844_meta = pool.all_transactions.txs.get(validated_4844.id()).unwrap();
4478 let tx_non_4844_meta = pool.all_transactions.txs.get(validated_non_4844.id()).unwrap();
4479
4480 assert!(!tx_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4482 assert_eq!(tx_4844_meta.subpool, SubPool::Blob);
4483
4484 assert!(tx_non_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4486 assert_eq!(tx_non_4844_meta.subpool, SubPool::Pending);
4487 }
4488
4489 #[test]
4490 fn test_basefee_decrease_preserves_non_4844_blob_fee_bit() {
4491 let mut f = MockTransactionFactory::default();
4492 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4493
4494 let non_4844_tx = MockTransaction::eip1559()
4496 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4497 .set_max_fee(500) .inc_limit();
4499
4500 pool.update_basefee(600, |_| {});
4502
4503 let validated = f.validated(non_4844_tx);
4504 let tx_id = *validated.id();
4505 pool.add_transaction(validated, U256::from(10_000), 0, None).unwrap();
4506
4507 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4509 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
4510 assert!(
4511 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4512 "Non-4844 tx in BaseFee pool must retain ENOUGH_BLOB_FEE_CAP_BLOCK bit"
4513 );
4514
4515 pool.update_basefee(400, |_| {});
4518
4519 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4521 assert_eq!(
4522 tx_meta.subpool,
4523 SubPool::Pending,
4524 "Non-4844 tx should be promoted from BaseFee to Pending after basefee decrease"
4525 );
4526 assert!(
4527 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4528 "Non-4844 tx must NEVER lose ENOUGH_BLOB_FEE_CAP_BLOCK bit during basefee promotion"
4529 );
4530 assert!(
4531 tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK),
4532 "Non-4844 tx should gain ENOUGH_FEE_CAP_BLOCK bit after basefee decrease"
4533 );
4534 }
4535}