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};
33#[cfg(test)]
34use alloy_primitives::Address;
35use alloy_primitives::{map::AddressSet, TxHash, B256};
36use rustc_hash::FxHashMap;
37use smallvec::SmallVec;
38use std::{
39 cmp::Ordering,
40 collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet},
41 fmt,
42 ops::Bound::{Excluded, Unbounded},
43 sync::Arc,
44};
45use tracing::{trace, warn};
46
47#[cfg_attr(doc, aquamarine::aquamarine)]
48pub struct TxPool<T: TransactionOrdering> {
90 pending_pool: PendingPool<T>,
94 config: PoolConfig,
96 queued_pool: ParkedPool<QueuedOrd<T::Transaction>>,
103 basefee_pool: ParkedPool<BasefeeOrd<T::Transaction>>,
108 blob_pool: BlobTransactions<T::Transaction>,
115 all_transactions: AllTransactions<T::Transaction>,
117 metrics: TxPoolMetrics,
119}
120
121impl<T: TransactionOrdering> TxPool<T> {
124 pub fn new(ordering: T, config: PoolConfig) -> Self {
126 Self {
127 pending_pool: PendingPool::with_buffer(
128 ordering,
129 config.max_new_pending_txs_notifications,
130 ),
131 queued_pool: Default::default(),
132 basefee_pool: Default::default(),
133 blob_pool: Default::default(),
134 all_transactions: AllTransactions::new(&config),
135 config,
136 metrics: Default::default(),
137 }
138 }
139
140 pub fn get_highest_nonce_by_sender(&self, sender: SenderId) -> Option<u64> {
142 self.all().txs_iter(sender).last().map(|(_, tx)| tx.transaction.nonce())
143 }
144
145 pub fn get_highest_transaction_by_sender(
148 &self,
149 sender: SenderId,
150 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
151 self.all().txs_iter(sender).last().map(|(_, tx)| Arc::clone(&tx.transaction))
152 }
153
154 pub(crate) fn get_highest_consecutive_transaction_by_sender(
161 &self,
162 mut on_chain: TransactionId,
163 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
164 let mut last_consecutive_tx = None;
165
166 if let Some(current) = self.all_transactions.sender_info.get(&on_chain.sender) {
168 on_chain.nonce = on_chain.nonce.max(current.state_nonce);
169 }
170
171 let mut next_expected_nonce = on_chain.nonce;
172 for (id, tx) in self.all().descendant_txs_inclusive(&on_chain) {
173 if next_expected_nonce != id.nonce {
174 break
175 }
176 next_expected_nonce = id.next_nonce();
177 last_consecutive_tx = Some(tx);
178 }
179
180 last_consecutive_tx.map(|tx| Arc::clone(&tx.transaction))
181 }
182
183 pub(crate) const fn all(&self) -> &AllTransactions<T::Transaction> {
185 &self.all_transactions
186 }
187
188 pub(crate) fn unique_senders(&self) -> AddressSet {
190 self.all_transactions.txs.values().map(|tx| tx.transaction.sender()).collect()
191 }
192
193 pub fn size(&self) -> PoolSize {
195 PoolSize {
196 pending: self.pending_pool.len(),
197 pending_size: self.pending_pool.size(),
198 basefee: self.basefee_pool.len(),
199 basefee_size: self.basefee_pool.size(),
200 queued: self.queued_pool.len(),
201 queued_size: self.queued_pool.size(),
202 blob: self.blob_pool.len(),
203 blob_size: self.blob_pool.size(),
204 total: self.all_transactions.len(),
205 }
206 }
207
208 pub const fn block_info(&self) -> BlockInfo {
210 BlockInfo {
211 block_gas_limit: self.all_transactions.block_gas_limit,
212 last_seen_block_hash: self.all_transactions.last_seen_block_hash,
213 last_seen_block_number: self.all_transactions.last_seen_block_number,
214 pending_basefee: self.all_transactions.pending_fees.base_fee,
215 pending_blob_fee: Some(self.all_transactions.pending_fees.blob_fee),
216 }
217 }
218
219 fn update_blob_fee<F>(
221 &mut self,
222 mut pending_blob_fee: u128,
223 base_fee_update: Ordering,
224 mut on_promoted: F,
225 ) where
226 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
227 {
228 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut pending_blob_fee);
229 match (self.all_transactions.pending_fees.blob_fee.cmp(&pending_blob_fee), base_fee_update)
230 {
231 (Ordering::Equal, Ordering::Equal | Ordering::Greater) => {
232 }
234 (Ordering::Greater, Ordering::Equal | Ordering::Greater) => {
235 let removed =
237 self.pending_pool.update_blob_fee(self.all_transactions.pending_fees.blob_fee);
238 for tx in removed {
239 let to = {
240 let tx =
241 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
242
243 tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
245 tx.subpool = tx.state.into();
246 tx.subpool
247 };
248 self.add_transaction_to_subpool(to, tx);
249 }
250 }
251 (Ordering::Less, _) | (_, Ordering::Less) => {
252 let removed =
254 self.blob_pool.enforce_pending_fees(&self.all_transactions.pending_fees);
255 for tx in removed {
256 let subpool = {
257 let tx_meta =
258 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
259 tx_meta.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
260 tx_meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
261 tx_meta.subpool = tx_meta.state.into();
262 tx_meta.subpool
263 };
264
265 if subpool == SubPool::Pending {
266 on_promoted(&tx);
267 }
268
269 self.add_transaction_to_subpool(subpool, tx);
270 }
271 }
272 }
273 }
274
275 fn update_basefee<F>(&mut self, mut pending_basefee: u64, mut on_promoted: F) -> Ordering
280 where
281 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
282 {
283 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut pending_basefee);
284 match self.all_transactions.pending_fees.base_fee.cmp(&pending_basefee) {
285 Ordering::Equal => {
286 Ordering::Equal
288 }
289 Ordering::Greater => {
290 let removed =
292 self.pending_pool.update_base_fee(self.all_transactions.pending_fees.base_fee);
293 for tx in removed {
294 let to = {
295 let tx =
296 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
297 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
298 tx.subpool = tx.state.into();
299 tx.subpool
300 };
301 self.add_transaction_to_subpool(to, tx);
302 }
303
304 Ordering::Greater
305 }
306 Ordering::Less => {
307 let current_base_fee = self.all_transactions.pending_fees.base_fee;
316 self.basefee_pool.enforce_basefee_with(current_base_fee, |tx| {
317 let subpool = {
319 let meta =
320 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
321 meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
322 meta.subpool = meta.state.into();
323 meta.subpool
324 };
325
326 if subpool == SubPool::Pending {
327 on_promoted(&tx);
328 }
329
330 trace!(target: "txpool", hash=%tx.transaction.hash(), pool=?subpool, "Adding transaction to a subpool");
331 match subpool {
332 SubPool::Queued => self.queued_pool.add_transaction(tx),
333 SubPool::Pending => {
334 self.pending_pool.add_transaction(tx, current_base_fee);
335 }
336 SubPool::Blob => {
337 self.blob_pool.add_transaction(tx);
338 }
339 SubPool::BaseFee => {
340 warn!(target: "txpool", "BaseFee transactions should become Pending after basefee decrease");
343 }
344 }
345 });
346
347 Ordering::Less
348 }
349 }
350 }
351
352 pub fn set_block_info(&mut self, info: BlockInfo) {
356 let basefee_ordering = self.update_basefee(info.pending_basefee, |_| {});
358 if let Some(blob_fee) = info.pending_blob_fee {
359 self.update_blob_fee(blob_fee, basefee_ordering, |_| {})
360 }
361 self.all_transactions.set_block_info(info);
363 }
364
365 pub(crate) fn best_transactions(&self) -> BestTransactions<T> {
368 self.pending_pool.best()
369 }
370
371 pub(crate) fn best_transactions_with_attributes(
378 &self,
379 best_transactions_attributes: BestTransactionsAttributes,
380 ) -> Box<dyn crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T::Transaction>>>>
381 {
382 match best_transactions_attributes.basefee.cmp(&self.all_transactions.pending_fees.base_fee)
385 {
386 Ordering::Equal => {
387 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
391 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
392 Ordering::Less => {
393 let unlocked =
395 self.blob_pool.satisfy_attributes(best_transactions_attributes);
396 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
397 unlocked,
398 best_transactions_attributes.basefee,
399 new_blob_fee,
400 ))
401 }
402 Ordering::Equal => Box::new(self.pending_pool.best()),
403 Ordering::Greater => {
404 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
406 best_transactions_attributes.basefee,
407 best_transactions_attributes.blob_fee.unwrap_or_default(),
408 ))
409 }
410 }
411 }
412 Ordering::Greater => {
413 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
415 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
416 Ordering::Less => {
417 let unlocked =
419 self.blob_pool.satisfy_attributes(best_transactions_attributes);
420 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
421 unlocked,
422 best_transactions_attributes.basefee,
423 new_blob_fee,
424 ))
425 }
426 Ordering::Equal | Ordering::Greater => {
427 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
429 best_transactions_attributes.basefee,
430 new_blob_fee,
431 ))
432 }
433 }
434 }
435 Ordering::Less => {
436 let mut unlocked = self
439 .basefee_pool
440 .satisfy_base_fee_transactions(best_transactions_attributes.basefee);
441
442 unlocked.extend(self.blob_pool.satisfy_attributes(best_transactions_attributes));
444
445 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
446 unlocked,
447 best_transactions_attributes.basefee,
448 best_transactions_attributes.blob_fee.unwrap_or_default(),
449 ))
450 }
451 }
452 }
453
454 pub(crate) fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
456 self.pending_pool.all().collect()
457 }
458 pub(crate) fn pending_transactions_iter(
460 &self,
461 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
462 self.pending_pool.all()
463 }
464
465 pub(crate) fn pending_transactions_count(&self) -> usize {
467 self.pending_pool.len()
468 }
469
470 pub(crate) fn pending_transactions_with_predicate(
472 &self,
473 mut predicate: impl FnMut(&ValidPoolTransaction<T::Transaction>) -> bool,
474 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
475 self.pending_transactions_iter().filter(|tx| predicate(tx)).collect()
476 }
477
478 pub(crate) fn pending_txs_by_sender(
480 &self,
481 sender: SenderId,
482 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
483 self.pending_transactions_iter().filter(|tx| tx.sender_id() == sender).collect()
484 }
485
486 pub(crate) fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
488 self.basefee_pool.all().chain(self.queued_pool.all()).collect()
489 }
490
491 pub(crate) fn queued_transactions_iter(
493 &self,
494 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
495 self.basefee_pool.all().chain(self.queued_pool.all())
496 }
497
498 pub(crate) fn queued_transactions_count(&self) -> usize {
500 self.basefee_pool.len() + self.queued_pool.len()
501 }
502
503 pub fn queued_and_pending_txs_by_sender(
505 &self,
506 sender: SenderId,
507 ) -> (SmallVec<[TransactionId; TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER]>, Vec<TransactionId>) {
508 (self.queued_pool.get_txs_by_sender(sender), self.pending_pool.get_txs_by_sender(sender))
509 }
510
511 pub(crate) fn queued_txs_by_sender(
513 &self,
514 sender: SenderId,
515 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
516 self.queued_transactions_iter().filter(|tx| tx.sender_id() == sender).collect()
517 }
518
519 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
521 self.all_transactions.contains(tx_hash)
522 }
523
524 #[cfg(test)]
526 pub(crate) fn subpool_contains(&self, subpool: SubPool, id: &TransactionId) -> bool {
527 match subpool {
528 SubPool::Queued => self.queued_pool.contains(id),
529 SubPool::Pending => self.pending_pool.contains(id),
530 SubPool::BaseFee => self.basefee_pool.contains(id),
531 SubPool::Blob => self.blob_pool.contains(id),
532 }
533 }
534
535 #[inline]
537 pub(crate) fn is_exceeded(&self) -> bool {
538 self.config.is_exceeded(self.size())
539 }
540
541 pub(crate) fn get(
543 &self,
544 tx_hash: &TxHash,
545 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
546 self.all_transactions.by_hash.get(tx_hash).cloned()
547 }
548
549 pub(crate) fn get_all(
551 &self,
552 txs: Vec<TxHash>,
553 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
554 txs.into_iter().filter_map(|tx| self.get(&tx))
555 }
556
557 pub(crate) fn get_transactions_by_sender(
559 &self,
560 sender: SenderId,
561 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
562 self.all_transactions.txs_iter(sender).map(|(_, tx)| Arc::clone(&tx.transaction)).collect()
563 }
564
565 pub(crate) fn get_pending_transaction_by_sender_and_nonce(
567 &self,
568 sender: SenderId,
569 nonce: u64,
570 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
571 self.all_transactions
572 .txs_iter(sender)
573 .find(|(id, tx)| id.nonce == nonce && tx.subpool == SubPool::Pending)
574 .map(|(_, tx)| Arc::clone(&tx.transaction))
575 }
576
577 const fn update_pending_fees_only(
580 &mut self,
581 mut new_base_fee: u64,
582 new_blob_fee: Option<u128>,
583 ) -> (u64, u128) {
584 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut new_base_fee);
585
586 let prev_blob_fee = if let Some(mut blob_fee) = new_blob_fee {
587 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut blob_fee);
588 blob_fee
589 } else {
590 self.all_transactions.pending_fees.blob_fee
591 };
592
593 (new_base_fee, prev_blob_fee)
594 }
595
596 fn apply_fee_updates(
603 &mut self,
604 prev_base_fee: u64,
605 prev_blob_fee: u128,
606 outcome: &mut UpdateOutcome<T::Transaction>,
607 ) {
608 let new_base_fee = self.all_transactions.pending_fees.base_fee;
609 let new_blob_fee = self.all_transactions.pending_fees.blob_fee;
610
611 if new_base_fee == prev_base_fee && new_blob_fee == prev_blob_fee {
612 return;
614 }
615
616 self.all_transactions.pending_fees.base_fee = prev_base_fee;
619 self.all_transactions.pending_fees.blob_fee = prev_blob_fee;
620
621 let base_fee_ordering = self.update_basefee(new_base_fee, |tx| {
622 outcome.promoted.push(tx.clone());
623 });
624
625 self.update_blob_fee(new_blob_fee, base_fee_ordering, |tx| {
626 outcome.promoted.push(tx.clone());
627 });
628 }
629
630 pub(crate) fn update_accounts(
632 &mut self,
633 changed_senders: FxHashMap<SenderId, SenderInfo>,
634 ) -> UpdateOutcome<T::Transaction> {
635 let updates = self.all_transactions.update(&changed_senders);
637
638 self.all_transactions.sender_info.extend(changed_senders);
640
641 let update = self.process_updates(updates);
643 self.update_size_metrics();
645 update
646 }
647
648 pub(crate) fn on_canonical_state_change(
653 &mut self,
654 block_info: BlockInfo,
655 mined_transactions: Vec<TxHash>,
656 changed_senders: FxHashMap<SenderId, SenderInfo>,
657 _update_kind: PoolUpdateKind,
658 ) -> OnNewCanonicalStateOutcome<T::Transaction> {
659 let block_hash = block_info.last_seen_block_hash;
661
662 let mut removed_txs_count = 0;
664 for tx_hash in &mined_transactions {
665 if self.prune_transaction_by_hash(tx_hash).is_some() {
666 removed_txs_count += 1;
667 }
668 }
669
670 self.metrics.removed_transactions.increment(removed_txs_count);
672
673 let (prev_base_fee, prev_blob_fee) =
678 self.update_pending_fees_only(block_info.pending_basefee, block_info.pending_blob_fee);
679
680 let mut outcome = self.update_accounts(changed_senders);
682
683 self.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
686
687 self.all_transactions.set_block_info(block_info);
689
690 self.update_transaction_type_metrics();
691 self.metrics.performed_state_updates.increment(1);
692
693 OnNewCanonicalStateOutcome {
694 block_hash,
695 mined: mined_transactions,
696 promoted: outcome.promoted,
697 discarded: outcome.discarded,
698 }
699 }
700
701 pub(crate) fn update_size_metrics(&self) {
703 let stats = self.size();
704 self.metrics.pending_pool_transactions.set(stats.pending as f64);
705 self.metrics.pending_pool_size_bytes.set(stats.pending_size as f64);
706 self.metrics.basefee_pool_transactions.set(stats.basefee as f64);
707 self.metrics.basefee_pool_size_bytes.set(stats.basefee_size as f64);
708 self.metrics.queued_pool_transactions.set(stats.queued as f64);
709 self.metrics.queued_pool_size_bytes.set(stats.queued_size as f64);
710 self.metrics.blob_pool_transactions.set(stats.blob as f64);
711 self.metrics.blob_pool_size_bytes.set(stats.blob_size as f64);
712 self.metrics.total_transactions.set(stats.total as f64);
713 }
714
715 pub(crate) fn update_transaction_type_metrics(&self) {
717 let mut legacy_count = 0;
718 let mut eip2930_count = 0;
719 let mut eip1559_count = 0;
720 let mut eip4844_count = 0;
721 let mut eip7702_count = 0;
722 let mut other_count = 0;
723
724 for tx in self.all_transactions.transactions_iter() {
725 match tx.transaction.ty() {
726 LEGACY_TX_TYPE_ID => legacy_count += 1,
727 EIP2930_TX_TYPE_ID => eip2930_count += 1,
728 EIP1559_TX_TYPE_ID => eip1559_count += 1,
729 EIP4844_TX_TYPE_ID => eip4844_count += 1,
730 EIP7702_TX_TYPE_ID => eip7702_count += 1,
731 _ => other_count += 1,
732 }
733 }
734
735 self.metrics.total_legacy_transactions.set(legacy_count as f64);
736 self.metrics.total_eip2930_transactions.set(eip2930_count as f64);
737 self.metrics.total_eip1559_transactions.set(eip1559_count as f64);
738 self.metrics.total_eip4844_transactions.set(eip4844_count as f64);
739 self.metrics.total_eip7702_transactions.set(eip7702_count as f64);
740 self.metrics.total_other_transactions.set(other_count as f64);
741 }
742
743 pub(crate) fn add_transaction(
744 &mut self,
745 tx: ValidPoolTransaction<T::Transaction>,
746 on_chain_balance: U256,
747 on_chain_nonce: u64,
748 on_chain_code_hash: Option<B256>,
749 ) -> PoolResult<AddedTransaction<T::Transaction>> {
750 if self.contains(tx.hash()) {
751 return Err(PoolError::new(*tx.hash(), PoolErrorKind::AlreadyImported))
752 }
753
754 self.validate_auth(&tx, on_chain_nonce, on_chain_code_hash)?;
755
756 self.all_transactions
758 .sender_info
759 .entry(tx.sender_id())
760 .or_default()
761 .update(on_chain_nonce, on_chain_balance);
762
763 match self.all_transactions.insert_tx(tx, on_chain_balance, on_chain_nonce) {
764 Ok(InsertOk { transaction, move_to, replaced_tx, updates, state }) => {
765 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
767 self.metrics.inserted_transactions.increment(1);
769 let UpdateOutcome { promoted, discarded } = self.process_updates(updates);
770
771 let replaced = replaced_tx.map(|(tx, _)| tx);
772
773 let res = if move_to.is_pending() {
775 AddedTransaction::Pending(AddedPendingTransaction {
776 transaction,
777 promoted,
778 discarded,
779 replaced,
780 })
781 } else {
782 let queued_reason = state.determine_queued_reason(move_to);
784 AddedTransaction::Parked {
785 transaction,
786 subpool: move_to,
787 replaced,
788 queued_reason,
789 }
790 };
791
792 self.update_size_metrics();
794
795 Ok(res)
796 }
797 Err(err) => {
798 self.metrics.invalid_transactions.increment(1);
800 match err {
801 InsertErr::Underpriced { existing: _, transaction } => Err(PoolError::new(
802 *transaction.hash(),
803 PoolErrorKind::ReplacementUnderpriced,
804 )),
805 InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap } => {
806 Err(PoolError::new(
807 *transaction.hash(),
808 PoolErrorKind::FeeCapBelowMinimumProtocolFeeCap(fee_cap),
809 ))
810 }
811 InsertErr::ExceededSenderTransactionsCapacity { transaction } => {
812 Err(PoolError::new(
813 *transaction.hash(),
814 PoolErrorKind::SpammerExceededCapacity(transaction.sender()),
815 ))
816 }
817 InsertErr::TxGasLimitMoreThanAvailableBlockGas {
818 transaction,
819 block_gas_limit,
820 tx_gas_limit,
821 } => Err(PoolError::new(
822 *transaction.hash(),
823 PoolErrorKind::InvalidTransaction(
824 InvalidPoolTransactionError::ExceedsGasLimit(
825 tx_gas_limit,
826 block_gas_limit,
827 ),
828 ),
829 )),
830 InsertErr::BlobTxHasNonceGap { transaction } => Err(PoolError::new(
831 *transaction.hash(),
832 PoolErrorKind::InvalidTransaction(
833 Eip4844PoolTransactionError::Eip4844NonceGap.into(),
834 ),
835 )),
836 InsertErr::Overdraft { transaction } => Err(PoolError::new(
837 *transaction.hash(),
838 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Overdraft {
839 cost: *transaction.cost(),
840 balance: on_chain_balance,
841 }),
842 )),
843 InsertErr::TxTypeConflict { transaction } => Err(PoolError::new(
844 *transaction.hash(),
845 PoolErrorKind::ExistingConflictingTransactionType(
846 transaction.sender(),
847 transaction.tx_type(),
848 ),
849 )),
850 }
851 }
852 }
853 }
854
855 fn check_delegation_limit(
859 &self,
860 transaction: &ValidPoolTransaction<T::Transaction>,
861 on_chain_nonce: u64,
862 on_chain_code_hash: Option<B256>,
863 ) -> Result<(), PoolError> {
864 if (on_chain_code_hash.is_none() || on_chain_code_hash == Some(KECCAK_EMPTY)) &&
866 !self.all_transactions.auths.contains_key(&transaction.sender_id())
867 {
868 return Ok(())
869 }
870
871 let mut txs_by_sender =
872 self.pending_pool.iter_txs_by_sender(transaction.sender_id()).peekable();
873
874 if txs_by_sender.peek().is_none() {
875 let nonce_gap_distance = transaction.nonce().saturating_sub(on_chain_nonce);
880 if nonce_gap_distance >= self.config.max_inflight_delegated_slot_limit as u64 {
881 return Err(PoolError::new(
882 *transaction.hash(),
883 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
884 Eip7702PoolTransactionError::OutOfOrderTxFromDelegated,
885 )),
886 ))
887 }
888 return Ok(())
889 }
890
891 let mut count = 0;
892 for id in txs_by_sender {
893 if id == &transaction.transaction_id {
894 return Ok(())
896 }
897 count += 1;
898 }
899
900 if count < self.config.max_inflight_delegated_slot_limit {
901 return Ok(())
903 }
904
905 Err(PoolError::new(
906 *transaction.hash(),
907 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
908 Eip7702PoolTransactionError::InflightTxLimitReached,
909 )),
910 ))
911 }
912
913 fn validate_auth(
923 &self,
924 transaction: &ValidPoolTransaction<T::Transaction>,
925 on_chain_nonce: u64,
926 on_chain_code_hash: Option<B256>,
927 ) -> Result<(), PoolError> {
928 self.check_delegation_limit(transaction, on_chain_nonce, on_chain_code_hash)?;
930
931 if let Some(authority_list) = &transaction.authority_ids {
932 for sender_id in authority_list {
933 if self.all_transactions.txs_iter(*sender_id).nth(1).is_some() {
935 return Err(PoolError::new(
936 *transaction.hash(),
937 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
938 Eip7702PoolTransactionError::AuthorityReserved,
939 )),
940 ))
941 }
942 }
943 }
944
945 Ok(())
946 }
947
948 fn process_updates(&mut self, updates: Vec<PoolUpdate>) -> UpdateOutcome<T::Transaction> {
952 let mut outcome = UpdateOutcome::default();
953 let mut removed = 0;
954 for PoolUpdate { id, current, destination } in updates {
955 match destination {
956 Destination::Discard => {
957 if let Some(tx) = self.prune_transaction_by_id(&id) {
959 outcome.discarded.push(tx);
960 }
961 removed += 1;
962 }
963 Destination::Pool(move_to) => {
964 debug_assert_ne!(&move_to, ¤t, "destination must be different");
965 let moved = self.move_transaction(current, move_to, &id);
966 if matches!(move_to, SubPool::Pending) &&
967 let Some(tx) = moved
968 {
969 trace!(target: "txpool", hash=%tx.transaction.hash(), "Promoted transaction to pending");
970 outcome.promoted.push(tx);
971 }
972 }
973 }
974 }
975
976 if removed > 0 {
977 self.metrics.removed_transactions.increment(removed);
978 }
979
980 outcome
981 }
982
983 fn move_transaction(
988 &mut self,
989 from: SubPool,
990 to: SubPool,
991 id: &TransactionId,
992 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
993 let tx = self.remove_from_subpool(from, id)?;
994 self.add_transaction_to_subpool(to, tx.clone());
995 Some(tx)
996 }
997
998 pub(crate) fn remove_transactions(
1003 &mut self,
1004 hashes: Vec<TxHash>,
1005 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1006 let txs =
1007 hashes.into_iter().filter_map(|hash| self.remove_transaction_by_hash(&hash)).collect();
1008 self.update_size_metrics();
1009 txs
1010 }
1011
1012 pub(crate) fn remove_transactions_and_descendants(
1014 &mut self,
1015 hashes: Vec<TxHash>,
1016 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1017 let mut removed = Vec::new();
1018 for hash in hashes {
1019 if let Some(tx) = self.remove_transaction_by_hash(&hash) {
1020 removed.push(tx.clone());
1021 self.remove_descendants(tx.id(), &mut removed);
1022 }
1023 }
1024 self.update_size_metrics();
1025 removed
1026 }
1027
1028 pub(crate) fn remove_transactions_by_sender(
1030 &mut self,
1031 sender_id: SenderId,
1032 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1033 let mut removed = Vec::new();
1034 let txs = self.get_transactions_by_sender(sender_id);
1035 for tx in txs {
1036 if let Some(tx) = self.remove_transaction(tx.id()) {
1037 removed.push(tx);
1038 }
1039 }
1040 self.update_size_metrics();
1041 removed
1042 }
1043
1044 pub(crate) fn prune_transactions(
1050 &mut self,
1051 hashes: Vec<TxHash>,
1052 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1053 let txs =
1054 hashes.into_iter().filter_map(|hash| self.prune_transaction_by_hash(&hash)).collect();
1055 self.update_size_metrics();
1056 txs
1057 }
1058
1059 fn remove_transaction(
1063 &mut self,
1064 id: &TransactionId,
1065 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1066 let (tx, pool) = self.all_transactions.remove_transaction(id)?;
1067 self.remove_from_subpool(pool, tx.id())
1068 }
1069
1070 fn remove_transaction_by_hash(
1076 &mut self,
1077 tx_hash: &B256,
1078 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1079 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1080
1081 let updates = self.all_transactions.park_descendant_transactions(tx.id());
1083 self.process_updates(updates);
1084 self.remove_from_subpool(pool, tx.id())
1085 }
1086
1087 fn prune_transaction_by_hash(
1094 &mut self,
1095 tx_hash: &B256,
1096 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1097 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1098 self.remove_from_subpool(pool, tx.id())
1099 }
1100 fn prune_transaction_by_id(
1105 &mut self,
1106 tx_id: &TransactionId,
1107 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1108 let (tx, pool) = self.all_transactions.remove_transaction_by_id(tx_id)?;
1109 self.remove_from_subpool(pool, tx.id())
1110 }
1111
1112 fn remove_from_subpool(
1116 &mut self,
1117 pool: SubPool,
1118 tx: &TransactionId,
1119 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1120 let tx = match pool {
1121 SubPool::Queued => self.queued_pool.remove_transaction(tx),
1122 SubPool::Pending => self.pending_pool.remove_transaction(tx),
1123 SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
1124 SubPool::Blob => self.blob_pool.remove_transaction(tx),
1125 };
1126
1127 if let Some(ref tx) = tx {
1128 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Removed transaction from a subpool");
1132 }
1133
1134 tx
1135 }
1136
1137 fn remove_descendants(
1141 &mut self,
1142 tx: &TransactionId,
1143 removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
1144 ) {
1145 let mut id = *tx;
1146
1147 loop {
1149 let descendant =
1150 self.all_transactions.descendant_txs_exclusive(&id).map(|(id, _)| *id).next();
1151 if let Some(descendant) = descendant {
1152 if let Some(tx) = self.remove_transaction(&descendant) {
1153 removed.push(tx)
1154 }
1155 id = descendant;
1156 } else {
1157 return
1158 }
1159 }
1160 }
1161
1162 fn add_transaction_to_subpool(
1164 &mut self,
1165 pool: SubPool,
1166 tx: Arc<ValidPoolTransaction<T::Transaction>>,
1167 ) {
1168 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Adding transaction to a subpool");
1172 match pool {
1173 SubPool::Queued => self.queued_pool.add_transaction(tx),
1174 SubPool::Pending => {
1175 self.pending_pool.add_transaction(tx, self.all_transactions.pending_fees.base_fee);
1176 }
1177 SubPool::BaseFee => {
1178 self.basefee_pool.add_transaction(tx);
1179 }
1180 SubPool::Blob => {
1181 self.blob_pool.add_transaction(tx);
1182 }
1183 }
1184 }
1185
1186 fn add_new_transaction(
1189 &mut self,
1190 transaction: Arc<ValidPoolTransaction<T::Transaction>>,
1191 replaced: Option<(Arc<ValidPoolTransaction<T::Transaction>>, SubPool)>,
1192 pool: SubPool,
1193 ) {
1194 if let Some((replaced, replaced_pool)) = replaced {
1195 self.remove_from_subpool(replaced_pool, replaced.id());
1197 }
1198
1199 self.add_transaction_to_subpool(pool, transaction)
1200 }
1201
1202 pub(crate) fn discard_worst(&mut self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1209 let mut removed = Vec::new();
1210
1211 macro_rules! discard_worst {
1213 ($this:ident, $removed:ident, [$($limit:ident => ($pool:ident, $metric:ident)),* $(,)*]) => {
1214 $ (
1215 while $this.$pool.exceeds(&$this.config.$limit)
1216 {
1217 trace!(
1218 target: "txpool",
1219 "discarding transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1220 stringify!($pool),
1221 $this.config.$limit,
1222 $this.$pool.size(),
1223 $this.$pool.len(),
1224 );
1225
1226 let removed_from_subpool = $this.$pool.truncate_pool($this.config.$limit.clone());
1228
1229 trace!(
1230 target: "txpool",
1231 "removed {} transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1232 removed_from_subpool.len(),
1233 stringify!($pool),
1234 $this.config.$limit,
1235 $this.$pool.size(),
1236 $this.$pool.len()
1237 );
1238 $this.metrics.$metric.increment(removed_from_subpool.len() as u64);
1239
1240 for tx in removed_from_subpool {
1242 $this.all_transactions.remove_transaction(tx.id());
1243
1244 let id = *tx.id();
1245
1246 removed.push(tx);
1248
1249 $this.remove_descendants(&id, &mut $removed);
1251 }
1252 }
1253
1254 )*
1255 };
1256 }
1257
1258 discard_worst!(
1259 self, removed, [
1260 pending_limit => (pending_pool, pending_transactions_evicted),
1261 basefee_limit => (basefee_pool, basefee_transactions_evicted),
1262 blob_limit => (blob_pool, blob_transactions_evicted),
1263 queued_limit => (queued_pool, queued_transactions_evicted),
1264 ]
1265 );
1266
1267 removed
1268 }
1269
1270 pub(crate) fn len(&self) -> usize {
1272 self.all_transactions.len()
1273 }
1274
1275 pub(crate) fn is_empty(&self) -> bool {
1277 self.all_transactions.is_empty()
1278 }
1279
1280 #[cfg(any(test, feature = "test-utils"))]
1288 pub fn assert_invariants(&self) {
1289 let size = self.size();
1290 let actual = size.basefee + size.pending + size.queued + size.blob;
1291 assert_eq!(
1292 size.total, actual,
1293 "total size must be equal to the sum of all sub-pools, basefee:{}, pending:{}, queued:{}, blob:{}",
1294 size.basefee, size.pending, size.queued, size.blob
1295 );
1296 self.all_transactions.assert_invariants();
1297 self.pending_pool.assert_invariants();
1298 self.basefee_pool.assert_invariants();
1299 self.queued_pool.assert_invariants();
1300 self.blob_pool.assert_invariants();
1301 }
1302}
1303
1304#[cfg(any(test, feature = "test-utils"))]
1305impl TxPool<crate::test_utils::MockOrdering> {
1306 pub fn mock() -> Self {
1308 Self::new(crate::test_utils::MockOrdering::default(), PoolConfig::default())
1309 }
1310}
1311
1312#[cfg(test)]
1313impl<T: TransactionOrdering> Drop for TxPool<T> {
1314 fn drop(&mut self) {
1315 self.assert_invariants();
1316 }
1317}
1318
1319impl<T: TransactionOrdering> TxPool<T> {
1320 pub const fn pending(&self) -> &PendingPool<T> {
1322 &self.pending_pool
1323 }
1324
1325 pub const fn base_fee(&self) -> &ParkedPool<BasefeeOrd<T::Transaction>> {
1327 &self.basefee_pool
1328 }
1329
1330 pub const fn queued(&self) -> &ParkedPool<QueuedOrd<T::Transaction>> {
1332 &self.queued_pool
1333 }
1334}
1335
1336impl<T: TransactionOrdering> fmt::Debug for TxPool<T> {
1337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1338 f.debug_struct("TxPool").field("config", &self.config).finish_non_exhaustive()
1339 }
1340}
1341
1342pub(crate) struct AllTransactions<T: PoolTransaction> {
1347 minimal_protocol_basefee: u64,
1351 block_gas_limit: u64,
1353 max_account_slots: usize,
1355 by_hash: HashMap<TxHash, Arc<ValidPoolTransaction<T>>>,
1357 txs: BTreeMap<TransactionId, PoolInternalTransaction<T>>,
1359 sender_info: FxHashMap<SenderId, SenderInfo>,
1361 tx_counter: FxHashMap<SenderId, usize>,
1363 last_seen_block_number: u64,
1365 last_seen_block_hash: B256,
1367 pending_fees: PendingFees,
1369 price_bumps: PriceBumpConfig,
1371 local_transactions_config: LocalTransactionConfig,
1373 auths: FxHashMap<SenderId, HashSet<TxHash>>,
1375 metrics: AllTransactionsMetrics,
1377}
1378
1379impl<T: PoolTransaction> AllTransactions<T> {
1380 fn new(config: &PoolConfig) -> Self {
1382 Self {
1383 max_account_slots: config.max_account_slots,
1384 price_bumps: config.price_bumps,
1385 local_transactions_config: config.local_transactions_config.clone(),
1386 minimal_protocol_basefee: config.minimal_protocol_basefee,
1387 block_gas_limit: config.gas_limit,
1388 ..Default::default()
1389 }
1390 }
1391
1392 #[expect(dead_code)]
1394 pub(crate) fn hashes_iter(&self) -> impl Iterator<Item = TxHash> + '_ {
1395 self.by_hash.keys().copied()
1396 }
1397
1398 pub(crate) fn transactions_iter(
1400 &self,
1401 ) -> impl Iterator<Item = &Arc<ValidPoolTransaction<T>>> + '_ {
1402 self.by_hash.values()
1403 }
1404
1405 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
1407 self.by_hash.contains_key(tx_hash)
1408 }
1409
1410 pub(crate) fn get(&self, id: &TransactionId) -> Option<&PoolInternalTransaction<T>> {
1412 self.txs.get(id)
1413 }
1414
1415 pub(crate) fn tx_inc(&mut self, sender: SenderId) {
1417 let count = self.tx_counter.entry(sender).or_default();
1418 *count += 1;
1419 self.metrics.all_transactions_by_all_senders.increment(1.0);
1420 }
1421
1422 pub(crate) fn tx_decr(&mut self, sender: SenderId) {
1424 if let hash_map::Entry::Occupied(mut entry) = self.tx_counter.entry(sender) {
1425 let count = entry.get_mut();
1426 if *count == 1 {
1427 entry.remove();
1428 self.sender_info.remove(&sender);
1429 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1430 return
1431 }
1432 *count -= 1;
1433 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1434 }
1435 }
1436
1437 fn set_block_info(&mut self, block_info: BlockInfo) {
1439 let BlockInfo {
1440 block_gas_limit,
1441 last_seen_block_hash,
1442 last_seen_block_number,
1443 pending_basefee,
1444 pending_blob_fee,
1445 } = block_info;
1446 self.last_seen_block_number = last_seen_block_number;
1447 self.last_seen_block_hash = last_seen_block_hash;
1448
1449 self.pending_fees.base_fee = pending_basefee;
1450 self.metrics.base_fee.set(pending_basefee as f64);
1451
1452 self.block_gas_limit = block_gas_limit;
1453
1454 if let Some(pending_blob_fee) = pending_blob_fee {
1455 self.pending_fees.blob_fee = pending_blob_fee;
1456 self.metrics.blob_base_fee.set(pending_blob_fee as f64);
1457 }
1458 }
1459
1460 pub(crate) fn update_size_metrics(&self) {
1462 self.metrics.all_transactions_by_hash.set(self.by_hash.len() as f64);
1463 self.metrics.all_transactions_by_id.set(self.txs.len() as f64);
1464 }
1465
1466 pub(crate) fn update(
1483 &mut self,
1484 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1485 ) -> Vec<PoolUpdate> {
1486 let mut updates = Vec::with_capacity(64);
1488
1489 let mut iter = self.txs.iter_mut().peekable();
1490
1491 'transactions: while let Some((id, tx)) = iter.next() {
1501 macro_rules! next_sender {
1502 ($iter:ident) => {
1503 'this: while let Some((peek, _)) = iter.peek() {
1504 if peek.sender != id.sender {
1505 break 'this
1506 }
1507 iter.next();
1508 }
1509 };
1510 }
1511
1512 let changed_balance = if let Some(info) = changed_accounts.get(&id.sender) {
1515 if id.nonce < info.state_nonce {
1517 updates.push(PoolUpdate {
1518 id: *tx.transaction.id(),
1519 current: tx.subpool,
1520 destination: Destination::Discard,
1521 });
1522 continue 'transactions
1523 }
1524
1525 let ancestor = TransactionId::ancestor(id.nonce, info.state_nonce, id.sender);
1526 if ancestor.is_none() {
1528 tx.state.insert(TxState::NO_NONCE_GAPS);
1529 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1530 tx.cumulative_cost = U256::ZERO;
1531 if tx.transaction.cost() > &info.balance {
1532 tx.state.remove(TxState::ENOUGH_BALANCE);
1534 } else {
1535 tx.state.insert(TxState::ENOUGH_BALANCE);
1536 }
1537 }
1538
1539 Some(&info.balance)
1540 } else {
1541 None
1542 };
1543
1544 if tx.state.has_nonce_gap() {
1546 next_sender!(iter);
1547 continue 'transactions
1548 }
1549
1550 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1552
1553 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1555 Self::record_subpool_update(&mut updates, tx);
1557
1558 let mut has_parked_ancestor = !tx.state.is_pending();
1560
1561 let mut cumulative_cost = tx.next_cumulative_cost();
1562
1563 let mut next_nonce_in_line = tx.transaction.nonce().saturating_add(1);
1565
1566 while let Some((peek, tx)) = iter.peek_mut() {
1568 if peek.sender != id.sender {
1569 continue 'transactions
1571 }
1572
1573 if tx.transaction.nonce() == next_nonce_in_line {
1574 tx.state.insert(TxState::NO_NONCE_GAPS);
1576 } else {
1577 next_sender!(iter);
1579 continue 'transactions
1580 }
1581
1582 next_nonce_in_line = next_nonce_in_line.saturating_add(1);
1584
1585 tx.cumulative_cost = cumulative_cost;
1587 cumulative_cost = tx.next_cumulative_cost();
1589
1590 if let Some(changed_balance) = changed_balance {
1592 if &cumulative_cost > changed_balance {
1593 tx.state.remove(TxState::ENOUGH_BALANCE);
1595 } else {
1596 tx.state.insert(TxState::ENOUGH_BALANCE);
1597 }
1598 }
1599
1600 if has_parked_ancestor {
1602 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1603 } else {
1604 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1605 }
1606 has_parked_ancestor = !tx.state.is_pending();
1607
1608 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1610 Self::record_subpool_update(&mut updates, tx);
1611
1612 iter.next();
1614 }
1615 }
1616
1617 updates
1618 }
1619
1620 fn record_subpool_update(updates: &mut Vec<PoolUpdate>, tx: &mut PoolInternalTransaction<T>) {
1625 let current_pool = tx.subpool;
1626 tx.subpool = tx.state.into();
1627 if current_pool != tx.subpool {
1628 updates.push(PoolUpdate {
1629 id: *tx.transaction.id(),
1630 current: current_pool,
1631 destination: tx.subpool.into(),
1632 })
1633 }
1634 }
1635
1636 fn update_tx_base_fee(pending_block_base_fee: u64, tx: &mut PoolInternalTransaction<T>) {
1638 match tx.transaction.max_fee_per_gas().cmp(&(pending_block_base_fee as u128)) {
1640 Ordering::Greater | Ordering::Equal => {
1641 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1642 }
1643 Ordering::Less => {
1644 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
1645 }
1646 }
1647 }
1648
1649 pub(crate) fn txs_iter(
1652 &self,
1653 sender: SenderId,
1654 ) -> impl Iterator<Item = (&TransactionId, &PoolInternalTransaction<T>)> + '_ {
1655 self.txs
1656 .range((sender.start_bound(), Unbounded))
1657 .take_while(move |(other, _)| sender == other.sender)
1658 }
1659
1660 #[cfg(test)]
1663 #[expect(dead_code)]
1664 pub(crate) fn txs_iter_mut(
1665 &mut self,
1666 sender: SenderId,
1667 ) -> impl Iterator<Item = (&TransactionId, &mut PoolInternalTransaction<T>)> + '_ {
1668 self.txs
1669 .range_mut((sender.start_bound(), Unbounded))
1670 .take_while(move |(other, _)| sender == other.sender)
1671 }
1672
1673 pub(crate) fn descendant_txs_exclusive<'a, 'b: 'a>(
1677 &'a self,
1678 id: &'b TransactionId,
1679 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1680 self.txs.range((Excluded(id), Unbounded)).take_while(|(other, _)| id.sender == other.sender)
1681 }
1682
1683 pub(crate) fn descendant_txs_inclusive<'a, 'b: 'a>(
1688 &'a self,
1689 id: &'b TransactionId,
1690 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1691 self.txs.range(id..).take_while(|(other, _)| id.sender == other.sender)
1692 }
1693
1694 pub(crate) fn descendant_txs_mut<'a, 'b: 'a>(
1699 &'a mut self,
1700 id: &'b TransactionId,
1701 ) -> impl Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)> + 'a {
1702 self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender)
1703 }
1704
1705 pub(crate) fn remove_transaction_by_hash(
1707 &mut self,
1708 tx_hash: &B256,
1709 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1710 let tx = self.by_hash.remove(tx_hash)?;
1711 let internal = self.txs.remove(&tx.transaction_id)?;
1712 self.remove_auths(&internal);
1713 self.tx_decr(tx.sender_id());
1715 Some((tx, internal.subpool))
1716 }
1717
1718 pub(crate) fn remove_transaction_by_id(
1722 &mut self,
1723 tx_id: &TransactionId,
1724 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1725 let internal = self.txs.remove(tx_id)?;
1726 let tx = self.by_hash.remove(internal.transaction.hash())?;
1727 self.remove_auths(&internal);
1728 self.tx_decr(tx.sender_id());
1730 Some((tx, internal.subpool))
1731 }
1732
1733 pub(crate) fn park_descendant_transactions(
1735 &mut self,
1736 tx_id: &TransactionId,
1737 ) -> Vec<PoolUpdate> {
1738 let mut updates = Vec::new();
1739
1740 for (id, tx) in self.descendant_txs_mut(tx_id) {
1741 let current_pool = tx.subpool;
1742
1743 tx.state.remove(TxState::NO_NONCE_GAPS);
1744
1745 tx.subpool = tx.state.into();
1747
1748 if current_pool != tx.subpool {
1750 updates.push(PoolUpdate {
1751 id: *id,
1752 current: current_pool,
1753 destination: tx.subpool.into(),
1754 })
1755 }
1756 }
1757
1758 updates
1759 }
1760
1761 pub(crate) fn remove_transaction(
1767 &mut self,
1768 id: &TransactionId,
1769 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1770 let internal = self.txs.remove(id)?;
1771
1772 self.tx_decr(internal.transaction.sender_id());
1774
1775 let result =
1776 self.by_hash.remove(internal.transaction.hash()).map(|tx| (tx, internal.subpool));
1777
1778 self.remove_auths(&internal);
1779
1780 result
1781 }
1782
1783 fn remove_auths(&mut self, tx: &PoolInternalTransaction<T>) {
1787 let Some(auths) = &tx.transaction.authority_ids else { return };
1788
1789 let tx_hash = tx.transaction.hash();
1790 for auth in auths {
1791 if let Some(list) = self.auths.get_mut(auth) {
1792 list.remove(tx_hash);
1793 if list.is_empty() {
1794 self.auths.remove(auth);
1795 }
1796 }
1797 }
1798 }
1799
1800 #[inline]
1806 fn contains_conflicting_transaction(&self, tx: &ValidPoolTransaction<T>) -> bool {
1807 self.txs_iter(tx.transaction_id.sender)
1808 .next()
1809 .is_some_and(|(_, existing)| tx.tx_type_conflicts_with(&existing.transaction))
1810 }
1811
1812 fn ensure_valid(
1821 &self,
1822 transaction: ValidPoolTransaction<T>,
1823 on_chain_nonce: u64,
1824 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1825 if !self.local_transactions_config.is_local(transaction.origin, transaction.sender_ref()) {
1826 let current_txs =
1827 self.tx_counter.get(&transaction.sender_id()).copied().unwrap_or_default();
1828
1829 if current_txs >= self.max_account_slots && transaction.nonce() > on_chain_nonce {
1832 return Err(InsertErr::ExceededSenderTransactionsCapacity {
1833 transaction: Arc::new(transaction),
1834 })
1835 }
1836 }
1837 if transaction.gas_limit() > self.block_gas_limit {
1838 return Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas {
1839 block_gas_limit: self.block_gas_limit,
1840 tx_gas_limit: transaction.gas_limit(),
1841 transaction: Arc::new(transaction),
1842 })
1843 }
1844
1845 if self.contains_conflicting_transaction(&transaction) {
1846 return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) })
1848 }
1849
1850 Ok(transaction)
1851 }
1852
1853 fn ensure_valid_blob_transaction(
1859 &self,
1860 new_blob_tx: ValidPoolTransaction<T>,
1861 on_chain_balance: U256,
1862 ancestor: Option<TransactionId>,
1863 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1864 if let Some(ancestor) = ancestor {
1865 let Some(ancestor_tx) = self.txs.get(&ancestor) else {
1866 self.metrics.blob_transactions_nonce_gaps.increment(1);
1868 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1869 };
1870 if ancestor_tx.state.has_nonce_gap() {
1871 self.metrics.blob_transactions_nonce_gaps.increment(1);
1874 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1875 }
1876
1877 let mut cumulative_cost = ancestor_tx.next_cumulative_cost() + new_blob_tx.cost();
1879
1880 if cumulative_cost > on_chain_balance {
1882 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1884 }
1885
1886 let id = new_blob_tx.transaction_id;
1889 let mut descendants = self.descendant_txs_inclusive(&id).peekable();
1890 if let Some((maybe_replacement, _)) = descendants.peek() &&
1891 **maybe_replacement == new_blob_tx.transaction_id
1892 {
1893 descendants.next();
1895
1896 for (_, tx) in descendants {
1898 cumulative_cost += tx.transaction.cost();
1899 if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance {
1900 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1902 }
1903 }
1904 }
1905 } else if new_blob_tx.cost() > &on_chain_balance {
1906 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1908 }
1909
1910 Ok(new_blob_tx)
1911 }
1912
1913 pub(crate) fn insert_tx(
1945 &mut self,
1946 transaction: ValidPoolTransaction<T>,
1947 on_chain_balance: U256,
1948 on_chain_nonce: u64,
1949 ) -> InsertResult<T> {
1950 assert!(on_chain_nonce <= transaction.nonce(), "Invalid transaction");
1951
1952 let mut transaction = self.ensure_valid(transaction, on_chain_nonce)?;
1953
1954 let inserted_tx_id = *transaction.id();
1955 let mut state = TxState::default();
1956 let mut cumulative_cost = U256::ZERO;
1957 let mut updates = Vec::new();
1958
1959 state.insert(TxState::NOT_TOO_MUCH_GAS);
1961
1962 let ancestor = TransactionId::ancestor(
1965 transaction.transaction.nonce(),
1966 on_chain_nonce,
1967 inserted_tx_id.sender,
1968 );
1969
1970 if transaction.is_eip4844() {
1973 state.insert(TxState::BLOB_TRANSACTION);
1974
1975 transaction =
1976 self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
1977 let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();
1978 if blob_fee_cap >= self.pending_fees.blob_fee {
1979 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1980 }
1981 } else {
1982 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1984 }
1985
1986 let transaction = Arc::new(transaction);
1987
1988 if ancestor.is_none() {
1990 state.insert(TxState::NO_NONCE_GAPS);
1991 state.insert(TxState::NO_PARKED_ANCESTORS);
1992 }
1993
1994 let fee_cap = transaction.max_fee_per_gas();
1996
1997 if fee_cap < self.minimal_protocol_basefee as u128 {
1998 return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap })
1999 }
2000 if fee_cap >= self.pending_fees.base_fee as u128 {
2001 state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
2002 }
2003
2004 let mut replaced_tx = None;
2006
2007 let pool_tx = PoolInternalTransaction {
2008 transaction: Arc::clone(&transaction),
2009 subpool: state.into(),
2010 state,
2011 cumulative_cost,
2012 };
2013
2014 match self.txs.entry(*transaction.id()) {
2016 Entry::Vacant(entry) => {
2017 self.by_hash.insert(*pool_tx.transaction.hash(), pool_tx.transaction.clone());
2019 entry.insert(pool_tx);
2020 }
2021 Entry::Occupied(mut entry) => {
2022 let existing_transaction = entry.get().transaction.as_ref();
2024 let maybe_replacement = transaction.as_ref();
2025
2026 if existing_transaction.is_underpriced(maybe_replacement, &self.price_bumps) {
2028 return Err(InsertErr::Underpriced {
2029 transaction: pool_tx.transaction,
2030 existing: *entry.get().transaction.hash(),
2031 })
2032 }
2033 let new_hash = *pool_tx.transaction.hash();
2034 let new_transaction = pool_tx.transaction.clone();
2035 let replaced = entry.insert(pool_tx);
2036 self.by_hash.remove(replaced.transaction.hash());
2037 self.by_hash.insert(new_hash, new_transaction);
2038
2039 self.remove_auths(&replaced);
2040
2041 replaced_tx = Some((replaced.transaction, replaced.subpool));
2043 }
2044 }
2045
2046 if let Some(auths) = &transaction.authority_ids {
2047 let tx_hash = transaction.hash();
2048 for auth in auths {
2049 self.auths.entry(*auth).or_default().insert(*tx_hash);
2050 }
2051 }
2052
2053 let on_chain_id = TransactionId::new(transaction.sender_id(), on_chain_nonce);
2055 {
2056 let mut next_nonce = on_chain_id.nonce;
2058
2059 let mut has_parked_ancestor = false;
2063
2064 for (id, tx) in self.descendant_txs_mut(&on_chain_id) {
2067 let current_pool = tx.subpool;
2068
2069 if next_nonce != id.nonce {
2071 break
2072 }
2073
2074 tx.state.insert(TxState::NO_NONCE_GAPS);
2076
2077 tx.cumulative_cost = cumulative_cost;
2079
2080 cumulative_cost = tx.next_cumulative_cost();
2082
2083 if cumulative_cost > on_chain_balance {
2084 tx.state.remove(TxState::ENOUGH_BALANCE);
2086 } else {
2087 tx.state.insert(TxState::ENOUGH_BALANCE);
2088 }
2089
2090 if has_parked_ancestor {
2092 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
2093 } else {
2094 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
2095 }
2096 has_parked_ancestor = !tx.state.is_pending();
2097
2098 tx.subpool = tx.state.into();
2100
2101 if inserted_tx_id.eq(id) {
2102 state = tx.state;
2104 } else {
2105 if current_pool != tx.subpool {
2107 updates.push(PoolUpdate {
2108 id: *id,
2109 current: current_pool,
2110 destination: tx.subpool.into(),
2111 })
2112 }
2113 }
2114
2115 next_nonce = id.next_nonce();
2117 }
2118 }
2119
2120 if replaced_tx.is_none() {
2122 self.tx_inc(inserted_tx_id.sender);
2123 }
2124
2125 self.update_size_metrics();
2126
2127 Ok(InsertOk { transaction, move_to: state.into(), state, replaced_tx, updates })
2128 }
2129
2130 pub(crate) fn len(&self) -> usize {
2132 self.txs.len()
2133 }
2134
2135 pub(crate) fn is_empty(&self) -> bool {
2137 self.txs.is_empty()
2138 }
2139
2140 #[cfg(any(test, feature = "test-utils"))]
2142 pub(crate) fn assert_invariants(&self) {
2143 assert_eq!(self.by_hash.len(), self.txs.len(), "by_hash.len() != txs.len()");
2144 assert!(self.auths.len() <= self.txs.len(), "auths.len() > txs.len()");
2145 }
2146}
2147
2148#[cfg(test)]
2149impl<T: PoolTransaction> AllTransactions<T> {
2150 pub(crate) fn tx_count(&self, sender: SenderId) -> usize {
2154 self.tx_counter.get(&sender).copied().unwrap_or_default()
2155 }
2156}
2157
2158impl<T: PoolTransaction> Default for AllTransactions<T> {
2159 fn default() -> Self {
2160 Self {
2161 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
2162 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
2163 block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
2164 by_hash: Default::default(),
2165 txs: Default::default(),
2166 sender_info: Default::default(),
2167 tx_counter: Default::default(),
2168 last_seen_block_number: Default::default(),
2169 last_seen_block_hash: Default::default(),
2170 pending_fees: Default::default(),
2171 price_bumps: Default::default(),
2172 local_transactions_config: Default::default(),
2173 auths: Default::default(),
2174 metrics: Default::default(),
2175 }
2176 }
2177}
2178
2179#[derive(Debug, Clone)]
2181pub(crate) struct PendingFees {
2182 pub(crate) base_fee: u64,
2184 pub(crate) blob_fee: u128,
2186}
2187
2188impl Default for PendingFees {
2189 fn default() -> Self {
2190 Self { base_fee: Default::default(), blob_fee: BLOB_TX_MIN_BLOB_GASPRICE }
2191 }
2192}
2193
2194pub(crate) type InsertResult<T> = Result<InsertOk<T>, InsertErr<T>>;
2196
2197#[derive(Debug)]
2199pub(crate) enum InsertErr<T: PoolTransaction> {
2200 Underpriced {
2202 transaction: Arc<ValidPoolTransaction<T>>,
2203 #[expect(dead_code)]
2204 existing: TxHash,
2205 },
2206 BlobTxHasNonceGap { transaction: Arc<ValidPoolTransaction<T>> },
2208 Overdraft { transaction: Arc<ValidPoolTransaction<T>> },
2211 FeeCapBelowMinimumProtocolFeeCap { transaction: Arc<ValidPoolTransaction<T>>, fee_cap: u128 },
2215 ExceededSenderTransactionsCapacity { transaction: Arc<ValidPoolTransaction<T>> },
2219 TxGasLimitMoreThanAvailableBlockGas {
2221 transaction: Arc<ValidPoolTransaction<T>>,
2222 block_gas_limit: u64,
2223 tx_gas_limit: u64,
2224 },
2225 TxTypeConflict { transaction: Arc<ValidPoolTransaction<T>> },
2227}
2228
2229#[derive(Debug)]
2231pub(crate) struct InsertOk<T: PoolTransaction> {
2232 transaction: Arc<ValidPoolTransaction<T>>,
2234 move_to: SubPool,
2236 state: TxState,
2238 replaced_tx: Option<(Arc<ValidPoolTransaction<T>>, SubPool)>,
2240 updates: Vec<PoolUpdate>,
2242}
2243
2244#[derive(Debug)]
2247pub(crate) struct PoolInternalTransaction<T: PoolTransaction> {
2248 pub(crate) transaction: Arc<ValidPoolTransaction<T>>,
2250 pub(crate) subpool: SubPool,
2252 pub(crate) state: TxState,
2255 pub(crate) cumulative_cost: U256,
2260}
2261
2262impl<T: PoolTransaction> PoolInternalTransaction<T> {
2265 fn next_cumulative_cost(&self) -> U256 {
2266 self.cumulative_cost + self.transaction.cost()
2267 }
2268}
2269
2270#[derive(Debug, Clone, Default)]
2272pub(crate) struct SenderInfo {
2273 pub(crate) state_nonce: u64,
2275 pub(crate) balance: U256,
2277}
2278
2279impl SenderInfo {
2282 const fn update(&mut self, state_nonce: u64, balance: U256) {
2284 *self = Self { state_nonce, balance };
2285 }
2286}
2287
2288#[cfg(test)]
2289mod tests {
2290 use super::*;
2291 use crate::{
2292 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
2293 traits::TransactionOrigin,
2294 SubPoolLimit,
2295 };
2296 use alloy_consensus::{Transaction, TxType};
2297 use alloy_primitives::address;
2298
2299 #[test]
2300 fn test_insert_blob() {
2301 let on_chain_balance = U256::MAX;
2302 let on_chain_nonce = 0;
2303 let mut f = MockTransactionFactory::default();
2304 let mut pool = AllTransactions::default();
2305 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2306 let valid_tx = f.validated(tx);
2307 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2308 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2309 assert!(updates.is_empty());
2310 assert!(replaced_tx.is_none());
2311 assert!(state.contains(TxState::NO_NONCE_GAPS));
2312 assert!(state.contains(TxState::ENOUGH_BALANCE));
2313 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2314 assert_eq!(move_to, SubPool::Pending);
2315
2316 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2317 assert_eq!(inserted.subpool, SubPool::Pending);
2318 }
2319
2320 #[test]
2321 fn test_insert_blob_not_enough_blob_fee() {
2322 let on_chain_balance = U256::MAX;
2323 let on_chain_nonce = 0;
2324 let mut f = MockTransactionFactory::default();
2325 let mut pool = AllTransactions {
2326 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2327 ..Default::default()
2328 };
2329 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2330 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2331 let valid_tx = f.validated(tx);
2332 let InsertOk { state, .. } =
2333 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2334 assert!(state.contains(TxState::NO_NONCE_GAPS));
2335 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2336
2337 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2338 }
2339
2340 #[test]
2341 fn test_valid_tx_with_decreasing_blob_fee() {
2342 let on_chain_balance = U256::MAX;
2343 let on_chain_nonce = 0;
2344 let mut f = MockTransactionFactory::default();
2345 let mut pool = AllTransactions {
2346 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2347 ..Default::default()
2348 };
2349 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2350
2351 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2352 let valid_tx = f.validated(tx.clone());
2353 let InsertOk { state, .. } =
2354 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2355 assert!(state.contains(TxState::NO_NONCE_GAPS));
2356 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2357
2358 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2359 pool.remove_transaction(&valid_tx.transaction_id);
2360
2361 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
2362 let InsertOk { state, .. } =
2363 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2364 assert!(state.contains(TxState::NO_NONCE_GAPS));
2365 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2366 }
2367
2368 #[test]
2369 fn test_demote_valid_tx_with_increasing_blob_fee() {
2370 let on_chain_balance = U256::MAX;
2371 let on_chain_nonce = 0;
2372 let mut f = MockTransactionFactory::default();
2373 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2374 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2375
2376 let mut block_info = pool.block_info();
2378 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2379 pool.set_block_info(block_info);
2380
2381 let validated = f.validated(tx.clone());
2382 let id = *validated.id();
2383 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2384
2385 assert!(pool.blob_pool.is_empty());
2387 assert_eq!(pool.pending_pool.len(), 1);
2388
2389 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2391 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2392 assert_eq!(internal_tx.subpool, SubPool::Pending);
2393
2394 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2396 pool.set_block_info(block_info);
2397
2398 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2400 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2401 assert_eq!(internal_tx.subpool, SubPool::Blob);
2402
2403 assert_eq!(pool.blob_pool.len(), 1);
2405 assert!(pool.pending_pool.is_empty());
2406 }
2407
2408 #[test]
2409 fn test_promote_valid_tx_with_decreasing_blob_fee() {
2410 let on_chain_balance = U256::MAX;
2411 let on_chain_nonce = 0;
2412 let mut f = MockTransactionFactory::default();
2413 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2414 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2415
2416 let mut block_info = pool.block_info();
2418 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2419 pool.set_block_info(block_info);
2420
2421 let validated = f.validated(tx.clone());
2422 let id = *validated.id();
2423 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2424
2425 assert!(pool.pending_pool.is_empty());
2427 assert_eq!(pool.blob_pool.len(), 1);
2428
2429 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2431 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2432 assert_eq!(internal_tx.subpool, SubPool::Blob);
2433
2434 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2436 pool.set_block_info(block_info);
2437
2438 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2440 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2441 assert_eq!(internal_tx.subpool, SubPool::Pending);
2442
2443 assert_eq!(pool.pending_pool.len(), 1);
2445 assert!(pool.blob_pool.is_empty());
2446 }
2447
2448 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
2450 struct PromotionTest {
2451 basefee: u64,
2453 blobfee: u128,
2455 subpool: SubPool,
2457 basefee_update: u64,
2459 blobfee_update: u128,
2461 new_subpool: SubPool,
2463 }
2464
2465 impl PromotionTest {
2466 const fn opposite(&self) -> Self {
2468 Self {
2469 basefee: self.basefee_update,
2470 blobfee: self.blobfee_update,
2471 subpool: self.new_subpool,
2472 blobfee_update: self.blobfee,
2473 basefee_update: self.basefee,
2474 new_subpool: self.subpool,
2475 }
2476 }
2477
2478 fn assert_subpool_lengths<T: TransactionOrdering>(
2479 &self,
2480 pool: &TxPool<T>,
2481 failure_message: String,
2482 check_subpool: SubPool,
2483 ) {
2484 match check_subpool {
2485 SubPool::Blob => {
2486 assert_eq!(pool.blob_pool.len(), 1, "{failure_message}");
2487 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2488 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2489 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2490 }
2491 SubPool::Pending => {
2492 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2493 assert_eq!(pool.pending_pool.len(), 1, "{failure_message}");
2494 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2495 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2496 }
2497 SubPool::BaseFee => {
2498 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2499 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2500 assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}");
2501 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2502 }
2503 SubPool::Queued => {
2504 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2505 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2506 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2507 assert_eq!(pool.queued_pool.len(), 1, "{failure_message}");
2508 }
2509 }
2510 }
2511
2512 fn assert_single_tx_starting_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2516 self.assert_subpool_lengths(
2517 pool,
2518 format!("pool length check failed at start of test: {self:?}"),
2519 self.subpool,
2520 );
2521 }
2522
2523 fn assert_single_tx_ending_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2527 self.assert_subpool_lengths(
2528 pool,
2529 format!("pool length check failed at end of test: {self:?}"),
2530 self.new_subpool,
2531 );
2532 }
2533 }
2534
2535 #[test]
2536 fn test_promote_blob_tx_with_both_pending_fee_updates() {
2537 let on_chain_balance = U256::MAX;
2540 let on_chain_nonce = 0;
2541 let mut f = MockTransactionFactory::default();
2542 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2543
2544 let max_fee_per_blob_gas = tx.max_fee_per_blob_gas().unwrap();
2545 let max_fee_per_gas = tx.max_fee_per_gas() as u64;
2546
2547 let mut expected_promotions = vec![
2549 PromotionTest {
2550 blobfee: max_fee_per_blob_gas + 1,
2551 basefee: max_fee_per_gas + 1,
2552 subpool: SubPool::Blob,
2553 blobfee_update: max_fee_per_blob_gas + 1,
2554 basefee_update: max_fee_per_gas + 1,
2555 new_subpool: SubPool::Blob,
2556 },
2557 PromotionTest {
2558 blobfee: max_fee_per_blob_gas + 1,
2559 basefee: max_fee_per_gas + 1,
2560 subpool: SubPool::Blob,
2561 blobfee_update: max_fee_per_blob_gas,
2562 basefee_update: max_fee_per_gas + 1,
2563 new_subpool: SubPool::Blob,
2564 },
2565 PromotionTest {
2566 blobfee: max_fee_per_blob_gas + 1,
2567 basefee: max_fee_per_gas + 1,
2568 subpool: SubPool::Blob,
2569 blobfee_update: max_fee_per_blob_gas + 1,
2570 basefee_update: max_fee_per_gas,
2571 new_subpool: SubPool::Blob,
2572 },
2573 PromotionTest {
2574 blobfee: max_fee_per_blob_gas + 1,
2575 basefee: max_fee_per_gas + 1,
2576 subpool: SubPool::Blob,
2577 blobfee_update: max_fee_per_blob_gas,
2578 basefee_update: max_fee_per_gas,
2579 new_subpool: SubPool::Pending,
2580 },
2581 PromotionTest {
2582 blobfee: max_fee_per_blob_gas,
2583 basefee: max_fee_per_gas + 1,
2584 subpool: SubPool::Blob,
2585 blobfee_update: max_fee_per_blob_gas,
2586 basefee_update: max_fee_per_gas,
2587 new_subpool: SubPool::Pending,
2588 },
2589 PromotionTest {
2590 blobfee: max_fee_per_blob_gas + 1,
2591 basefee: max_fee_per_gas,
2592 subpool: SubPool::Blob,
2593 blobfee_update: max_fee_per_blob_gas,
2594 basefee_update: max_fee_per_gas,
2595 new_subpool: SubPool::Pending,
2596 },
2597 PromotionTest {
2598 blobfee: max_fee_per_blob_gas,
2599 basefee: max_fee_per_gas,
2600 subpool: SubPool::Pending,
2601 blobfee_update: max_fee_per_blob_gas,
2602 basefee_update: max_fee_per_gas,
2603 new_subpool: SubPool::Pending,
2604 },
2605 ];
2606
2607 let reversed = expected_promotions.iter().map(|test| test.opposite()).collect::<Vec<_>>();
2609 expected_promotions.extend(reversed);
2610
2611 let expected_promotions = expected_promotions.into_iter().collect::<HashSet<_>>();
2613
2614 for promotion_test in &expected_promotions {
2615 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2616
2617 let mut block_info = pool.block_info();
2619
2620 block_info.pending_blob_fee = Some(promotion_test.blobfee);
2621 block_info.pending_basefee = promotion_test.basefee;
2622 pool.set_block_info(block_info);
2623
2624 let validated = f.validated(tx.clone());
2625 let id = *validated.id();
2626 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2627
2628 promotion_test.assert_single_tx_starting_subpool(&pool);
2630
2631 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2633 assert_eq!(
2634 internal_tx.subpool, promotion_test.subpool,
2635 "Subpools do not match at start of test: {promotion_test:?}"
2636 );
2637
2638 block_info.pending_basefee = promotion_test.basefee_update;
2640 block_info.pending_blob_fee = Some(promotion_test.blobfee_update);
2641 pool.set_block_info(block_info);
2642
2643 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2645 assert_eq!(
2646 internal_tx.subpool, promotion_test.new_subpool,
2647 "Subpools do not match at end of test: {promotion_test:?}"
2648 );
2649
2650 promotion_test.assert_single_tx_ending_subpool(&pool);
2652 }
2653 }
2654
2655 #[test]
2656 fn test_insert_pending() {
2657 let on_chain_balance = U256::MAX;
2658 let on_chain_nonce = 0;
2659 let mut f = MockTransactionFactory::default();
2660 let mut pool = AllTransactions::default();
2661 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2662 let valid_tx = f.validated(tx);
2663 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2664 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2665 assert!(updates.is_empty());
2666 assert!(replaced_tx.is_none());
2667 assert!(state.contains(TxState::NO_NONCE_GAPS));
2668 assert!(state.contains(TxState::ENOUGH_BALANCE));
2669 assert_eq!(move_to, SubPool::Pending);
2670
2671 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2672 assert_eq!(inserted.subpool, SubPool::Pending);
2673 }
2674
2675 #[test]
2676 fn test_simple_insert() {
2677 let on_chain_balance = U256::ZERO;
2678 let on_chain_nonce = 0;
2679 let mut f = MockTransactionFactory::default();
2680 let mut pool = AllTransactions::default();
2681 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2682 tx.set_priority_fee(100);
2683 tx.set_max_fee(100);
2684 let valid_tx = f.validated(tx.clone());
2685 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2686 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2687 assert!(updates.is_empty());
2688 assert!(replaced_tx.is_none());
2689 assert!(state.contains(TxState::NO_NONCE_GAPS));
2690 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2691 assert_eq!(move_to, SubPool::Queued);
2692
2693 assert_eq!(pool.len(), 1);
2694 assert!(pool.contains(valid_tx.hash()));
2695 let expected_state = TxState::ENOUGH_FEE_CAP_BLOCK | TxState::NO_NONCE_GAPS;
2696 let inserted = pool.get(valid_tx.id()).unwrap();
2697 assert!(inserted.state.intersects(expected_state));
2698
2699 let res = pool.insert_tx(valid_tx, on_chain_balance, on_chain_nonce);
2701 res.unwrap_err();
2702 assert_eq!(pool.len(), 1);
2703
2704 let valid_tx = f.validated(tx.next());
2705 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2706 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2707
2708 assert!(updates.is_empty());
2709 assert!(replaced_tx.is_none());
2710 assert!(state.contains(TxState::NO_NONCE_GAPS));
2711 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2712 assert_eq!(move_to, SubPool::Queued);
2713
2714 assert!(pool.contains(valid_tx.hash()));
2715 assert_eq!(pool.len(), 2);
2716 let inserted = pool.get(valid_tx.id()).unwrap();
2717 assert!(inserted.state.intersects(expected_state));
2718 }
2719
2720 #[test]
2721 fn test_on_canonical_state_change_no_double_processing() {
2724 let mut tx_factory = MockTransactionFactory::default();
2725 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2726
2727 let tx = MockTransaction::eip1559().with_gas_price(50).with_gas_limit(30_000);
2729 let sender = tx.sender();
2730
2731 let mut block_info = pool.block_info();
2733 block_info.pending_basefee = 100;
2734 pool.set_block_info(block_info);
2735
2736 let validated = tx_factory.validated(tx);
2737 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2738
2739 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2741
2742 assert_eq!(pool.basefee_pool.len(), 1);
2743 assert_eq!(pool.pending_pool.len(), 0);
2744
2745 block_info.pending_basefee = 40;
2749
2750 let mut changed_senders = FxHashMap::default();
2751 changed_senders.insert(
2752 sender_id,
2753 SenderInfo {
2754 state_nonce: 0,
2755 balance: U256::from(20_000_000), },
2757 );
2758
2759 let outcome = pool.on_canonical_state_change(
2760 block_info,
2761 vec![], changed_senders,
2763 PoolUpdateKind::Commit,
2764 );
2765
2766 assert_eq!(pool.pending_pool.len(), 1, "Transaction should be in pending pool");
2768 assert_eq!(pool.basefee_pool.len(), 0, "Transaction should not be in basefee pool");
2769 assert_eq!(outcome.promoted.len(), 1, "Should report exactly one promotion");
2770 }
2771
2772 #[test]
2773 fn test_canonical_state_change_with_basefee_update_regression() {
2776 let mut tx_factory = MockTransactionFactory::default();
2777 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2778
2779 let sender_balance = U256::from(100_000_000);
2781
2782 let tx1 =
2784 MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000).with_nonce(0);
2785 let sender1 = tx1.sender();
2786
2787 let tx2 =
2789 MockTransaction::eip1559().with_gas_price(55).with_gas_limit(21_000).with_nonce(0);
2790 let sender2 = tx2.sender();
2791
2792 let tx3 =
2794 MockTransaction::eip1559().with_gas_price(45).with_gas_limit(21_000).with_nonce(0);
2795 let sender3 = tx3.sender();
2796
2797 let mut block_info = pool.block_info();
2799 block_info.pending_basefee = 70;
2800 pool.set_block_info(block_info);
2801
2802 let validated1 = tx_factory.validated(tx1);
2804 let validated2 = tx_factory.validated(tx2);
2805 let validated3 = tx_factory.validated(tx3);
2806
2807 pool.add_transaction(validated1, sender_balance, 0, None).unwrap();
2808 pool.add_transaction(validated2, sender_balance, 0, None).unwrap();
2809 pool.add_transaction(validated3, sender_balance, 0, None).unwrap();
2810
2811 let sender1_id = tx_factory.ids.sender_id(&sender1).unwrap();
2812 let sender2_id = tx_factory.ids.sender_id(&sender2).unwrap();
2813 let sender3_id = tx_factory.ids.sender_id(&sender3).unwrap();
2814
2815 assert_eq!(pool.basefee_pool.len(), 3, "All txs should be in basefee pool");
2817 assert_eq!(pool.pending_pool.len(), 0, "No txs should be in pending pool");
2818
2819 block_info.pending_basefee = 50;
2822
2823 let mut changed_senders = FxHashMap::default();
2825 changed_senders.insert(
2826 sender1_id,
2827 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2828 );
2829 changed_senders.insert(
2830 sender2_id,
2831 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2832 );
2833 changed_senders.insert(
2834 sender3_id,
2835 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2836 );
2837
2838 let outcome = pool.on_canonical_state_change(
2839 block_info,
2840 vec![],
2841 changed_senders,
2842 PoolUpdateKind::Commit,
2843 );
2844
2845 assert_eq!(pool.pending_pool.len(), 2, "tx1 and tx2 should be promoted");
2847 assert_eq!(pool.basefee_pool.len(), 1, "tx3 should remain in basefee");
2848
2849 assert_eq!(
2852 outcome.promoted.len(),
2853 2,
2854 "Should report exactly 2 promotions, not double-counted"
2855 );
2856
2857 let promoted_prices: Vec<u128> =
2859 outcome.promoted.iter().map(|tx| tx.max_fee_per_gas()).collect();
2860 assert!(promoted_prices.contains(&60));
2861 assert!(promoted_prices.contains(&55));
2862 }
2863
2864 #[test]
2865 fn test_basefee_decrease_with_empty_senders() {
2866 let mut tx_factory = MockTransactionFactory::default();
2869 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2870
2871 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2873
2874 let mut block_info = pool.block_info();
2876 block_info.pending_basefee = 100;
2877 pool.set_block_info(block_info);
2878
2879 let validated = tx_factory.validated(tx);
2881 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2882
2883 assert_eq!(pool.basefee_pool.len(), 1);
2884 assert_eq!(pool.pending_pool.len(), 0);
2885
2886 block_info.pending_basefee = 50;
2888 let outcome = pool.on_canonical_state_change(
2889 block_info,
2890 vec![],
2891 FxHashMap::default(), PoolUpdateKind::Commit,
2893 );
2894
2895 assert_eq!(pool.pending_pool.len(), 1, "Fee decrease should promote tx");
2897 assert_eq!(pool.basefee_pool.len(), 0);
2898 assert_eq!(outcome.promoted.len(), 1, "Should report promotion from fee update");
2899 }
2900
2901 #[test]
2902 fn test_basefee_decrease_account_makes_unfundable() {
2903 let mut tx_factory = MockTransactionFactory::default();
2906 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2907
2908 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2909 let sender = tx.sender();
2910
2911 let mut block_info = pool.block_info();
2913 block_info.pending_basefee = 100;
2914 pool.set_block_info(block_info);
2915
2916 let validated = tx_factory.validated(tx);
2917 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2918 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2919
2920 assert_eq!(pool.basefee_pool.len(), 1);
2921
2922 block_info.pending_basefee = 50;
2924 let mut changed_senders = FxHashMap::default();
2925 changed_senders.insert(
2926 sender_id,
2927 SenderInfo {
2928 state_nonce: 0,
2929 balance: U256::from(100), },
2931 );
2932
2933 let outcome = pool.on_canonical_state_change(
2934 block_info,
2935 vec![],
2936 changed_senders,
2937 PoolUpdateKind::Commit,
2938 );
2939
2940 assert_eq!(pool.pending_pool.len(), 0, "Unfunded tx should not be in pending");
2942 assert_eq!(pool.basefee_pool.len(), 0, "Tx no longer in basefee pool");
2943 assert_eq!(pool.queued_pool.len(), 1, "Unfunded tx should be in queued pool");
2944
2945 let tx_count = pool.all_transactions.txs.len();
2947 assert_eq!(tx_count, 1, "Transaction should still be in pool (in queued)");
2948
2949 assert_eq!(outcome.promoted.len(), 0, "Should not report promotion");
2950 assert_eq!(outcome.discarded.len(), 0, "Queued tx is not reported as discarded");
2951 }
2952
2953 #[test]
2954 fn insert_already_imported() {
2955 let on_chain_balance = U256::ZERO;
2956 let on_chain_nonce = 0;
2957 let mut f = MockTransactionFactory::default();
2958 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2959 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2960 let tx = f.validated(tx);
2961 pool.add_transaction(tx.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
2962 match pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap_err().kind {
2963 PoolErrorKind::AlreadyImported => {}
2964 _ => unreachable!(),
2965 }
2966 }
2967
2968 #[test]
2969 fn insert_replace() {
2970 let on_chain_balance = U256::ZERO;
2971 let on_chain_nonce = 0;
2972 let mut f = MockTransactionFactory::default();
2973 let mut pool = AllTransactions::default();
2974 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2975 let first = f.validated(tx.clone());
2976 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
2977 let replacement = f.validated(tx.rng_hash().inc_price());
2978 let InsertOk { updates, replaced_tx, .. } =
2979 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
2980 assert!(updates.is_empty());
2981 let replaced = replaced_tx.unwrap();
2982 assert_eq!(replaced.0.hash(), first.hash());
2983
2984 assert!(!pool.contains(first.hash()));
2986 assert!(pool.contains(replacement.hash()));
2987 assert_eq!(pool.len(), 1);
2988 }
2989
2990 #[test]
2991 fn insert_replace_txpool() {
2992 let on_chain_balance = U256::ZERO;
2993 let on_chain_nonce = 0;
2994 let mut f = MockTransactionFactory::default();
2995 let mut pool = TxPool::mock();
2996
2997 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2998 let first = f.validated(tx.clone());
2999 let first_added =
3000 pool.add_transaction(first, on_chain_balance, on_chain_nonce, None).unwrap();
3001 let replacement = f.validated(tx.rng_hash().inc_price());
3002 let replacement_added = pool
3003 .add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce, None)
3004 .unwrap();
3005
3006 assert!(!pool.contains(first_added.hash()));
3008 assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));
3010
3011 assert!(pool.contains(replacement.hash()));
3012 let size = pool.size();
3013 assert_eq!(size.total, 1);
3014 size.assert_invariants();
3015 }
3016
3017 #[test]
3018 fn insert_replace_underpriced() {
3019 let on_chain_balance = U256::ZERO;
3020 let on_chain_nonce = 0;
3021 let mut f = MockTransactionFactory::default();
3022 let mut pool = AllTransactions::default();
3023 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3024 let first = f.validated(tx.clone());
3025 let _res = pool.insert_tx(first, on_chain_balance, on_chain_nonce);
3026 let mut replacement = f.validated(tx.rng_hash());
3027 replacement.transaction = replacement.transaction.decr_price();
3028 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3029 assert!(matches!(err, InsertErr::Underpriced { .. }));
3030 }
3031
3032 #[test]
3033 fn insert_replace_underpriced_not_enough_bump() {
3034 let on_chain_balance = U256::ZERO;
3035 let on_chain_nonce = 0;
3036 let mut f = MockTransactionFactory::default();
3037 let mut pool = AllTransactions::default();
3038 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3039 tx.set_priority_fee(100);
3040 tx.set_max_fee(100);
3041 let first = f.validated(tx.clone());
3042 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3043 let mut replacement = f.validated(tx.rng_hash().inc_price());
3044
3045 replacement.transaction.set_priority_fee(109);
3047 replacement.transaction.set_max_fee(109);
3048 let err =
3049 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3050 assert!(matches!(err, InsertErr::Underpriced { .. }));
3051 assert!(pool.contains(first.hash()));
3053 assert_eq!(pool.len(), 1);
3054
3055 replacement.transaction.set_priority_fee(110);
3057 replacement.transaction.set_max_fee(109);
3058 let err =
3059 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3060 assert!(matches!(err, InsertErr::Underpriced { .. }));
3061 assert!(pool.contains(first.hash()));
3062 assert_eq!(pool.len(), 1);
3063
3064 replacement.transaction.set_priority_fee(109);
3066 replacement.transaction.set_max_fee(110);
3067 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3068 assert!(matches!(err, InsertErr::Underpriced { .. }));
3069 assert!(pool.contains(first.hash()));
3070 assert_eq!(pool.len(), 1);
3071 }
3072
3073 #[test]
3074 fn insert_conflicting_type_normal_to_blob() {
3075 let on_chain_balance = U256::from(10_000);
3076 let on_chain_nonce = 0;
3077 let mut f = MockTransactionFactory::default();
3078 let mut pool = AllTransactions::default();
3079 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3080 let first = f.validated(tx.clone());
3081 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3082 let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3083 let blob = f.validated(tx);
3084 let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
3085 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3086 }
3087
3088 #[test]
3089 fn insert_conflicting_type_blob_to_normal() {
3090 let on_chain_balance = U256::from(10_000);
3091 let on_chain_nonce = 0;
3092 let mut f = MockTransactionFactory::default();
3093 let mut pool = AllTransactions::default();
3094 let tx = MockTransaction::eip4844().inc_price().inc_limit();
3095 let first = f.validated(tx.clone());
3096 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3097 let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3098 let tx = f.validated(tx);
3099 let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
3100 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3101 }
3102
3103 #[test]
3105 fn insert_previous() {
3106 let on_chain_balance = U256::ZERO;
3107 let on_chain_nonce = 0;
3108 let mut f = MockTransactionFactory::default();
3109 let mut pool = AllTransactions::default();
3110 let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
3111 let first = f.validated(tx.clone());
3112 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3113
3114 let first_in_pool = pool.get(first.id()).unwrap();
3115
3116 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3118
3119 let prev = f.validated(tx.prev());
3120 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3121 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3122
3123 assert!(updates.is_empty());
3125 assert!(replaced_tx.is_none());
3126 assert!(state.contains(TxState::NO_NONCE_GAPS));
3127 assert_eq!(move_to, SubPool::Queued);
3128
3129 let first_in_pool = pool.get(first.id()).unwrap();
3130 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3132 }
3133
3134 #[test]
3136 fn insert_with_updates() {
3137 let on_chain_balance = U256::from(10_000);
3138 let on_chain_nonce = 0;
3139 let mut f = MockTransactionFactory::default();
3140 let mut pool = AllTransactions::default();
3141 let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
3142 let first = f.validated(tx.clone());
3143 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3144
3145 let first_in_pool = pool.get(first.id()).unwrap();
3146 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3148 assert_eq!(SubPool::Queued, first_in_pool.subpool);
3149
3150 let prev = f.validated(tx.prev());
3151 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3152 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3153
3154 assert_eq!(updates.len(), 1);
3156 assert!(replaced_tx.is_none());
3157 assert!(state.contains(TxState::NO_NONCE_GAPS));
3158 assert_eq!(move_to, SubPool::Pending);
3159
3160 let first_in_pool = pool.get(first.id()).unwrap();
3161 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3163 assert_eq!(SubPool::Pending, first_in_pool.subpool);
3164 }
3165
3166 #[test]
3167 fn insert_previous_blocking() {
3168 let on_chain_balance = U256::from(1_000);
3169 let on_chain_nonce = 0;
3170 let mut f = MockTransactionFactory::default();
3171 let mut pool = AllTransactions::default();
3172 pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
3173 let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
3174 let first = f.validated(tx.clone());
3175
3176 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3177
3178 let first_in_pool = pool.get(first.id()).unwrap();
3179
3180 assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
3181 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3183
3184 let prev = f.validated(tx.prev());
3185 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3186 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3187
3188 assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3189 assert!(updates.is_empty());
3191 assert!(replaced_tx.is_none());
3192 assert!(state.contains(TxState::NO_NONCE_GAPS));
3193 assert_eq!(move_to, SubPool::BaseFee);
3194
3195 let first_in_pool = pool.get(first.id()).unwrap();
3196 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3198 }
3199
3200 #[test]
3201 fn rejects_spammer() {
3202 let on_chain_balance = U256::from(1_000);
3203 let on_chain_nonce = 0;
3204 let mut f = MockTransactionFactory::default();
3205 let mut pool = AllTransactions::default();
3206
3207 let mut tx = MockTransaction::eip1559();
3208 let unblocked_tx = tx.clone();
3209 for _ in 0..pool.max_account_slots {
3210 tx = tx.next();
3211 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
3212 }
3213
3214 assert_eq!(
3215 pool.max_account_slots,
3216 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3217 );
3218
3219 let err =
3220 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
3221 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
3222
3223 assert!(pool
3224 .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
3225 .is_ok());
3226 }
3227
3228 #[test]
3229 fn allow_local_spamming() {
3230 let on_chain_balance = U256::from(1_000);
3231 let on_chain_nonce = 0;
3232 let mut f = MockTransactionFactory::default();
3233 let mut pool = AllTransactions::default();
3234
3235 let mut tx = MockTransaction::eip1559();
3236 for _ in 0..pool.max_account_slots {
3237 tx = tx.next();
3238 pool.insert_tx(
3239 f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
3240 on_chain_balance,
3241 on_chain_nonce,
3242 )
3243 .unwrap();
3244 }
3245
3246 assert_eq!(
3247 pool.max_account_slots,
3248 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3249 );
3250
3251 pool.insert_tx(
3252 f.validated_with_origin(TransactionOrigin::Local, tx.next()),
3253 on_chain_balance,
3254 on_chain_nonce,
3255 )
3256 .unwrap();
3257 }
3258
3259 #[test]
3260 fn reject_tx_over_gas_limit() {
3261 let on_chain_balance = U256::from(1_000);
3262 let on_chain_nonce = 0;
3263 let mut f = MockTransactionFactory::default();
3264 let mut pool = AllTransactions::default();
3265
3266 let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
3267
3268 assert!(matches!(
3269 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
3270 Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
3271 ));
3272 }
3273
3274 #[test]
3275 fn test_tx_equal_gas_limit() {
3276 let on_chain_balance = U256::from(1_000);
3277 let on_chain_nonce = 0;
3278 let mut f = MockTransactionFactory::default();
3279 let mut pool = AllTransactions::default();
3280
3281 let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
3282
3283 let InsertOk { state, .. } =
3284 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
3285 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
3286 }
3287
3288 #[test]
3289 fn update_basefee_subpools() {
3290 let mut f = MockTransactionFactory::default();
3291 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3292
3293 let tx = MockTransaction::eip1559().inc_price_by(10);
3294 let validated = f.validated(tx.clone());
3295 let id = *validated.id();
3296 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3297
3298 assert_eq!(pool.pending_pool.len(), 1);
3299
3300 pool.update_basefee((tx.max_fee_per_gas() + 1) as u64, |_| {});
3301
3302 assert!(pool.pending_pool.is_empty());
3303 assert_eq!(pool.basefee_pool.len(), 1);
3304
3305 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3306 }
3307
3308 #[test]
3309 fn update_basefee_subpools_setting_block_info() {
3310 let mut f = MockTransactionFactory::default();
3311 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3312
3313 let tx = MockTransaction::eip1559().inc_price_by(10);
3314 let validated = f.validated(tx.clone());
3315 let id = *validated.id();
3316 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3317
3318 assert_eq!(pool.pending_pool.len(), 1);
3319
3320 let mut block_info = pool.block_info();
3322 block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
3323 pool.set_block_info(block_info);
3324
3325 assert!(pool.pending_pool.is_empty());
3326 assert_eq!(pool.basefee_pool.len(), 1);
3327
3328 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3329 }
3330
3331 #[test]
3332 fn basefee_decrease_promotes_affordable_and_keeps_unaffordable() {
3333 use alloy_primitives::address;
3334 let mut f = MockTransactionFactory::default();
3335 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3336
3337 let sender_a = address!("0x000000000000000000000000000000000000000a");
3340 let sender_b = address!("0x000000000000000000000000000000000000000b");
3341 let sender_c = address!("0x000000000000000000000000000000000000000c");
3342
3343 let tx1 = MockTransaction::eip1559()
3344 .set_sender(sender_a)
3345 .set_nonce(0)
3346 .set_max_fee(500)
3347 .inc_limit();
3348 let tx2 = MockTransaction::eip1559()
3349 .set_sender(sender_b)
3350 .set_nonce(0)
3351 .set_max_fee(600)
3352 .inc_limit();
3353 let tx3 = MockTransaction::eip1559()
3354 .set_sender(sender_c)
3355 .set_nonce(0)
3356 .set_max_fee(400)
3357 .inc_limit();
3358
3359 let mut block_info = pool.block_info();
3361 block_info.pending_basefee = 700;
3362 pool.set_block_info(block_info);
3363
3364 let validated1 = f.validated(tx1);
3365 let validated2 = f.validated(tx2);
3366 let validated3 = f.validated(tx3);
3367 let id1 = *validated1.id();
3368 let id2 = *validated2.id();
3369 let id3 = *validated3.id();
3370
3371 pool.add_transaction(validated1, U256::from(10_000), 0, None).unwrap();
3375 pool.add_transaction(validated2, U256::from(10_000), 0, None).unwrap();
3376 pool.add_transaction(validated3, U256::from(10_000), 0, None).unwrap();
3377
3378 println!("Basefee pool len: {}", pool.basefee_pool.len());
3380 println!("Pending pool len: {}", pool.pending_pool.len());
3381 println!("tx1 subpool: {:?}", pool.all_transactions.txs.get(&id1).unwrap().subpool);
3382 println!("tx2 subpool: {:?}", pool.all_transactions.txs.get(&id2).unwrap().subpool);
3383 println!("tx3 subpool: {:?}", pool.all_transactions.txs.get(&id3).unwrap().subpool);
3384
3385 assert_eq!(pool.basefee_pool.len(), 3);
3387 assert_eq!(pool.pending_pool.len(), 0);
3388 assert_eq!(pool.all_transactions.txs.get(&id1).unwrap().subpool, SubPool::BaseFee);
3389 assert_eq!(pool.all_transactions.txs.get(&id2).unwrap().subpool, SubPool::BaseFee);
3390 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3391
3392 let mut block_info = pool.block_info();
3394 block_info.pending_basefee = 450; pool.set_block_info(block_info);
3396
3397 assert_eq!(pool.basefee_pool.len(), 1);
3402 assert_eq!(pool.pending_pool.len(), 2);
3403
3404 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3406
3407 let tx1_meta = pool.all_transactions.txs.get(&id1).unwrap();
3409 let tx2_meta = pool.all_transactions.txs.get(&id2).unwrap();
3410 assert_eq!(tx1_meta.subpool, SubPool::Pending);
3411 assert_eq!(tx2_meta.subpool, SubPool::Pending);
3412 assert!(tx1_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3413 assert!(tx2_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3414
3415 let best: Vec<_> = pool.best_transactions().take(3).collect();
3417 assert_eq!(best.len(), 2); assert!(best.iter().any(|tx| tx.id() == &id1));
3419 assert!(best.iter().any(|tx| tx.id() == &id2));
3420 }
3421
3422 #[test]
3423 fn apply_fee_updates_records_promotions_after_basefee_drop() {
3424 let mut f = MockTransactionFactory::default();
3425 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3426
3427 let tx = MockTransaction::eip1559()
3428 .with_gas_limit(21_000)
3429 .with_max_fee(500)
3430 .with_priority_fee(1);
3431 let validated = f.validated(tx);
3432 let id = *validated.id();
3433 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3434
3435 assert_eq!(pool.pending_pool.len(), 1);
3436
3437 pool.update_basefee(600, |_| {});
3439 assert!(pool.pending_pool.is_empty());
3440 assert_eq!(pool.basefee_pool.len(), 1);
3441
3442 let prev_base_fee = 600;
3443 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3444
3445 pool.all_transactions.pending_fees.base_fee = 400;
3447
3448 let mut outcome = UpdateOutcome::default();
3449 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3450
3451 assert_eq!(pool.pending_pool.len(), 1);
3452 assert!(pool.basefee_pool.is_empty());
3453 assert_eq!(outcome.promoted.len(), 1);
3454 assert_eq!(outcome.promoted[0].id(), &id);
3455 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3456 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3457
3458 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3459 assert_eq!(tx_meta.subpool, SubPool::Pending);
3460 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3461 }
3462
3463 #[test]
3464 fn apply_fee_updates_records_promotions_after_blob_fee_drop() {
3465 let mut f = MockTransactionFactory::default();
3466 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3467
3468 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3469
3470 let tx = MockTransaction::eip4844().with_blob_fee(initial_blob_fee + 100);
3471 let validated = f.validated(tx.clone());
3472 let id = *validated.id();
3473 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3474
3475 assert_eq!(pool.pending_pool.len(), 1);
3476
3477 let increased_blob_fee = tx.max_fee_per_blob_gas().unwrap() + 200;
3479 pool.update_blob_fee(increased_blob_fee, Ordering::Equal, |_| {});
3480 assert!(pool.pending_pool.is_empty());
3481 assert_eq!(pool.blob_pool.len(), 1);
3482
3483 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3484 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3485
3486 pool.all_transactions.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
3488
3489 let mut outcome = UpdateOutcome::default();
3490 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3491
3492 assert_eq!(pool.pending_pool.len(), 1);
3493 assert!(pool.blob_pool.is_empty());
3494 assert_eq!(outcome.promoted.len(), 1);
3495 assert_eq!(outcome.promoted[0].id(), &id);
3496 assert_eq!(pool.all_transactions.pending_fees.base_fee, prev_base_fee);
3497 assert_eq!(pool.all_transactions.pending_fees.blob_fee, tx.max_fee_per_blob_gas().unwrap());
3498
3499 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3500 assert_eq!(tx_meta.subpool, SubPool::Pending);
3501 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3502 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3503 }
3504
3505 #[test]
3506 fn apply_fee_updates_promotes_blob_after_basefee_drop() {
3507 let mut f = MockTransactionFactory::default();
3508 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3509
3510 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3511
3512 let tx = MockTransaction::eip4844()
3513 .with_max_fee(500)
3514 .with_priority_fee(1)
3515 .with_blob_fee(initial_blob_fee + 100);
3516 let validated = f.validated(tx);
3517 let id = *validated.id();
3518 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3519
3520 assert_eq!(pool.pending_pool.len(), 1);
3521
3522 let high_base_fee = 600;
3524 pool.update_basefee(high_base_fee, |_| {});
3525 assert!(pool.pending_pool.is_empty());
3526 assert_eq!(pool.blob_pool.len(), 1);
3527
3528 let prev_base_fee = high_base_fee;
3529 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3530
3531 pool.all_transactions.pending_fees.base_fee = 400;
3533
3534 let mut outcome = UpdateOutcome::default();
3535 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3536
3537 assert_eq!(pool.pending_pool.len(), 1);
3538 assert!(pool.blob_pool.is_empty());
3539 assert_eq!(outcome.promoted.len(), 1);
3540 assert_eq!(outcome.promoted[0].id(), &id);
3541 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3542 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3543
3544 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3545 assert_eq!(tx_meta.subpool, SubPool::Pending);
3546 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3547 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3548 }
3549
3550 #[test]
3551 fn apply_fee_updates_demotes_after_basefee_rise() {
3552 let mut f = MockTransactionFactory::default();
3553 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3554
3555 let tx = MockTransaction::eip1559()
3556 .with_gas_limit(21_000)
3557 .with_max_fee(400)
3558 .with_priority_fee(1);
3559 let validated = f.validated(tx);
3560 let id = *validated.id();
3561 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3562
3563 assert_eq!(pool.pending_pool.len(), 1);
3564
3565 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3566 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3567
3568 let new_base_fee = prev_base_fee + 1_000;
3570 pool.all_transactions.pending_fees.base_fee = new_base_fee;
3571
3572 let mut outcome = UpdateOutcome::default();
3573 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3574
3575 assert!(pool.pending_pool.is_empty());
3576 assert_eq!(pool.basefee_pool.len(), 1);
3577 assert!(outcome.promoted.is_empty());
3578 assert_eq!(pool.all_transactions.pending_fees.base_fee, new_base_fee);
3579 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3580
3581 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3582 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
3583 assert!(!tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3584 }
3585
3586 #[test]
3587 fn get_highest_transaction_by_sender_and_nonce() {
3588 let mut f = MockTransactionFactory::default();
3590 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3591
3592 let tx = MockTransaction::eip1559();
3594 pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0, None).unwrap();
3595
3596 let tx1 = tx.inc_price().next();
3598
3599 let tx1_validated = f.validated(tx1.clone());
3601 pool.add_transaction(tx1_validated, U256::from(1_000), 0, None).unwrap();
3602
3603 assert_eq!(
3605 pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
3606 Some(1)
3607 );
3608
3609 let highest_tx = pool
3611 .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
3612 .expect("Failed to retrieve highest transaction");
3613
3614 assert_eq!(highest_tx.as_ref().transaction, tx1);
3616 }
3617
3618 #[test]
3619 fn get_highest_consecutive_transaction_by_sender() {
3620 let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
3622 let mut f = MockTransactionFactory::default();
3623
3624 let sender = Address::random();
3626 let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
3627 for nonce in txs {
3628 let mut mock_tx = MockTransaction::eip1559();
3629 mock_tx.set_sender(sender);
3630 mock_tx.set_nonce(nonce);
3631
3632 let validated_tx = f.validated(mock_tx);
3633 pool.add_transaction(validated_tx, U256::from(1000), 0, None).unwrap();
3634 }
3635
3636 let sender_id = f.ids.sender_id(&sender).unwrap();
3638 let next_tx =
3639 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
3640 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
3641
3642 let next_tx =
3643 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
3644 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
3645
3646 let next_tx =
3647 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3648 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
3649
3650 let mut info = SenderInfo::default();
3652 info.update(8, U256::ZERO);
3653 pool.all_transactions.sender_info.insert(sender_id, info);
3654 let next_tx =
3655 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3656 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
3657 }
3658
3659 #[test]
3660 fn discard_nonce_too_low() {
3661 let mut f = MockTransactionFactory::default();
3662 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3663
3664 let tx = MockTransaction::eip1559().inc_price_by(10);
3665 let validated = f.validated(tx.clone());
3666 let id = *validated.id();
3667 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3668
3669 let next = tx.next();
3670 let validated = f.validated(next.clone());
3671 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3672
3673 assert_eq!(pool.pending_pool.len(), 2);
3674
3675 let mut changed_senders = HashMap::default();
3676 changed_senders.insert(
3677 id.sender,
3678 SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
3679 );
3680 let outcome = pool.update_accounts(changed_senders);
3681 assert_eq!(outcome.discarded.len(), 1);
3682 assert_eq!(pool.pending_pool.len(), 1);
3683 }
3684
3685 #[test]
3686 fn discard_with_large_blob_txs() {
3687 reth_tracing::init_test_tracing();
3689
3690 let mut f = MockTransactionFactory::default();
3692 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3693 let default_limits = pool.config.blob_limit;
3694
3695 let a_sender = address!("0x000000000000000000000000000000000000000a");
3698
3699 let mut block_info = pool.block_info();
3701 block_info.pending_blob_fee = Some(100);
3702 block_info.pending_basefee = 100;
3703
3704 pool.set_block_info(block_info);
3706
3707 let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
3709 .into_iter()
3710 .map(|mut tx| {
3711 tx.set_size(default_limits.max_size / 2 + 1);
3712 tx.set_max_fee((block_info.pending_basefee - 1).into());
3713 tx
3714 })
3715 .collect::<Vec<_>>();
3716
3717 for tx in a_txs {
3719 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3720 }
3721
3722 let removed = pool.discard_worst();
3724 assert_eq!(removed.len(), 1);
3725 }
3726
3727 #[test]
3728 fn discard_with_parked_large_txs() {
3729 reth_tracing::init_test_tracing();
3731
3732 let mut f = MockTransactionFactory::default();
3734 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3735 let default_limits = pool.config.queued_limit;
3736
3737 let a_sender = address!("0x000000000000000000000000000000000000000a");
3740
3741 let pool_base_fee = 100;
3743 pool.update_basefee(pool_base_fee, |_| {});
3744
3745 let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
3747 .into_iter()
3748 .map(|mut tx| {
3749 tx.set_size(default_limits.max_size / 2 + 1);
3750 tx.set_max_fee((pool_base_fee - 1).into());
3751 tx
3752 })
3753 .collect::<Vec<_>>();
3754
3755 for tx in a_txs {
3757 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3758 }
3759
3760 let removed = pool.discard_worst();
3762 assert_eq!(removed.len(), 1);
3763 }
3764
3765 #[test]
3766 fn discard_at_capacity() {
3767 let mut f = MockTransactionFactory::default();
3768 let queued_limit = SubPoolLimit::new(1000, usize::MAX);
3769 let mut pool =
3770 TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
3771
3772 for _ in 0..queued_limit.max_txs {
3774 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3775 let validated = f.validated(tx.clone());
3776 let _id = *validated.id();
3777 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3778 }
3779
3780 let size = pool.size();
3781 assert_eq!(size.queued, queued_limit.max_txs);
3782
3783 for _ in 0..queued_limit.max_txs {
3784 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3785 let validated = f.validated(tx.clone());
3786 let _id = *validated.id();
3787 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3788
3789 pool.discard_worst();
3790 pool.assert_invariants();
3791 assert!(pool.size().queued <= queued_limit.max_txs);
3792 }
3793 }
3794
3795 #[test]
3796 fn discard_blobs_at_capacity() {
3797 let mut f = MockTransactionFactory::default();
3798 let blob_limit = SubPoolLimit::new(1000, usize::MAX);
3799 let mut pool =
3800 TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
3801 pool.all_transactions.pending_fees.blob_fee = 10000;
3802 for _ in 0..blob_limit.max_txs {
3804 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3805 let validated = f.validated(tx.clone());
3806 let _id = *validated.id();
3807 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3808 }
3809
3810 let size = pool.size();
3811 assert_eq!(size.blob, blob_limit.max_txs);
3812
3813 for _ in 0..blob_limit.max_txs {
3814 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3815 let validated = f.validated(tx.clone());
3816 let _id = *validated.id();
3817 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3818
3819 pool.discard_worst();
3820 pool.assert_invariants();
3821 assert!(pool.size().blob <= blob_limit.max_txs);
3822 }
3823 }
3824
3825 #[test]
3826 fn account_updates_sender_balance() {
3827 let mut on_chain_balance = U256::from(100);
3828 let on_chain_nonce = 0;
3829 let mut f = MockTransactionFactory::default();
3830 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3831
3832 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3833 let tx_1 = tx_0.next();
3834 let tx_2 = tx_1.next();
3835
3836 let v0 = f.validated(tx_0);
3838 let v1 = f.validated(tx_1);
3839 let v2 = f.validated(tx_2);
3840
3841 let _res =
3842 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3843 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3844 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3845
3846 assert_eq!(1, pool.pending_transactions().len());
3848 assert_eq!(2, pool.queued_transactions().len());
3849
3850 let mut updated_accounts = HashMap::default();
3852 on_chain_balance = U256::from(300);
3853 updated_accounts.insert(
3854 v0.sender_id(),
3855 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3856 );
3857 pool.update_accounts(updated_accounts.clone());
3858
3859 assert_eq!(3, pool.pending_transactions().len());
3860 assert!(pool.queued_transactions().is_empty());
3861
3862 updated_accounts.entry(v0.sender_id()).and_modify(|v| v.balance = U256::from(1));
3864 pool.update_accounts(updated_accounts);
3865
3866 assert!(pool.pending_transactions().is_empty());
3867 assert_eq!(3, pool.queued_transactions().len());
3868 }
3869
3870 #[test]
3871 fn account_updates_nonce_gap() {
3872 let on_chain_balance = U256::from(10_000);
3873 let mut on_chain_nonce = 0;
3874 let mut f = MockTransactionFactory::default();
3875 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3876
3877 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3878 let tx_1 = tx_0.next();
3879 let tx_2 = tx_1.next();
3880
3881 let v0 = f.validated(tx_0);
3883 let v1 = f.validated(tx_1);
3884 let v2 = f.validated(tx_2);
3885
3886 let _res =
3888 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3889 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3890
3891 assert!(pool.queued_transactions().is_empty());
3892 assert_eq!(2, pool.pending_transactions().len());
3893
3894 pool.remove_transaction_by_hash(v0.hash());
3896
3897 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3899
3900 assert_eq!(2, pool.queued_transactions().len());
3902 assert!(pool.pending_transactions().is_empty());
3903
3904 let mut updated_accounts = HashMap::default();
3906 on_chain_nonce += 1;
3907 updated_accounts.insert(
3908 v0.sender_id(),
3909 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3910 );
3911 pool.update_accounts(updated_accounts);
3912
3913 assert!(pool.queued_transactions().is_empty());
3915 assert_eq!(2, pool.pending_transactions().len());
3916 }
3917 #[test]
3918 fn test_transaction_removal() {
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
3927 let v0 = f.validated(tx_0);
3929 let v1 = f.validated(tx_1);
3930
3931 let _res =
3933 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3934 let _res =
3935 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3936
3937 assert_eq!(0, pool.queued_transactions().len());
3938 assert_eq!(2, pool.pending_transactions().len());
3939
3940 pool.remove_transaction(v0.id());
3942 let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
3944 assert_eq!(vec![v1.nonce()], pool_txs);
3945 }
3946 #[test]
3947 fn test_remove_transactions() {
3948 let on_chain_balance = U256::from(10_000);
3949 let on_chain_nonce = 0;
3950 let mut f = MockTransactionFactory::default();
3951 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3952
3953 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3954 let tx_1 = tx_0.next();
3955 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3956 let tx_3 = tx_2.next();
3957
3958 let v0 = f.validated(tx_0);
3960 let v1 = f.validated(tx_1);
3961 let v2 = f.validated(tx_2);
3962 let v3 = f.validated(tx_3);
3963
3964 let _res =
3966 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3967 let _res =
3968 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3969 let _res =
3970 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3971 let _res =
3972 pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3973
3974 assert_eq!(0, pool.queued_transactions().len());
3975 assert_eq!(4, pool.pending_transactions().len());
3976
3977 pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
3978
3979 assert_eq!(2, pool.queued_transactions().len());
3980 assert!(pool.pending_transactions().is_empty());
3981 assert!(pool.contains(v1.hash()));
3982 assert!(pool.contains(v3.hash()));
3983 }
3984
3985 #[test]
3986 fn test_remove_transactions_middle_pending_hash() {
3987 let on_chain_balance = U256::from(10_000);
3988 let on_chain_nonce = 0;
3989 let mut f = MockTransactionFactory::default();
3990 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3991
3992 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3993 let tx_1 = tx_0.next();
3994 let tx_2 = tx_1.next();
3995 let tx_3 = tx_2.next();
3996
3997 let v0 = f.validated(tx_0);
3999 let v1 = f.validated(tx_1);
4000 let v2 = f.validated(tx_2);
4001 let v3 = f.validated(tx_3);
4002
4003 let _res = pool.add_transaction(v0, on_chain_balance, on_chain_nonce, None).unwrap();
4005 let _res =
4006 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4007 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4008 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4009
4010 assert_eq!(0, pool.queued_transactions().len());
4011 assert_eq!(4, pool.pending_transactions().len());
4012
4013 let mut removed_txs = pool.remove_transactions(vec![*v1.hash()]);
4014 assert_eq!(1, removed_txs.len());
4015
4016 assert_eq!(2, pool.queued_transactions().len());
4017 assert_eq!(1, pool.pending_transactions().len());
4018
4019 let removed_tx = removed_txs.pop().unwrap();
4021 let v1 = f.validated(removed_tx.transaction.clone());
4022 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4023 assert_eq!(0, pool.queued_transactions().len());
4024 assert_eq!(4, pool.pending_transactions().len());
4025 }
4026
4027 #[test]
4028 fn test_remove_transactions_and_descendants() {
4029 let on_chain_balance = U256::from(10_000);
4030 let on_chain_nonce = 0;
4031 let mut f = MockTransactionFactory::default();
4032 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4033
4034 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4035 let tx_1 = tx_0.next();
4036 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4037 let tx_3 = tx_2.next();
4038 let tx_4 = tx_3.next();
4039
4040 let v0 = f.validated(tx_0);
4042 let v1 = f.validated(tx_1);
4043 let v2 = f.validated(tx_2);
4044 let v3 = f.validated(tx_3);
4045 let v4 = f.validated(tx_4);
4046
4047 let _res =
4049 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4050 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4051 let _res =
4052 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4053 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4054 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4055
4056 assert_eq!(0, pool.queued_transactions().len());
4057 assert_eq!(5, pool.pending_transactions().len());
4058
4059 pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
4060
4061 assert_eq!(0, pool.queued_transactions().len());
4062 assert_eq!(0, pool.pending_transactions().len());
4063 }
4064 #[test]
4065 fn test_remove_descendants() {
4066 let on_chain_balance = U256::from(10_000);
4067 let on_chain_nonce = 0;
4068 let mut f = MockTransactionFactory::default();
4069 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4070
4071 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4072 let tx_1 = tx_0.next();
4073 let tx_2 = tx_1.next();
4074 let tx_3 = tx_2.next();
4075
4076 let v0 = f.validated(tx_0);
4078 let v1 = f.validated(tx_1);
4079 let v2 = f.validated(tx_2);
4080 let v3 = f.validated(tx_3);
4081
4082 let _res =
4084 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4085 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4086 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4087 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4088
4089 assert_eq!(0, pool.queued_transactions().len());
4090 assert_eq!(4, pool.pending_transactions().len());
4091
4092 let mut removed = Vec::new();
4093 pool.remove_transaction(v0.id());
4094 pool.remove_descendants(v0.id(), &mut removed);
4095
4096 assert_eq!(0, pool.queued_transactions().len());
4097 assert_eq!(0, pool.pending_transactions().len());
4098 assert_eq!(3, removed.len());
4099 }
4100 #[test]
4101 fn test_remove_transactions_by_sender() {
4102 let on_chain_balance = U256::from(10_000);
4103 let on_chain_nonce = 0;
4104 let mut f = MockTransactionFactory::default();
4105 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4106
4107 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4108 let tx_1 = tx_0.next();
4109 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4110 let tx_3 = tx_2.next();
4111 let tx_4 = tx_3.next();
4112
4113 let v0 = f.validated(tx_0);
4115 let v1 = f.validated(tx_1);
4116 let v2 = f.validated(tx_2);
4117 let v3 = f.validated(tx_3);
4118 let v4 = f.validated(tx_4);
4119
4120 let _res =
4122 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4123 let _res =
4124 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4125 let _res =
4126 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4127 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4128 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4129
4130 assert_eq!(0, pool.queued_transactions().len());
4131 assert_eq!(5, pool.pending_transactions().len());
4132
4133 pool.remove_transactions_by_sender(v2.sender_id());
4134
4135 assert_eq!(0, pool.queued_transactions().len());
4136 assert_eq!(2, pool.pending_transactions().len());
4137 assert!(pool.contains(v0.hash()));
4138 assert!(pool.contains(v1.hash()));
4139 }
4140 #[test]
4141 fn wrong_best_order_of_transactions() {
4142 let on_chain_balance = U256::from(10_000);
4143 let mut on_chain_nonce = 0;
4144 let mut f = MockTransactionFactory::default();
4145 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4146
4147 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4148 let tx_1 = tx_0.next();
4149 let tx_2 = tx_1.next();
4150 let tx_3 = tx_2.next();
4151
4152 let v0 = f.validated(tx_0);
4154 let v1 = f.validated(tx_1);
4155 let v2 = f.validated(tx_2);
4156 let v3 = f.validated(tx_3);
4157
4158 let _res =
4160 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4161 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4162
4163 assert_eq!(0, pool.queued_transactions().len());
4164 assert_eq!(2, pool.pending_transactions().len());
4165
4166 pool.remove_transaction(v0.id());
4168
4169 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4171
4172 assert_eq!(1, pool.queued_transactions().len());
4174 assert_eq!(1, pool.pending_transactions().len());
4175
4176 let mut updated_accounts = HashMap::default();
4178 on_chain_nonce += 1;
4179 updated_accounts.insert(
4180 v0.sender_id(),
4181 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4182 );
4183 pool.update_accounts(updated_accounts);
4184
4185 assert_eq!(0, pool.queued_transactions().len());
4188 assert_eq!(2, pool.pending_transactions().len());
4189
4190 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4192 assert_eq!(0, pool.queued_transactions().len());
4193 assert_eq!(3, pool.pending_transactions().len());
4194
4195 assert_eq!(
4198 pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
4199 vec![1, 2, 3]
4200 );
4201 }
4202
4203 #[test]
4204 fn test_best_with_attributes() {
4205 let on_chain_balance = U256::MAX;
4206 let on_chain_nonce = 0;
4207 let mut f = MockTransactionFactory::default();
4208 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4209
4210 let base_fee: u128 = 100;
4211 let blob_fee: u128 = 100;
4212
4213 let mut block_info = pool.block_info();
4215 block_info.pending_basefee = base_fee as u64;
4216 block_info.pending_blob_fee = Some(blob_fee);
4217 pool.set_block_info(block_info);
4218
4219 let tx1 = MockTransaction::eip4844()
4221 .with_sender(Address::with_last_byte(1))
4222 .with_max_fee(base_fee + 10)
4223 .with_blob_fee(blob_fee + 10);
4224 let tx2 = MockTransaction::eip4844()
4225 .with_sender(Address::with_last_byte(2))
4226 .with_max_fee(base_fee + 10)
4227 .with_blob_fee(blob_fee);
4228 let tx3 = MockTransaction::eip4844()
4229 .with_sender(Address::with_last_byte(3))
4230 .with_max_fee(base_fee)
4231 .with_blob_fee(blob_fee + 10);
4232 let tx4 = MockTransaction::eip4844()
4233 .with_sender(Address::with_last_byte(4))
4234 .with_max_fee(base_fee)
4235 .with_blob_fee(blob_fee);
4236 let tx5 = MockTransaction::eip4844()
4237 .with_sender(Address::with_last_byte(5))
4238 .with_max_fee(base_fee)
4239 .with_blob_fee(blob_fee - 10);
4240 let tx6 = MockTransaction::eip4844()
4241 .with_sender(Address::with_last_byte(6))
4242 .with_max_fee(base_fee - 10)
4243 .with_blob_fee(blob_fee);
4244 let tx7 = MockTransaction::eip4844()
4245 .with_sender(Address::with_last_byte(7))
4246 .with_max_fee(base_fee - 10)
4247 .with_blob_fee(blob_fee - 10);
4248
4249 for tx in vec![
4250 tx1.clone(),
4251 tx2.clone(),
4252 tx3.clone(),
4253 tx4.clone(),
4254 tx5.clone(),
4255 tx6.clone(),
4256 tx7.clone(),
4257 ] {
4258 pool.add_transaction(f.validated(tx.clone()), on_chain_balance, on_chain_nonce, None)
4259 .unwrap();
4260 }
4261
4262 let base_fee = base_fee as u64;
4263 let blob_fee = blob_fee as u64;
4264
4265 let cases = vec![
4266 (BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee + 5)), vec![tx1.clone()]),
4268 (
4270 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee)),
4271 vec![tx1.clone(), tx2.clone()],
4272 ),
4273 (
4275 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee - 5)),
4276 vec![tx1.clone(), tx2.clone()],
4277 ),
4278 (
4280 BestTransactionsAttributes::new(base_fee, Some(blob_fee + 5)),
4281 vec![tx1.clone(), tx3.clone()],
4282 ),
4283 (
4285 BestTransactionsAttributes::new(base_fee, Some(blob_fee)),
4286 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
4287 ),
4288 (
4290 BestTransactionsAttributes::new(base_fee, Some(blob_fee - 10)),
4291 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx5.clone()],
4292 ),
4293 (
4295 BestTransactionsAttributes::new(base_fee - 5, Some(blob_fee + 5)),
4296 vec![tx1.clone(), tx3.clone()],
4297 ),
4298 (
4300 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee)),
4301 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx6.clone()],
4302 ),
4303 (
4305 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee - 10)),
4306 vec![tx1, tx2, tx5, tx3, tx4, tx6, tx7],
4307 ),
4308 ];
4309
4310 for (idx, (attribute, expected)) in cases.into_iter().enumerate() {
4311 let mut best = pool.best_transactions_with_attributes(attribute);
4312
4313 for (tx_idx, expected_tx) in expected.into_iter().enumerate() {
4314 let tx = best.next().expect("Transaction should be returned");
4315 assert_eq!(
4316 tx.transaction,
4317 expected_tx,
4318 "Failed tx {} in case {}",
4319 tx_idx + 1,
4320 idx + 1
4321 );
4322 }
4323
4324 assert!(best.next().is_none());
4326 }
4327 }
4328
4329 #[test]
4330 fn test_pending_ordering() {
4331 let mut f = MockTransactionFactory::default();
4332 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4333
4334 let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
4335 let tx_1 = tx_0.next();
4336
4337 let v0 = f.validated(tx_0);
4338 let v1 = f.validated(tx_1);
4339
4340 pool.add_transaction(v0.clone(), U256::MAX, 0, None).unwrap();
4342 assert_eq!(1, pool.queued_transactions().len());
4343
4344 pool.add_transaction(v1, U256::MAX, 1, None).unwrap();
4346
4347 assert_eq!(2, pool.pending_transactions().len());
4348 assert_eq!(0, pool.queued_transactions().len());
4349
4350 assert_eq!(
4351 pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
4352 v0.nonce()
4353 );
4354 }
4355
4356 #[test]
4358 fn one_sender_one_independent_transaction() {
4359 let mut on_chain_balance = U256::from(4_999); let mut on_chain_nonce = 40;
4361 let mut f = MockTransactionFactory::default();
4362 let mut pool = TxPool::mock();
4363 let mut submitted_txs = Vec::new();
4364
4365 let template =
4367 MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
4368
4369 for tx_nonce in 40..48 {
4372 let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
4373 submitted_txs.push(*tx.id());
4374 pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap();
4375 }
4376
4377 on_chain_balance = U256::from(999_999);
4380 on_chain_nonce = 42;
4381 pool.remove_transaction(&submitted_txs[0]);
4382 pool.remove_transaction(&submitted_txs[1]);
4383
4384 for tx_nonce in 48..52 {
4386 pool.add_transaction(
4387 f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
4388 on_chain_balance,
4389 on_chain_nonce,
4390 None,
4391 )
4392 .unwrap();
4393 }
4394
4395 let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
4396 assert_eq!(best_txs.len(), 10); assert_eq!(pool.pending_pool.independent().len(), 1);
4399 }
4400
4401 #[test]
4402 fn test_insertion_disorder() {
4403 let mut f = MockTransactionFactory::default();
4404 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4405
4406 let sender = address!("0x1234567890123456789012345678901234567890");
4407 let tx0 = f.validated_arc(
4408 MockTransaction::legacy().with_sender(sender).with_nonce(0).with_gas_price(10),
4409 );
4410 let tx1 = f.validated_arc(
4411 MockTransaction::eip1559()
4412 .with_sender(sender)
4413 .with_nonce(1)
4414 .with_gas_limit(1000)
4415 .with_gas_price(10),
4416 );
4417 let tx2 = f.validated_arc(
4418 MockTransaction::legacy().with_sender(sender).with_nonce(2).with_gas_price(10),
4419 );
4420 let tx3 = f.validated_arc(
4421 MockTransaction::legacy().with_sender(sender).with_nonce(3).with_gas_price(10),
4422 );
4423
4424 pool.add_transaction((*tx0).clone(), U256::from(1000), 0, None).unwrap();
4426 let mut best = pool.best_transactions();
4427 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4428 assert_eq!(t0.id(), tx0.id());
4429 pool.add_transaction((*tx1).clone(), U256::from(1000), 0, None).unwrap();
4431 let mut best = pool.best_transactions();
4432 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4433 assert_eq!(t0.id(), tx0.id());
4434 assert!(best.next().is_none());
4435
4436 pool.add_transaction((*tx2).clone(), U256::MAX, 0, None).unwrap();
4438
4439 let mut best = pool.best_transactions();
4440
4441 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4442 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4443 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4444 assert_eq!(t0.id(), tx0.id());
4445 assert_eq!(t1.id(), tx1.id());
4446 assert_eq!(t2.id(), tx2.id());
4447
4448 pool.add_transaction((*tx3).clone(), U256::MAX, 0, None).unwrap();
4450 let mut best = pool.best_transactions();
4451 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4452 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4453 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4454 let t3 = best.next().expect("tx3 should be put in the pending subpool");
4455 assert_eq!(t0.id(), tx0.id());
4456 assert_eq!(t1.id(), tx1.id());
4457 assert_eq!(t2.id(), tx2.id());
4458 assert_eq!(t3.id(), tx3.id());
4459 }
4460
4461 #[test]
4462 fn test_non_4844_blob_fee_bit_invariant() {
4463 let mut f = MockTransactionFactory::default();
4464 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4465
4466 let non_4844_tx = MockTransaction::eip1559().set_max_fee(200).inc_limit();
4467 let validated = f.validated(non_4844_tx.clone());
4468
4469 assert!(!non_4844_tx.is_eip4844());
4470 pool.add_transaction(validated.clone(), U256::from(10_000), 0, None).unwrap();
4471
4472 let tx_meta = pool.all_transactions.txs.get(validated.id()).unwrap();
4474 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4475 assert_eq!(tx_meta.subpool, SubPool::Pending);
4476 }
4477
4478 #[test]
4479 fn test_blob_fee_enforcement_only_applies_to_eip4844() {
4480 let mut f = MockTransactionFactory::default();
4481 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4482
4483 let mut block_info = pool.block_info();
4485 block_info.pending_blob_fee = Some(160);
4486 block_info.pending_basefee = 100;
4487 pool.set_block_info(block_info);
4488
4489 let eip4844_tx = MockTransaction::eip4844()
4490 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4491 .with_max_fee(200)
4492 .with_blob_fee(150) .inc_limit();
4494
4495 let non_4844_tx = MockTransaction::eip1559()
4496 .with_sender(address!("0x000000000000000000000000000000000000000b"))
4497 .set_max_fee(200)
4498 .inc_limit();
4499
4500 let validated_4844 = f.validated(eip4844_tx);
4501 let validated_non_4844 = f.validated(non_4844_tx);
4502
4503 pool.add_transaction(validated_4844.clone(), U256::from(10_000), 0, None).unwrap();
4504 pool.add_transaction(validated_non_4844.clone(), U256::from(10_000), 0, None).unwrap();
4505
4506 let tx_4844_meta = pool.all_transactions.txs.get(validated_4844.id()).unwrap();
4507 let tx_non_4844_meta = pool.all_transactions.txs.get(validated_non_4844.id()).unwrap();
4508
4509 assert!(!tx_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4511 assert_eq!(tx_4844_meta.subpool, SubPool::Blob);
4512
4513 assert!(tx_non_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4515 assert_eq!(tx_non_4844_meta.subpool, SubPool::Pending);
4516 }
4517
4518 #[test]
4519 fn test_basefee_decrease_preserves_non_4844_blob_fee_bit() {
4520 let mut f = MockTransactionFactory::default();
4521 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4522
4523 let non_4844_tx = MockTransaction::eip1559()
4525 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4526 .set_max_fee(500) .inc_limit();
4528
4529 pool.update_basefee(600, |_| {});
4531
4532 let validated = f.validated(non_4844_tx);
4533 let tx_id = *validated.id();
4534 pool.add_transaction(validated, U256::from(10_000), 0, None).unwrap();
4535
4536 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4538 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
4539 assert!(
4540 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4541 "Non-4844 tx in BaseFee pool must retain ENOUGH_BLOB_FEE_CAP_BLOCK bit"
4542 );
4543
4544 pool.update_basefee(400, |_| {});
4547
4548 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4550 assert_eq!(
4551 tx_meta.subpool,
4552 SubPool::Pending,
4553 "Non-4844 tx should be promoted from BaseFee to Pending after basefee decrease"
4554 );
4555 assert!(
4556 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4557 "Non-4844 tx must NEVER lose ENOUGH_BLOB_FEE_CAP_BLOCK bit during basefee promotion"
4558 );
4559 assert!(
4560 tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK),
4561 "Non-4844 tx should gain ENOUGH_FEE_CAP_BLOCK bit after basefee decrease"
4562 );
4563 }
4564}