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};
32#[cfg(test)]
33use alloy_primitives::Address;
34use alloy_primitives::{
35 map::{AddressSet, B256Map, B256Set},
36 TxHash, B256,
37};
38use rustc_hash::FxHashMap;
39use smallvec::SmallVec;
40#[cfg(test)]
41use std::collections::{HashMap, HashSet};
42use std::{
43 cmp::Ordering,
44 collections::{btree_map::Entry, hash_map, BTreeMap},
45 fmt,
46 ops::Bound::{Excluded, Unbounded},
47 sync::Arc,
48};
49use tracing::{trace, warn};
50
51#[cfg_attr(doc, aquamarine::aquamarine)]
52pub struct TxPool<T: TransactionOrdering> {
94 pending_pool: PendingPool<T>,
98 config: PoolConfig,
100 queued_pool: ParkedPool<QueuedOrd<T::Transaction>>,
107 basefee_pool: ParkedPool<BasefeeOrd<T::Transaction>>,
112 blob_pool: BlobTransactions<T::Transaction>,
119 all_transactions: AllTransactions<T::Transaction>,
121 metrics: TxPoolMetrics,
123}
124
125impl<T: TransactionOrdering> TxPool<T> {
128 pub fn new(ordering: T, config: PoolConfig) -> Self {
130 Self {
131 pending_pool: PendingPool::with_buffer(
132 ordering,
133 config.max_new_pending_txs_notifications,
134 ),
135 queued_pool: Default::default(),
136 basefee_pool: Default::default(),
137 blob_pool: Default::default(),
138 all_transactions: AllTransactions::new(&config),
139 config,
140 metrics: Default::default(),
141 }
142 }
143
144 pub fn get_highest_nonce_by_sender(&self, sender: SenderId) -> Option<u64> {
146 self.all().txs_iter(sender).last().map(|(_, tx)| tx.transaction.nonce())
147 }
148
149 pub fn get_highest_transaction_by_sender(
152 &self,
153 sender: SenderId,
154 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
155 self.all().txs_iter(sender).last().map(|(_, tx)| Arc::clone(&tx.transaction))
156 }
157
158 pub(crate) fn get_highest_consecutive_transaction_by_sender(
165 &self,
166 mut on_chain: TransactionId,
167 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
168 let mut last_consecutive_tx = None;
169
170 if let Some(current) = self.all_transactions.sender_info.get(&on_chain.sender) {
172 on_chain.nonce = on_chain.nonce.max(current.state_nonce);
173 }
174
175 let mut next_expected_nonce = on_chain.nonce;
176 for (id, tx) in self.all().descendant_txs_inclusive(&on_chain) {
177 if next_expected_nonce != id.nonce {
178 break
179 }
180 next_expected_nonce = id.next_nonce();
181 last_consecutive_tx = Some(tx);
182 }
183
184 last_consecutive_tx.map(|tx| Arc::clone(&tx.transaction))
185 }
186
187 pub(crate) const fn all(&self) -> &AllTransactions<T::Transaction> {
189 &self.all_transactions
190 }
191
192 pub(crate) fn unique_senders(&self) -> AddressSet {
194 self.all_transactions.txs.values().map(|tx| tx.transaction.sender()).collect()
195 }
196
197 pub fn size(&self) -> PoolSize {
199 PoolSize {
200 pending: self.pending_pool.len(),
201 pending_size: self.pending_pool.size(),
202 basefee: self.basefee_pool.len(),
203 basefee_size: self.basefee_pool.size(),
204 queued: self.queued_pool.len(),
205 queued_size: self.queued_pool.size(),
206 blob: self.blob_pool.len(),
207 blob_size: self.blob_pool.size(),
208 total: self.all_transactions.len(),
209 }
210 }
211
212 pub const fn block_info(&self) -> BlockInfo {
214 BlockInfo {
215 block_gas_limit: self.all_transactions.block_gas_limit,
216 last_seen_block_hash: self.all_transactions.last_seen_block_hash,
217 last_seen_block_number: self.all_transactions.last_seen_block_number,
218 pending_basefee: self.all_transactions.pending_fees.base_fee,
219 pending_blob_fee: Some(self.all_transactions.pending_fees.blob_fee),
220 }
221 }
222
223 fn update_blob_fee<F>(
225 &mut self,
226 mut pending_blob_fee: u128,
227 base_fee_update: Ordering,
228 mut on_promoted: F,
229 ) where
230 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
231 {
232 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut pending_blob_fee);
233 match (self.all_transactions.pending_fees.blob_fee.cmp(&pending_blob_fee), base_fee_update)
234 {
235 (Ordering::Equal, Ordering::Equal | Ordering::Greater) => {
236 }
238 (Ordering::Greater, Ordering::Equal | Ordering::Greater) => {
239 let removed =
241 self.pending_pool.update_blob_fee(self.all_transactions.pending_fees.blob_fee);
242 for tx in removed {
243 let to = {
244 let tx =
245 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
246
247 tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
249 tx.subpool = tx.state.into();
250 tx.subpool
251 };
252 self.add_transaction_to_subpool(to, tx);
253 }
254 }
255 (Ordering::Less, _) | (_, Ordering::Less) => {
256 let removed =
258 self.blob_pool.enforce_pending_fees(&self.all_transactions.pending_fees);
259 for tx in removed {
260 let subpool = {
261 let tx_meta =
262 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
263 tx_meta.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
264 tx_meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
265 tx_meta.subpool = tx_meta.state.into();
266 tx_meta.subpool
267 };
268
269 if subpool == SubPool::Pending {
270 on_promoted(&tx);
271 }
272
273 self.add_transaction_to_subpool(subpool, tx);
274 }
275 }
276 }
277 }
278
279 fn update_basefee<F>(&mut self, mut pending_basefee: u64, mut on_promoted: F) -> Ordering
284 where
285 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
286 {
287 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut pending_basefee);
288 match self.all_transactions.pending_fees.base_fee.cmp(&pending_basefee) {
289 Ordering::Equal => {
290 Ordering::Equal
292 }
293 Ordering::Greater => {
294 let removed =
296 self.pending_pool.update_base_fee(self.all_transactions.pending_fees.base_fee);
297 for tx in removed {
298 let to = {
299 let tx =
300 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
301 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
302 tx.subpool = tx.state.into();
303 tx.subpool
304 };
305 self.add_transaction_to_subpool(to, tx);
306 }
307
308 Ordering::Greater
309 }
310 Ordering::Less => {
311 let current_base_fee = self.all_transactions.pending_fees.base_fee;
320 self.basefee_pool.enforce_basefee_with(current_base_fee, |tx| {
321 let subpool = {
323 let meta =
324 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
325 meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
326 meta.subpool = meta.state.into();
327 meta.subpool
328 };
329
330 if subpool == SubPool::Pending {
331 on_promoted(&tx);
332 }
333
334 trace!(target: "txpool", hash=%tx.transaction.hash(), pool=?subpool, "Adding transaction to a subpool");
335 match subpool {
336 SubPool::Queued => self.queued_pool.add_transaction(tx),
337 SubPool::Pending => {
338 self.pending_pool.add_transaction(tx, current_base_fee);
339 }
340 SubPool::Blob => {
341 self.blob_pool.add_transaction(tx);
342 }
343 SubPool::BaseFee => {
344 warn!(target: "txpool", "BaseFee transactions should become Pending after basefee decrease");
347 }
348 }
349 });
350
351 Ordering::Less
352 }
353 }
354 }
355
356 pub fn set_block_info(&mut self, info: BlockInfo) -> UpdateOutcome<T::Transaction> {
362 let mut outcome = UpdateOutcome::default();
363
364 let basefee_ordering = self.update_basefee(info.pending_basefee, |tx| {
366 outcome.promoted.push(tx.clone());
367 });
368 if let Some(blob_fee) = info.pending_blob_fee {
369 self.update_blob_fee(blob_fee, basefee_ordering, |tx| {
370 outcome.promoted.push(tx.clone());
371 })
372 }
373 self.all_transactions.set_block_info(info);
375
376 outcome
377 }
378
379 pub(crate) fn best_transactions(&self) -> BestTransactions<T> {
382 self.pending_pool.best()
383 }
384
385 pub(crate) fn best_transactions_with_attributes(
392 &self,
393 best_transactions_attributes: BestTransactionsAttributes,
394 ) -> Box<dyn crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T::Transaction>>>>
395 {
396 match best_transactions_attributes.basefee.cmp(&self.all_transactions.pending_fees.base_fee)
399 {
400 Ordering::Equal => {
401 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
405 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
406 Ordering::Less => {
407 let unlocked =
409 self.blob_pool.satisfy_attributes(best_transactions_attributes);
410 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
411 unlocked,
412 best_transactions_attributes.basefee,
413 new_blob_fee,
414 ))
415 }
416 Ordering::Equal => Box::new(self.pending_pool.best()),
417 Ordering::Greater => {
418 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
420 best_transactions_attributes.basefee,
421 best_transactions_attributes.blob_fee.unwrap_or_default(),
422 ))
423 }
424 }
425 }
426 Ordering::Greater => {
427 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
429 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
430 Ordering::Less => {
431 let unlocked =
433 self.blob_pool.satisfy_attributes(best_transactions_attributes);
434 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
435 unlocked,
436 best_transactions_attributes.basefee,
437 new_blob_fee,
438 ))
439 }
440 Ordering::Equal | Ordering::Greater => {
441 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
443 best_transactions_attributes.basefee,
444 new_blob_fee,
445 ))
446 }
447 }
448 }
449 Ordering::Less => {
450 let mut unlocked = self
453 .basefee_pool
454 .satisfy_base_fee_transactions(best_transactions_attributes.basefee);
455
456 unlocked.extend(self.blob_pool.satisfy_attributes(best_transactions_attributes));
458
459 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
460 unlocked,
461 best_transactions_attributes.basefee,
462 best_transactions_attributes.blob_fee.unwrap_or_default(),
463 ))
464 }
465 }
466 }
467
468 pub(crate) fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
470 self.pending_pool.all().collect()
471 }
472 pub(crate) fn pending_transactions_iter(
474 &self,
475 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
476 self.pending_pool.all()
477 }
478
479 pub(crate) fn pending_transactions_count(&self) -> usize {
481 self.pending_pool.len()
482 }
483
484 pub(crate) fn pending_transactions_with_predicate(
486 &self,
487 mut predicate: impl FnMut(&ValidPoolTransaction<T::Transaction>) -> bool,
488 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
489 self.pending_transactions_iter().filter(|tx| predicate(tx)).collect()
490 }
491
492 pub(crate) fn pending_txs_by_sender(
494 &self,
495 sender: SenderId,
496 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
497 self.pending_pool.txs_by_sender(sender).collect()
498 }
499
500 pub(crate) fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
502 self.basefee_pool.all().chain(self.queued_pool.all()).chain(self.blob_pool.all()).collect()
503 }
504
505 pub(crate) fn queued_transactions_count(&self) -> usize {
507 self.basefee_pool.len() + self.queued_pool.len() + self.blob_pool.len()
508 }
509
510 pub fn queued_and_pending_txs_by_sender(
512 &self,
513 sender: SenderId,
514 ) -> (SmallVec<[TransactionId; TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER]>, Vec<TransactionId>) {
515 (self.queued_pool.get_txs_by_sender(sender), self.pending_pool.get_txs_by_sender(sender))
516 }
517
518 pub(crate) fn queued_txs_by_sender(
520 &self,
521 sender: SenderId,
522 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
523 self.basefee_pool
524 .txs_by_sender(sender)
525 .chain(self.queued_pool.txs_by_sender(sender))
526 .chain(self.blob_pool.txs_by_sender(sender))
527 .collect()
528 }
529
530 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
532 self.all_transactions.contains(tx_hash)
533 }
534
535 #[cfg(test)]
537 pub(crate) fn subpool_contains(&self, subpool: SubPool, id: &TransactionId) -> bool {
538 match subpool {
539 SubPool::Queued => self.queued_pool.contains(id),
540 SubPool::Pending => self.pending_pool.contains(id),
541 SubPool::BaseFee => self.basefee_pool.contains(id),
542 SubPool::Blob => self.blob_pool.contains(id),
543 }
544 }
545
546 #[inline]
548 pub(crate) fn is_exceeded(&self) -> bool {
549 self.config.is_exceeded(self.size())
550 }
551
552 pub(crate) fn get(
554 &self,
555 tx_hash: &TxHash,
556 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
557 self.all_transactions.by_hash.get(tx_hash).cloned()
558 }
559
560 pub(crate) fn get_all(
562 &self,
563 txs: Vec<TxHash>,
564 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
565 txs.into_iter().filter_map(|tx| self.get(&tx))
566 }
567
568 pub(crate) fn get_transactions_by_sender(
570 &self,
571 sender: SenderId,
572 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
573 self.all_transactions.txs_iter(sender).map(|(_, tx)| Arc::clone(&tx.transaction)).collect()
574 }
575
576 pub(crate) fn get_pending_transaction_by_sender_and_nonce(
578 &self,
579 sender: SenderId,
580 nonce: u64,
581 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
582 self.all_transactions
583 .txs_iter(sender)
584 .find(|(id, tx)| id.nonce == nonce && tx.subpool == SubPool::Pending)
585 .map(|(_, tx)| Arc::clone(&tx.transaction))
586 }
587
588 const fn update_pending_fees_only(
591 &mut self,
592 mut new_base_fee: u64,
593 new_blob_fee: Option<u128>,
594 ) -> (u64, u128) {
595 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut new_base_fee);
596
597 let prev_blob_fee = if let Some(mut blob_fee) = new_blob_fee {
598 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut blob_fee);
599 blob_fee
600 } else {
601 self.all_transactions.pending_fees.blob_fee
602 };
603
604 (new_base_fee, prev_blob_fee)
605 }
606
607 fn apply_fee_updates(
614 &mut self,
615 prev_base_fee: u64,
616 prev_blob_fee: u128,
617 outcome: &mut UpdateOutcome<T::Transaction>,
618 ) {
619 let new_base_fee = self.all_transactions.pending_fees.base_fee;
620 let new_blob_fee = self.all_transactions.pending_fees.blob_fee;
621
622 if new_base_fee == prev_base_fee && new_blob_fee == prev_blob_fee {
623 return;
625 }
626
627 self.all_transactions.pending_fees.base_fee = prev_base_fee;
630 self.all_transactions.pending_fees.blob_fee = prev_blob_fee;
631
632 let base_fee_ordering = self.update_basefee(new_base_fee, |tx| {
633 outcome.promoted.push(tx.clone());
634 });
635
636 self.update_blob_fee(new_blob_fee, base_fee_ordering, |tx| {
637 outcome.promoted.push(tx.clone());
638 });
639 }
640
641 pub(crate) fn update_accounts(
643 &mut self,
644 changed_senders: FxHashMap<SenderId, SenderInfo>,
645 ) -> UpdateOutcome<T::Transaction> {
646 let mut updates = self.all_transactions.update(&changed_senders);
648
649 self.all_transactions.sender_info.extend(changed_senders);
651
652 let mut outcome = UpdateOutcome::default();
654 #[expect(clippy::iter_with_drain, reason = "retain the scratch buffer allocation")]
655 self.process_updates(updates.drain(..), &mut outcome);
656 self.all_transactions.update_buffer = updates;
657 self.update_size_metrics();
659 outcome
660 }
661
662 pub(crate) fn on_canonical_state_change(
667 &mut self,
668 block_info: BlockInfo,
669 mined_transactions: Vec<TxHash>,
670 changed_senders: FxHashMap<SenderId, SenderInfo>,
671 _update_kind: PoolUpdateKind,
672 ) -> OnNewCanonicalStateOutcome<T::Transaction> {
673 let block_hash = block_info.last_seen_block_hash;
675
676 let mut removed_txs_count = 0;
678 for tx_hash in &mined_transactions {
679 if self.prune_transaction_by_hash(tx_hash).is_some() {
680 removed_txs_count += 1;
681 }
682 }
683
684 self.metrics.removed_transactions.increment(removed_txs_count);
686
687 let (prev_base_fee, prev_blob_fee) =
692 self.update_pending_fees_only(block_info.pending_basefee, block_info.pending_blob_fee);
693
694 let mut outcome = self.update_accounts(changed_senders);
696
697 self.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
700
701 self.all_transactions.set_block_info(block_info);
703
704 self.update_transaction_type_metrics();
705 self.metrics.performed_state_updates.increment(1);
706
707 OnNewCanonicalStateOutcome {
708 block_hash,
709 mined: mined_transactions,
710 promoted: outcome.promoted,
711 discarded: outcome.discarded,
712 }
713 }
714
715 pub(crate) fn update_size_metrics(&self) {
717 self.all_transactions.update_size_metrics();
718 let stats = self.size();
719 self.metrics.pending_pool_transactions.set(stats.pending as f64);
720 self.metrics.pending_pool_size_bytes.set(stats.pending_size as f64);
721 self.metrics.basefee_pool_transactions.set(stats.basefee as f64);
722 self.metrics.basefee_pool_size_bytes.set(stats.basefee_size as f64);
723 self.metrics.queued_pool_transactions.set(stats.queued as f64);
724 self.metrics.queued_pool_size_bytes.set(stats.queued_size as f64);
725 self.metrics.blob_pool_transactions.set(stats.blob as f64);
726 self.metrics.blob_pool_size_bytes.set(stats.blob_size as f64);
727 self.metrics.total_transactions.set(stats.total as f64);
728 }
729
730 pub(crate) fn update_transaction_type_metrics(&self) {
732 let counts = &self.all_transactions.tx_type_counts;
733 self.metrics.total_legacy_transactions.set(counts.legacy as f64);
734 self.metrics.total_eip2930_transactions.set(counts.eip2930 as f64);
735 self.metrics.total_eip1559_transactions.set(counts.eip1559 as f64);
736 self.metrics.total_eip4844_transactions.set(counts.eip4844 as f64);
737 self.metrics.total_eip7702_transactions.set(counts.eip7702 as f64);
738 self.metrics.total_other_transactions.set(counts.other as f64);
739 }
740
741 pub(crate) fn add_transaction(
742 &mut self,
743 tx: ValidPoolTransaction<T::Transaction>,
744 on_chain_balance: U256,
745 on_chain_nonce: u64,
746 on_chain_code_hash: Option<B256>,
747 ) -> PoolResult<AddedTransaction<T::Transaction>> {
748 if self.contains(tx.hash()) {
749 return Err(PoolError::new(*tx.hash(), PoolErrorKind::AlreadyImported))
750 }
751
752 self.validate_auth(&tx, on_chain_nonce, on_chain_code_hash)?;
753
754 self.all_transactions
756 .sender_info
757 .entry(tx.sender_id())
758 .or_default()
759 .update(on_chain_nonce, on_chain_balance);
760
761 match self.all_transactions.insert_tx(tx, on_chain_balance, on_chain_nonce) {
762 Ok(InsertOk { transaction, move_to, replaced_tx, mut updates, state }) => {
763 let new_nonce = transaction.id().nonce;
771 let split = updates.partition_point(|u| u.id.nonce < new_nonce);
772 let mut outcome = UpdateOutcome::default();
773 #[expect(clippy::iter_with_drain, reason = "retain the scratch buffer allocation")]
774 let mut drain = updates.drain(..);
775 self.process_updates(drain.by_ref().take(split), &mut outcome);
776 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
777 self.process_updates(drain, &mut outcome);
778 self.all_transactions.update_buffer = updates;
779 let UpdateOutcome { promoted, discarded } = outcome;
780 self.metrics.inserted_transactions.increment(1);
781
782 let replaced = replaced_tx.map(|(tx, _)| tx);
783
784 let res = if move_to.is_pending() {
786 AddedTransaction::Pending(AddedPendingTransaction {
787 transaction,
788 promoted,
789 discarded,
790 replaced,
791 })
792 } else {
793 let queued_reason = state.determine_queued_reason(move_to);
795 AddedTransaction::Parked {
796 transaction,
797 subpool: move_to,
798 replaced,
799 queued_reason,
800 }
801 };
802
803 Ok(res)
804 }
805 Err(err) => {
806 self.metrics.invalid_transactions.increment(1);
808 match err {
809 InsertErr::Underpriced { existing: _, transaction } => Err(PoolError::new(
810 *transaction.hash(),
811 PoolErrorKind::ReplacementUnderpriced,
812 )),
813 InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap } => {
814 Err(PoolError::new(
815 *transaction.hash(),
816 PoolErrorKind::FeeCapBelowMinimumProtocolFeeCap(fee_cap),
817 ))
818 }
819 InsertErr::ExceededSenderTransactionsCapacity { transaction } => {
820 Err(PoolError::new(
821 *transaction.hash(),
822 PoolErrorKind::SpammerExceededCapacity(transaction.sender()),
823 ))
824 }
825 InsertErr::TxGasLimitMoreThanAvailableBlockGas {
826 transaction,
827 block_gas_limit,
828 tx_gas_limit,
829 } => Err(PoolError::new(
830 *transaction.hash(),
831 PoolErrorKind::InvalidTransaction(
832 InvalidPoolTransactionError::ExceedsGasLimit(
833 tx_gas_limit,
834 block_gas_limit,
835 ),
836 ),
837 )),
838 InsertErr::BlobTxHasNonceGap { transaction } => Err(PoolError::new(
839 *transaction.hash(),
840 PoolErrorKind::InvalidTransaction(
841 Eip4844PoolTransactionError::Eip4844NonceGap.into(),
842 ),
843 )),
844 InsertErr::Overdraft { transaction } => Err(PoolError::new(
845 *transaction.hash(),
846 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Overdraft {
847 cost: *transaction.cost(),
848 balance: on_chain_balance,
849 }),
850 )),
851 InsertErr::TxTypeConflict { transaction } => Err(PoolError::new(
852 *transaction.hash(),
853 PoolErrorKind::ExistingConflictingTransactionType(
854 transaction.sender(),
855 transaction.tx_type(),
856 ),
857 )),
858 }
859 }
860 }
861 }
862
863 fn check_delegation_limit(
867 &self,
868 transaction: &ValidPoolTransaction<T::Transaction>,
869 on_chain_nonce: u64,
870 on_chain_code_hash: Option<B256>,
871 ) -> Result<(), PoolError> {
872 if (on_chain_code_hash.is_none() || on_chain_code_hash == Some(KECCAK_EMPTY)) &&
874 !self.all_transactions.auths.contains_key(&transaction.sender_id())
875 {
876 return Ok(())
877 }
878
879 let mut txs_by_sender =
880 self.pending_pool.iter_txs_by_sender(transaction.sender_id()).peekable();
881
882 if txs_by_sender.peek().is_none() {
883 let nonce_gap_distance = transaction.nonce().saturating_sub(on_chain_nonce);
888 if nonce_gap_distance >= self.config.max_inflight_delegated_slot_limit as u64 {
889 return Err(PoolError::new(
890 *transaction.hash(),
891 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
892 Eip7702PoolTransactionError::OutOfOrderTxFromDelegated,
893 )),
894 ))
895 }
896 return Ok(())
897 }
898
899 let mut count = 0;
900 for id in txs_by_sender {
901 if id == &transaction.transaction_id {
902 return Ok(())
904 }
905 count += 1;
906 }
907
908 if count < self.config.max_inflight_delegated_slot_limit {
909 return Ok(())
911 }
912
913 Err(PoolError::new(
914 *transaction.hash(),
915 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
916 Eip7702PoolTransactionError::InflightTxLimitReached,
917 )),
918 ))
919 }
920
921 fn validate_auth(
931 &self,
932 transaction: &ValidPoolTransaction<T::Transaction>,
933 on_chain_nonce: u64,
934 on_chain_code_hash: Option<B256>,
935 ) -> Result<(), PoolError> {
936 self.check_delegation_limit(transaction, on_chain_nonce, on_chain_code_hash)?;
938
939 if let Some(authority_list) = &transaction.authority_ids {
940 for sender_id in authority_list {
941 if self
943 .all_transactions
944 .txs_iter(*sender_id)
945 .nth(self.config.max_inflight_delegated_slot_limit)
946 .is_some()
947 {
948 return Err(PoolError::new(
949 *transaction.hash(),
950 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
951 Eip7702PoolTransactionError::AuthorityReserved,
952 )),
953 ))
954 }
955 }
956 }
957
958 Ok(())
959 }
960
961 fn process_updates(
965 &mut self,
966 updates: impl IntoIterator<Item = PoolUpdate>,
967 outcome: &mut UpdateOutcome<T::Transaction>,
968 ) {
969 let mut removed = 0;
970 for PoolUpdate { id, current, destination } in updates {
971 match destination {
972 Destination::Discard => {
973 if let Some(tx) = self.prune_transaction_by_id(&id) {
975 outcome.discarded.push(tx);
976 }
977 removed += 1;
978 }
979 Destination::Pool(move_to) => {
980 debug_assert_ne!(&move_to, ¤t, "destination must be different");
981 let moved = self.move_transaction(current, move_to, &id);
982 if matches!(move_to, SubPool::Pending) &&
983 let Some(tx) = moved
984 {
985 trace!(target: "txpool", hash=%tx.transaction.hash(), "Promoted transaction to pending");
986 outcome.promoted.push(tx);
987 }
988 }
989 }
990 }
991
992 if removed > 0 {
993 self.metrics.removed_transactions.increment(removed);
994 }
995 }
996
997 fn move_transaction(
1002 &mut self,
1003 from: SubPool,
1004 to: SubPool,
1005 id: &TransactionId,
1006 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1007 let tx = self.remove_from_subpool(from, id)?;
1008 self.add_transaction_to_subpool(to, tx.clone());
1009 Some(tx)
1010 }
1011
1012 pub(crate) fn remove_transactions(
1017 &mut self,
1018 hashes: Vec<TxHash>,
1019 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1020 let txs =
1021 hashes.into_iter().filter_map(|hash| self.remove_transaction_by_hash(&hash)).collect();
1022 self.update_size_metrics();
1023 txs
1024 }
1025
1026 pub(crate) fn remove_transactions_and_descendants(
1028 &mut self,
1029 hashes: Vec<TxHash>,
1030 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1031 let mut removed = Vec::new();
1032 for hash in hashes {
1033 if let Some(tx) = self.remove_transaction_by_hash(&hash) {
1034 removed.push(tx.clone());
1035 self.remove_descendants(tx.id(), &mut removed);
1036 }
1037 }
1038 self.update_size_metrics();
1039 removed
1040 }
1041
1042 pub(crate) fn remove_transactions_by_sender(
1044 &mut self,
1045 sender_id: SenderId,
1046 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1047 let mut removed = Vec::new();
1048 let txs = self.get_transactions_by_sender(sender_id);
1049 for tx in txs {
1050 if let Some(tx) = self.remove_transaction(tx.id()) {
1051 removed.push(tx);
1052 }
1053 }
1054 self.update_size_metrics();
1055 removed
1056 }
1057
1058 pub(crate) fn prune_transactions(
1064 &mut self,
1065 hashes: Vec<TxHash>,
1066 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1067 let txs =
1068 hashes.into_iter().filter_map(|hash| self.prune_transaction_by_hash(&hash)).collect();
1069 self.update_size_metrics();
1070 txs
1071 }
1072
1073 fn remove_transaction(
1077 &mut self,
1078 id: &TransactionId,
1079 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1080 let (tx, pool) = self.all_transactions.remove_transaction(id)?;
1081 self.remove_from_subpool(pool, tx.id())
1082 }
1083
1084 fn remove_transaction_by_hash(
1090 &mut self,
1091 tx_hash: &B256,
1092 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1093 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1094
1095 let mut updates = self.all_transactions.park_descendant_transactions(tx.id());
1097 #[expect(clippy::iter_with_drain, reason = "retain the scratch buffer allocation")]
1098 self.process_updates(updates.drain(..), &mut UpdateOutcome::default());
1099 self.all_transactions.update_buffer = updates;
1100 self.remove_from_subpool(pool, tx.id())
1101 }
1102
1103 fn prune_transaction_by_hash(
1110 &mut self,
1111 tx_hash: &B256,
1112 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1113 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1114 self.remove_from_subpool(pool, tx.id())
1115 }
1116 fn prune_transaction_by_id(
1121 &mut self,
1122 tx_id: &TransactionId,
1123 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1124 let (tx, pool) = self.all_transactions.remove_transaction_by_id(tx_id)?;
1125 self.remove_from_subpool(pool, tx.id())
1126 }
1127
1128 fn remove_from_subpool(
1132 &mut self,
1133 pool: SubPool,
1134 tx: &TransactionId,
1135 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1136 let tx = match pool {
1137 SubPool::Queued => self.queued_pool.remove_transaction(tx),
1138 SubPool::Pending => self.pending_pool.remove_transaction(tx),
1139 SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
1140 SubPool::Blob => self.blob_pool.remove_transaction(tx),
1141 };
1142
1143 if let Some(ref tx) = tx {
1144 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Removed transaction from a subpool");
1148 }
1149
1150 tx
1151 }
1152
1153 fn remove_descendants(
1157 &mut self,
1158 tx: &TransactionId,
1159 removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
1160 ) {
1161 let mut id = *tx;
1162
1163 loop {
1165 let descendant =
1166 self.all_transactions.descendant_txs_exclusive(&id).map(|(id, _)| *id).next();
1167 if let Some(descendant) = descendant {
1168 if let Some(tx) = self.remove_transaction(&descendant) {
1169 removed.push(tx)
1170 }
1171 id = descendant;
1172 } else {
1173 return
1174 }
1175 }
1176 }
1177
1178 fn add_transaction_to_subpool(
1180 &mut self,
1181 pool: SubPool,
1182 tx: Arc<ValidPoolTransaction<T::Transaction>>,
1183 ) {
1184 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Adding transaction to a subpool");
1188 match pool {
1189 SubPool::Queued => self.queued_pool.add_transaction(tx),
1190 SubPool::Pending => {
1191 self.pending_pool.add_transaction(tx, self.all_transactions.pending_fees.base_fee);
1192 }
1193 SubPool::BaseFee => {
1194 self.basefee_pool.add_transaction(tx);
1195 }
1196 SubPool::Blob => {
1197 self.blob_pool.add_transaction(tx);
1198 }
1199 }
1200 }
1201
1202 fn add_new_transaction(
1205 &mut self,
1206 transaction: Arc<ValidPoolTransaction<T::Transaction>>,
1207 replaced: Option<(Arc<ValidPoolTransaction<T::Transaction>>, SubPool)>,
1208 pool: SubPool,
1209 ) {
1210 if let Some((replaced, replaced_pool)) = replaced {
1211 self.remove_from_subpool(replaced_pool, replaced.id());
1213 }
1214
1215 self.add_transaction_to_subpool(pool, transaction)
1216 }
1217
1218 pub(crate) fn discard_worst(&mut self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1225 let mut removed = Vec::new();
1226
1227 macro_rules! discard_worst {
1229 ($this:ident, $removed:ident, [$($limit:ident => ($pool:ident, $metric:ident)),* $(,)*]) => {
1230 $ (
1231 while $this.$pool.exceeds(&$this.config.$limit)
1232 {
1233 trace!(
1234 target: "txpool",
1235 "discarding transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1236 stringify!($pool),
1237 $this.config.$limit,
1238 $this.$pool.size(),
1239 $this.$pool.len(),
1240 );
1241
1242 let removed_from_subpool = $this.$pool.truncate_pool($this.config.$limit.clone());
1244
1245 trace!(
1246 target: "txpool",
1247 "removed {} transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1248 removed_from_subpool.len(),
1249 stringify!($pool),
1250 $this.config.$limit,
1251 $this.$pool.size(),
1252 $this.$pool.len()
1253 );
1254 $this.metrics.$metric.increment(removed_from_subpool.len() as u64);
1255
1256 for tx in removed_from_subpool {
1258 $this.all_transactions.remove_transaction(tx.id());
1259
1260 let id = *tx.id();
1261
1262 removed.push(tx);
1264
1265 $this.remove_descendants(&id, &mut $removed);
1267 }
1268 }
1269
1270 )*
1271 };
1272 }
1273
1274 discard_worst!(
1275 self, removed, [
1276 pending_limit => (pending_pool, pending_transactions_evicted),
1277 basefee_limit => (basefee_pool, basefee_transactions_evicted),
1278 blob_limit => (blob_pool, blob_transactions_evicted),
1279 queued_limit => (queued_pool, queued_transactions_evicted),
1280 ]
1281 );
1282
1283 removed
1284 }
1285
1286 pub(crate) fn len(&self) -> usize {
1288 self.all_transactions.len()
1289 }
1290
1291 pub(crate) fn is_empty(&self) -> bool {
1293 self.all_transactions.is_empty()
1294 }
1295
1296 #[cfg(any(test, feature = "test-utils"))]
1304 pub fn assert_invariants(&self) {
1305 let size = self.size();
1306 let actual = size.basefee + size.pending + size.queued + size.blob;
1307 assert_eq!(
1308 size.total, actual,
1309 "total size must be equal to the sum of all sub-pools, basefee:{}, pending:{}, queued:{}, blob:{}",
1310 size.basefee, size.pending, size.queued, size.blob
1311 );
1312 self.all_transactions.assert_invariants();
1313 self.pending_pool.assert_invariants();
1314 self.basefee_pool.assert_invariants();
1315 self.queued_pool.assert_invariants();
1316 self.blob_pool.assert_invariants();
1317 }
1318}
1319
1320#[cfg(any(test, feature = "test-utils"))]
1321impl TxPool<crate::test_utils::MockOrdering> {
1322 pub fn mock() -> Self {
1324 Self::new(crate::test_utils::MockOrdering::default(), PoolConfig::default())
1325 }
1326}
1327
1328#[cfg(test)]
1329impl<T: TransactionOrdering> Drop for TxPool<T> {
1330 fn drop(&mut self) {
1331 self.assert_invariants();
1332 }
1333}
1334
1335impl<T: TransactionOrdering> TxPool<T> {
1336 pub const fn pending(&self) -> &PendingPool<T> {
1338 &self.pending_pool
1339 }
1340
1341 pub const fn base_fee(&self) -> &ParkedPool<BasefeeOrd<T::Transaction>> {
1343 &self.basefee_pool
1344 }
1345
1346 pub const fn queued(&self) -> &ParkedPool<QueuedOrd<T::Transaction>> {
1348 &self.queued_pool
1349 }
1350}
1351
1352impl<T: TransactionOrdering> fmt::Debug for TxPool<T> {
1353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1354 f.debug_struct("TxPool").field("config", &self.config).finish_non_exhaustive()
1355 }
1356}
1357
1358const FULL_UPDATE_MIN_SENDERS: usize = 1_000;
1363
1364const FULL_UPDATE_SENDER_RATIO: usize = 4;
1366
1367pub(crate) struct AllTransactions<T: PoolTransaction> {
1372 minimal_protocol_basefee: u64,
1376 block_gas_limit: u64,
1378 max_account_slots: usize,
1380 by_hash: B256Map<Arc<ValidPoolTransaction<T>>>,
1382 txs: BTreeMap<TransactionId, PoolInternalTransaction<T>>,
1384 sender_info: FxHashMap<SenderId, SenderInfo>,
1386 tx_counter: FxHashMap<SenderId, usize>,
1388 last_seen_block_number: u64,
1390 last_seen_block_hash: B256,
1392 pending_fees: PendingFees,
1394 last_full_update_fees: PendingFees,
1402 price_bumps: PriceBumpConfig,
1404 local_transactions_config: LocalTransactionConfig,
1406 auths: FxHashMap<SenderId, B256Set>,
1408 tx_type_counts: TxTypeCounts,
1411 update_buffer: Vec<PoolUpdate>,
1414 metrics: AllTransactionsMetrics,
1416}
1417
1418impl<T: PoolTransaction> AllTransactions<T> {
1419 fn new(config: &PoolConfig) -> Self {
1421 Self {
1422 max_account_slots: config.max_account_slots,
1423 price_bumps: config.price_bumps,
1424 local_transactions_config: config.local_transactions_config.clone(),
1425 minimal_protocol_basefee: config.minimal_protocol_basefee,
1426 block_gas_limit: config.gas_limit,
1427 ..Default::default()
1428 }
1429 }
1430
1431 #[expect(dead_code)]
1433 pub(crate) fn hashes_iter(&self) -> impl Iterator<Item = TxHash> + '_ {
1434 self.by_hash.keys().copied()
1435 }
1436
1437 pub(crate) fn transactions_iter(
1439 &self,
1440 ) -> impl Iterator<Item = &Arc<ValidPoolTransaction<T>>> + '_ {
1441 self.by_hash.values()
1442 }
1443
1444 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
1446 self.by_hash.contains_key(tx_hash)
1447 }
1448
1449 pub(crate) fn get(&self, id: &TransactionId) -> Option<&PoolInternalTransaction<T>> {
1451 self.txs.get(id)
1452 }
1453
1454 pub(crate) fn tx_inc(&mut self, sender: SenderId) {
1456 let count = self.tx_counter.entry(sender).or_default();
1457 *count += 1;
1458 self.metrics.all_transactions_by_all_senders.increment(1.0);
1459 }
1460
1461 pub(crate) fn tx_decr(&mut self, sender: SenderId) {
1463 if let hash_map::Entry::Occupied(mut entry) = self.tx_counter.entry(sender) {
1464 let count = entry.get_mut();
1465 if *count == 1 {
1466 entry.remove();
1467 self.sender_info.remove(&sender);
1468 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1469 return
1470 }
1471 *count -= 1;
1472 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1473 }
1474 }
1475
1476 fn set_block_info(&mut self, block_info: BlockInfo) {
1478 let BlockInfo {
1479 block_gas_limit,
1480 last_seen_block_hash,
1481 last_seen_block_number,
1482 pending_basefee,
1483 pending_blob_fee,
1484 } = block_info;
1485 self.last_seen_block_number = last_seen_block_number;
1486 self.last_seen_block_hash = last_seen_block_hash;
1487
1488 self.pending_fees.base_fee = pending_basefee;
1489 self.metrics.base_fee.set(pending_basefee as f64);
1490
1491 self.block_gas_limit = block_gas_limit;
1492
1493 if let Some(pending_blob_fee) = pending_blob_fee {
1494 self.pending_fees.blob_fee = pending_blob_fee;
1495 self.metrics.blob_base_fee.set(pending_blob_fee as f64);
1496 }
1497 }
1498
1499 pub(crate) fn update_size_metrics(&self) {
1501 self.metrics.all_transactions_by_hash.set(self.by_hash.len() as f64);
1502 self.metrics.all_transactions_by_id.set(self.txs.len() as f64);
1503 }
1504
1505 pub(crate) fn update(
1522 &mut self,
1523 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1524 ) -> Vec<PoolUpdate> {
1525 let mut updates = std::mem::take(&mut self.update_buffer);
1526 let pending_fees = self.pending_fees;
1527
1528 let update_all = self.last_full_update_fees != self.pending_fees ||
1529 self.should_update_all_senders(changed_accounts.len());
1530
1531 if update_all {
1532 Self::update_txs(pending_fees, changed_accounts, &mut updates, self.txs.iter_mut());
1533 self.last_full_update_fees = self.pending_fees;
1534 } else {
1535 for sender in changed_accounts.keys() {
1538 let range = TransactionId::new(*sender, 0)..=TransactionId::new(*sender, u64::MAX);
1539 Self::update_txs(
1540 pending_fees,
1541 changed_accounts,
1542 &mut updates,
1543 self.txs.range_mut(range),
1544 );
1545 }
1546 }
1547
1548 updates
1549 }
1550
1551 fn should_update_all_senders(&self, changed_sender_count: usize) -> bool {
1553 let pool_sender_count = self.tx_counter.len();
1554 changed_sender_count >= pool_sender_count ||
1555 (pool_sender_count >= FULL_UPDATE_MIN_SENDERS &&
1556 changed_sender_count >= pool_sender_count.div_ceil(FULL_UPDATE_SENDER_RATIO))
1557 }
1558
1559 fn update_txs<'a, I>(
1564 pending_fees: PendingFees,
1565 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1566 updates: &mut Vec<PoolUpdate>,
1567 txs: I,
1568 ) where
1569 T: 'a,
1570 I: Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)>,
1571 {
1572 let mut iter = txs.peekable();
1573
1574 'transactions: while let Some((id, tx)) = iter.next() {
1584 macro_rules! next_sender {
1585 ($iter:ident) => {
1586 'this: while let Some((peek, _)) = iter.peek() {
1587 if peek.sender != id.sender {
1588 break 'this
1589 }
1590 iter.next();
1591 }
1592 };
1593 }
1594
1595 let changed_balance = if let Some(info) = changed_accounts.get(&id.sender) {
1598 if id.nonce < info.state_nonce {
1600 updates.push(PoolUpdate {
1601 id: *tx.transaction.id(),
1602 current: tx.subpool,
1603 destination: Destination::Discard,
1604 });
1605 continue 'transactions
1606 }
1607
1608 let ancestor = TransactionId::ancestor(id.nonce, info.state_nonce, id.sender);
1609 if ancestor.is_none() {
1611 tx.state.insert(TxState::NO_NONCE_GAPS);
1612 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1613 tx.cumulative_cost = U256::ZERO;
1614 if tx.transaction.cost() > &info.balance {
1615 tx.state.remove(TxState::ENOUGH_BALANCE);
1617 } else {
1618 tx.state.insert(TxState::ENOUGH_BALANCE);
1619 }
1620 }
1621
1622 Some(&info.balance)
1623 } else {
1624 None
1625 };
1626
1627 if tx.state.has_nonce_gap() {
1629 next_sender!(iter);
1630 continue 'transactions
1631 }
1632
1633 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1635
1636 Self::update_tx_fees(pending_fees, tx);
1638 Self::record_subpool_update(updates, tx);
1640
1641 let mut has_parked_ancestor = !tx.state.is_pending();
1643
1644 let mut cumulative_cost = tx.next_cumulative_cost();
1645
1646 let mut next_nonce_in_line = tx.transaction.nonce().saturating_add(1);
1648
1649 while let Some((peek, tx)) = iter.peek_mut() {
1651 if peek.sender != id.sender {
1652 continue 'transactions
1654 }
1655
1656 if tx.transaction.nonce() == next_nonce_in_line {
1657 tx.state.insert(TxState::NO_NONCE_GAPS);
1659 } else {
1660 next_sender!(iter);
1662 continue 'transactions
1663 }
1664
1665 next_nonce_in_line = next_nonce_in_line.saturating_add(1);
1667
1668 tx.cumulative_cost = cumulative_cost;
1670 cumulative_cost = tx.next_cumulative_cost();
1672
1673 if let Some(changed_balance) = changed_balance {
1675 if &cumulative_cost > changed_balance {
1676 tx.state.remove(TxState::ENOUGH_BALANCE);
1678 } else {
1679 tx.state.insert(TxState::ENOUGH_BALANCE);
1680 }
1681 }
1682
1683 if has_parked_ancestor {
1685 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1686 } else {
1687 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1688 }
1689
1690 Self::update_tx_fees(pending_fees, tx);
1692 Self::record_subpool_update(updates, tx);
1693 has_parked_ancestor = !tx.state.is_pending();
1694
1695 iter.next();
1697 }
1698 }
1699 }
1700
1701 fn record_subpool_update(updates: &mut Vec<PoolUpdate>, tx: &mut PoolInternalTransaction<T>) {
1706 let current_pool = tx.subpool;
1707 tx.subpool = tx.state.into();
1708 if current_pool != tx.subpool {
1709 updates.push(PoolUpdate {
1710 id: *tx.transaction.id(),
1711 current: current_pool,
1712 destination: tx.subpool.into(),
1713 })
1714 }
1715 }
1716
1717 fn update_tx_fees(pending_fees: PendingFees, tx: &mut PoolInternalTransaction<T>) {
1719 match tx.transaction.max_fee_per_gas().cmp(&(pending_fees.base_fee as u128)) {
1720 Ordering::Greater | Ordering::Equal => {
1721 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1722 }
1723 Ordering::Less => {
1724 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
1725 }
1726 }
1727
1728 match tx.transaction.max_fee_per_blob_gas() {
1729 Some(blob_fee_cap) if blob_fee_cap < pending_fees.blob_fee => {
1730 tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1731 }
1732 _ => {
1733 tx.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1734 }
1735 }
1736 }
1737
1738 pub(crate) fn txs_iter(
1741 &self,
1742 sender: SenderId,
1743 ) -> impl Iterator<Item = (&TransactionId, &PoolInternalTransaction<T>)> + '_ {
1744 self.txs
1745 .range((sender.start_bound(), Unbounded))
1746 .take_while(move |(other, _)| sender == other.sender)
1747 }
1748
1749 #[cfg(test)]
1752 #[expect(dead_code)]
1753 pub(crate) fn txs_iter_mut(
1754 &mut self,
1755 sender: SenderId,
1756 ) -> impl Iterator<Item = (&TransactionId, &mut PoolInternalTransaction<T>)> + '_ {
1757 self.txs
1758 .range_mut((sender.start_bound(), Unbounded))
1759 .take_while(move |(other, _)| sender == other.sender)
1760 }
1761
1762 pub(crate) fn descendant_txs_exclusive<'a, 'b: 'a>(
1766 &'a self,
1767 id: &'b TransactionId,
1768 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1769 self.txs.range((Excluded(id), Unbounded)).take_while(|(other, _)| id.sender == other.sender)
1770 }
1771
1772 pub(crate) fn descendant_txs_inclusive<'a, 'b: 'a>(
1777 &'a self,
1778 id: &'b TransactionId,
1779 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1780 self.txs.range(id..).take_while(|(other, _)| id.sender == other.sender)
1781 }
1782
1783 pub(crate) fn descendant_txs_mut<'a, 'b: 'a>(
1788 &'a mut self,
1789 id: &'b TransactionId,
1790 ) -> impl Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)> + 'a {
1791 self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender)
1792 }
1793
1794 pub(crate) fn remove_transaction_by_hash(
1796 &mut self,
1797 tx_hash: &B256,
1798 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1799 let tx = self.by_hash.remove(tx_hash)?;
1800 let internal = self.txs.remove(&tx.transaction_id)?;
1801 self.remove_auths(&internal);
1802 self.tx_type_counts.dec(internal.transaction.transaction.ty());
1803 self.tx_decr(tx.sender_id());
1805 Some((tx, internal.subpool))
1806 }
1807
1808 pub(crate) fn remove_transaction_by_id(
1812 &mut self,
1813 tx_id: &TransactionId,
1814 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1815 let internal = self.txs.remove(tx_id)?;
1816 let tx = self.by_hash.remove(internal.transaction.hash())?;
1817 self.remove_auths(&internal);
1818 self.tx_type_counts.dec(internal.transaction.transaction.ty());
1819 self.tx_decr(tx.sender_id());
1821 Some((tx, internal.subpool))
1822 }
1823
1824 pub(crate) fn park_descendant_transactions(
1826 &mut self,
1827 tx_id: &TransactionId,
1828 ) -> Vec<PoolUpdate> {
1829 let mut updates = std::mem::take(&mut self.update_buffer);
1830
1831 for (id, tx) in self.descendant_txs_mut(tx_id) {
1832 let current_pool = tx.subpool;
1833
1834 tx.state.remove(TxState::NO_NONCE_GAPS);
1835
1836 tx.subpool = tx.state.into();
1838
1839 if current_pool != tx.subpool {
1841 updates.push(PoolUpdate {
1842 id: *id,
1843 current: current_pool,
1844 destination: tx.subpool.into(),
1845 })
1846 }
1847 }
1848
1849 updates
1850 }
1851
1852 pub(crate) fn remove_transaction(
1858 &mut self,
1859 id: &TransactionId,
1860 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1861 let internal = self.txs.remove(id)?;
1862
1863 self.tx_decr(internal.transaction.sender_id());
1865 self.tx_type_counts.dec(internal.transaction.transaction.ty());
1866
1867 let result =
1868 self.by_hash.remove(internal.transaction.hash()).map(|tx| (tx, internal.subpool));
1869
1870 self.remove_auths(&internal);
1871
1872 result
1873 }
1874
1875 fn remove_auths(&mut self, tx: &PoolInternalTransaction<T>) {
1879 let Some(auths) = &tx.transaction.authority_ids else { return };
1880
1881 let tx_hash = tx.transaction.hash();
1882 for auth in auths {
1883 if let Some(list) = self.auths.get_mut(auth) {
1884 list.remove(tx_hash);
1885 if list.is_empty() {
1886 self.auths.remove(auth);
1887 }
1888 }
1889 }
1890 }
1891
1892 #[inline]
1898 fn contains_conflicting_transaction(&self, tx: &ValidPoolTransaction<T>) -> bool {
1899 self.txs_iter(tx.transaction_id.sender)
1900 .next()
1901 .is_some_and(|(_, existing)| tx.tx_type_conflicts_with(&existing.transaction))
1902 }
1903
1904 fn ensure_valid(
1913 &self,
1914 transaction: ValidPoolTransaction<T>,
1915 on_chain_nonce: u64,
1916 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1917 if !self.local_transactions_config.is_local(transaction.origin, transaction.sender_ref()) {
1918 let current_txs =
1919 self.tx_counter.get(&transaction.sender_id()).copied().unwrap_or_default();
1920
1921 if current_txs >= self.max_account_slots && transaction.nonce() > on_chain_nonce {
1924 return Err(InsertErr::ExceededSenderTransactionsCapacity {
1925 transaction: Arc::new(transaction),
1926 })
1927 }
1928 }
1929 if transaction.gas_limit() > self.block_gas_limit {
1930 return Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas {
1931 block_gas_limit: self.block_gas_limit,
1932 tx_gas_limit: transaction.gas_limit(),
1933 transaction: Arc::new(transaction),
1934 })
1935 }
1936
1937 if self.contains_conflicting_transaction(&transaction) {
1938 return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) })
1940 }
1941
1942 Ok(transaction)
1943 }
1944
1945 fn ensure_valid_blob_transaction(
1951 &self,
1952 new_blob_tx: ValidPoolTransaction<T>,
1953 on_chain_balance: U256,
1954 ancestor: Option<TransactionId>,
1955 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1956 if let Some(ancestor) = ancestor {
1957 let Some(ancestor_tx) = self.txs.get(&ancestor) else {
1958 self.metrics.blob_transactions_nonce_gaps.increment(1);
1960 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1961 };
1962 if ancestor_tx.state.has_nonce_gap() {
1963 self.metrics.blob_transactions_nonce_gaps.increment(1);
1966 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1967 }
1968
1969 let mut cumulative_cost = ancestor_tx.next_cumulative_cost() + new_blob_tx.cost();
1971
1972 if cumulative_cost > on_chain_balance {
1974 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1976 }
1977
1978 let id = new_blob_tx.transaction_id;
1981 let mut descendants = self.descendant_txs_inclusive(&id).peekable();
1982 if let Some((maybe_replacement, _)) = descendants.peek() &&
1983 **maybe_replacement == new_blob_tx.transaction_id
1984 {
1985 descendants.next();
1987
1988 for (_, tx) in descendants {
1990 cumulative_cost += tx.transaction.cost();
1991 if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance {
1992 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1994 }
1995 }
1996 }
1997 } else if new_blob_tx.cost() > &on_chain_balance {
1998 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
2000 }
2001
2002 Ok(new_blob_tx)
2003 }
2004
2005 pub(crate) fn insert_tx(
2037 &mut self,
2038 transaction: ValidPoolTransaction<T>,
2039 on_chain_balance: U256,
2040 on_chain_nonce: u64,
2041 ) -> InsertResult<T> {
2042 assert!(on_chain_nonce <= transaction.nonce(), "Invalid transaction");
2043
2044 let mut transaction = self.ensure_valid(transaction, on_chain_nonce)?;
2045
2046 let inserted_tx_id = *transaction.id();
2047 let mut state = TxState::default();
2048 let mut cumulative_cost = U256::ZERO;
2049
2050 state.insert(TxState::NOT_TOO_MUCH_GAS);
2052
2053 let ancestor = TransactionId::ancestor(
2056 transaction.transaction.nonce(),
2057 on_chain_nonce,
2058 inserted_tx_id.sender,
2059 );
2060
2061 if transaction.is_eip4844() {
2064 state.insert(TxState::BLOB_TRANSACTION);
2065
2066 transaction =
2067 self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
2068 let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();
2069 if blob_fee_cap >= self.pending_fees.blob_fee {
2070 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
2071 }
2072 } else {
2073 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
2075 }
2076
2077 let transaction = Arc::new(transaction);
2078
2079 if ancestor.is_none() {
2081 state.insert(TxState::NO_NONCE_GAPS);
2082 state.insert(TxState::NO_PARKED_ANCESTORS);
2083 }
2084
2085 let fee_cap = transaction.max_fee_per_gas();
2087
2088 if fee_cap < self.minimal_protocol_basefee as u128 {
2089 return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap })
2090 }
2091 if fee_cap >= self.pending_fees.base_fee as u128 {
2092 state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
2093 }
2094
2095 let mut replaced_tx = None;
2097
2098 let pool_tx = PoolInternalTransaction {
2099 transaction: Arc::clone(&transaction),
2100 subpool: state.into(),
2101 state,
2102 cumulative_cost,
2103 };
2104
2105 match self.txs.entry(*transaction.id()) {
2107 Entry::Vacant(entry) => {
2108 self.by_hash.insert(*pool_tx.transaction.hash(), pool_tx.transaction.clone());
2110 self.tx_type_counts.inc(pool_tx.transaction.transaction.ty());
2111 entry.insert(pool_tx);
2112 }
2113 Entry::Occupied(mut entry) => {
2114 let existing_transaction = entry.get().transaction.as_ref();
2116 let maybe_replacement = transaction.as_ref();
2117
2118 if existing_transaction.is_underpriced(maybe_replacement, &self.price_bumps) {
2120 return Err(InsertErr::Underpriced {
2121 transaction: pool_tx.transaction,
2122 existing: *entry.get().transaction.hash(),
2123 })
2124 }
2125 let new_hash = *pool_tx.transaction.hash();
2126 let new_transaction = pool_tx.transaction.clone();
2127 self.tx_type_counts.inc(pool_tx.transaction.transaction.ty());
2128 let replaced = entry.insert(pool_tx);
2129 self.tx_type_counts.dec(replaced.transaction.transaction.ty());
2130 self.by_hash.remove(replaced.transaction.hash());
2131 self.by_hash.insert(new_hash, new_transaction);
2132
2133 self.remove_auths(&replaced);
2134
2135 replaced_tx = Some((replaced.transaction, replaced.subpool));
2137 }
2138 }
2139
2140 if let Some(auths) = &transaction.authority_ids {
2141 let tx_hash = transaction.hash();
2142 for auth in auths {
2143 self.auths.entry(*auth).or_default().insert(*tx_hash);
2144 }
2145 }
2146
2147 let mut updates = std::mem::take(&mut self.update_buffer);
2149
2150 let on_chain_id = TransactionId::new(transaction.sender_id(), on_chain_nonce);
2152 let pending_fees = self.pending_fees;
2153 {
2154 let mut next_nonce = on_chain_id.nonce;
2156
2157 let mut has_parked_ancestor = false;
2161
2162 for (id, tx) in self.descendant_txs_mut(&on_chain_id) {
2165 let current_pool = tx.subpool;
2166
2167 if next_nonce != id.nonce {
2169 break
2170 }
2171
2172 tx.state.insert(TxState::NO_NONCE_GAPS);
2174
2175 tx.cumulative_cost = cumulative_cost;
2177
2178 cumulative_cost = tx.next_cumulative_cost();
2180
2181 if cumulative_cost > on_chain_balance {
2182 tx.state.remove(TxState::ENOUGH_BALANCE);
2184 } else {
2185 tx.state.insert(TxState::ENOUGH_BALANCE);
2186 }
2187
2188 if has_parked_ancestor {
2190 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
2191 } else {
2192 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
2193 }
2194
2195 Self::update_tx_fees(pending_fees, tx);
2196
2197 tx.subpool = tx.state.into();
2199 has_parked_ancestor = !tx.state.is_pending();
2200
2201 if inserted_tx_id.eq(id) {
2202 state = tx.state;
2204 } else {
2205 if current_pool != tx.subpool {
2207 updates.push(PoolUpdate {
2208 id: *id,
2209 current: current_pool,
2210 destination: tx.subpool.into(),
2211 })
2212 }
2213 }
2214
2215 next_nonce = id.next_nonce();
2217 }
2218 }
2219
2220 if replaced_tx.is_none() {
2222 self.tx_inc(inserted_tx_id.sender);
2223 }
2224
2225 Ok(InsertOk { transaction, move_to: state.into(), state, replaced_tx, updates })
2226 }
2227
2228 pub(crate) fn len(&self) -> usize {
2230 self.txs.len()
2231 }
2232
2233 pub(crate) fn is_empty(&self) -> bool {
2235 self.txs.is_empty()
2236 }
2237
2238 #[cfg(any(test, feature = "test-utils"))]
2240 pub(crate) fn assert_invariants(&self) {
2241 assert_eq!(self.by_hash.len(), self.txs.len(), "by_hash.len() != txs.len()");
2242 assert!(self.auths.len() <= self.txs.len(), "auths.len() > txs.len()");
2243 }
2244}
2245
2246#[cfg(test)]
2247impl<T: PoolTransaction> AllTransactions<T> {
2248 pub(crate) fn tx_count(&self, sender: SenderId) -> usize {
2252 self.tx_counter.get(&sender).copied().unwrap_or_default()
2253 }
2254}
2255
2256impl<T: PoolTransaction> Default for AllTransactions<T> {
2257 fn default() -> Self {
2258 Self {
2259 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
2260 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
2261 block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
2262 by_hash: Default::default(),
2263 txs: Default::default(),
2264 sender_info: Default::default(),
2265 tx_counter: Default::default(),
2266 last_seen_block_number: Default::default(),
2267 last_seen_block_hash: Default::default(),
2268 pending_fees: Default::default(),
2269 last_full_update_fees: Default::default(),
2271 price_bumps: Default::default(),
2272 local_transactions_config: Default::default(),
2273 auths: Default::default(),
2274 tx_type_counts: Default::default(),
2275 update_buffer: Default::default(),
2276 metrics: Default::default(),
2277 }
2278 }
2279}
2280
2281#[derive(Debug, Clone, Copy, Default)]
2286pub(crate) struct TxTypeCounts {
2287 legacy: u64,
2288 eip2930: u64,
2289 eip1559: u64,
2290 eip4844: u64,
2291 eip7702: u64,
2292 other: u64,
2293}
2294
2295impl TxTypeCounts {
2296 const fn counter_mut(&mut self, tx_type: u8) -> &mut u64 {
2298 match tx_type {
2299 LEGACY_TX_TYPE_ID => &mut self.legacy,
2300 EIP2930_TX_TYPE_ID => &mut self.eip2930,
2301 EIP1559_TX_TYPE_ID => &mut self.eip1559,
2302 EIP4844_TX_TYPE_ID => &mut self.eip4844,
2303 EIP7702_TX_TYPE_ID => &mut self.eip7702,
2304 _ => &mut self.other,
2305 }
2306 }
2307
2308 const fn inc(&mut self, tx_type: u8) {
2310 *self.counter_mut(tx_type) += 1;
2311 }
2312
2313 const fn dec(&mut self, tx_type: u8) {
2315 *self.counter_mut(tx_type) -= 1;
2316 }
2317}
2318
2319#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2321pub(crate) struct PendingFees {
2322 pub(crate) base_fee: u64,
2324 pub(crate) blob_fee: u128,
2326}
2327
2328impl Default for PendingFees {
2329 fn default() -> Self {
2330 Self { base_fee: Default::default(), blob_fee: BLOB_TX_MIN_BLOB_GASPRICE }
2331 }
2332}
2333
2334pub(crate) type InsertResult<T> = Result<InsertOk<T>, InsertErr<T>>;
2336
2337#[derive(Debug)]
2339pub(crate) enum InsertErr<T: PoolTransaction> {
2340 Underpriced {
2342 transaction: Arc<ValidPoolTransaction<T>>,
2343 #[expect(dead_code)]
2344 existing: TxHash,
2345 },
2346 BlobTxHasNonceGap { transaction: Arc<ValidPoolTransaction<T>> },
2348 Overdraft { transaction: Arc<ValidPoolTransaction<T>> },
2351 FeeCapBelowMinimumProtocolFeeCap { transaction: Arc<ValidPoolTransaction<T>>, fee_cap: u128 },
2355 ExceededSenderTransactionsCapacity { transaction: Arc<ValidPoolTransaction<T>> },
2359 TxGasLimitMoreThanAvailableBlockGas {
2361 transaction: Arc<ValidPoolTransaction<T>>,
2362 block_gas_limit: u64,
2363 tx_gas_limit: u64,
2364 },
2365 TxTypeConflict { transaction: Arc<ValidPoolTransaction<T>> },
2367}
2368
2369#[derive(Debug)]
2371pub(crate) struct InsertOk<T: PoolTransaction> {
2372 transaction: Arc<ValidPoolTransaction<T>>,
2374 move_to: SubPool,
2376 state: TxState,
2378 replaced_tx: Option<(Arc<ValidPoolTransaction<T>>, SubPool)>,
2380 updates: Vec<PoolUpdate>,
2382}
2383
2384#[derive(Debug)]
2387pub(crate) struct PoolInternalTransaction<T: PoolTransaction> {
2388 pub(crate) transaction: Arc<ValidPoolTransaction<T>>,
2390 pub(crate) subpool: SubPool,
2392 pub(crate) state: TxState,
2395 pub(crate) cumulative_cost: U256,
2400}
2401
2402impl<T: PoolTransaction> PoolInternalTransaction<T> {
2405 fn next_cumulative_cost(&self) -> U256 {
2406 self.cumulative_cost + self.transaction.cost()
2407 }
2408}
2409
2410#[derive(Debug, Clone, Default)]
2412pub(crate) struct SenderInfo {
2413 pub(crate) state_nonce: u64,
2415 pub(crate) balance: U256,
2417}
2418
2419impl SenderInfo {
2422 const fn update(&mut self, state_nonce: u64, balance: U256) {
2424 *self = Self { state_nonce, balance };
2425 }
2426}
2427
2428#[cfg(test)]
2429mod tests {
2430 use super::*;
2431 use crate::{
2432 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
2433 traits::TransactionOrigin,
2434 SubPoolLimit,
2435 };
2436 use alloy_consensus::{Transaction, TxType};
2437 use alloy_primitives::address;
2438
2439 #[test]
2440 fn test_insert_blob() {
2441 let on_chain_balance = U256::MAX;
2442 let on_chain_nonce = 0;
2443 let mut f = MockTransactionFactory::default();
2444 let mut pool = AllTransactions::default();
2445 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2446 let valid_tx = f.validated(tx);
2447 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2448 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2449 assert!(updates.is_empty());
2450 assert!(replaced_tx.is_none());
2451 assert!(state.contains(TxState::NO_NONCE_GAPS));
2452 assert!(state.contains(TxState::ENOUGH_BALANCE));
2453 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2454 assert_eq!(move_to, SubPool::Pending);
2455
2456 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2457 assert_eq!(inserted.subpool, SubPool::Pending);
2458 }
2459
2460 #[test]
2461 fn test_insert_blob_not_enough_blob_fee() {
2462 let on_chain_balance = U256::MAX;
2463 let on_chain_nonce = 0;
2464 let mut f = MockTransactionFactory::default();
2465 let mut pool = AllTransactions {
2466 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2467 ..Default::default()
2468 };
2469 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2470 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2471 let valid_tx = f.validated(tx);
2472 let InsertOk { state, .. } =
2473 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2474 assert!(state.contains(TxState::NO_NONCE_GAPS));
2475 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2476
2477 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2478 }
2479
2480 #[test]
2481 fn test_valid_tx_with_decreasing_blob_fee() {
2482 let on_chain_balance = U256::MAX;
2483 let on_chain_nonce = 0;
2484 let mut f = MockTransactionFactory::default();
2485 let mut pool = AllTransactions {
2486 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2487 ..Default::default()
2488 };
2489 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2490
2491 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2492 let valid_tx = f.validated(tx.clone());
2493 let InsertOk { state, .. } =
2494 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2495 assert!(state.contains(TxState::NO_NONCE_GAPS));
2496 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2497
2498 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2499 pool.remove_transaction(&valid_tx.transaction_id);
2500
2501 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
2502 let InsertOk { state, .. } =
2503 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2504 assert!(state.contains(TxState::NO_NONCE_GAPS));
2505 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2506 }
2507
2508 #[test]
2509 fn test_demote_valid_tx_with_increasing_blob_fee() {
2510 let on_chain_balance = U256::MAX;
2511 let on_chain_nonce = 0;
2512 let mut f = MockTransactionFactory::default();
2513 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2514 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2515
2516 let mut block_info = pool.block_info();
2518 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2519 pool.set_block_info(block_info);
2520
2521 let validated = f.validated(tx.clone());
2522 let id = *validated.id();
2523 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2524
2525 assert!(pool.blob_pool.is_empty());
2527 assert_eq!(pool.pending_pool.len(), 1);
2528
2529 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2531 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2532 assert_eq!(internal_tx.subpool, SubPool::Pending);
2533
2534 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2536 pool.set_block_info(block_info);
2537
2538 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2540 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2541 assert_eq!(internal_tx.subpool, SubPool::Blob);
2542
2543 assert_eq!(pool.blob_pool.len(), 1);
2545 assert!(pool.pending_pool.is_empty());
2546 }
2547
2548 #[test]
2549 fn test_promote_valid_tx_with_decreasing_blob_fee() {
2550 let on_chain_balance = U256::MAX;
2551 let on_chain_nonce = 0;
2552 let mut f = MockTransactionFactory::default();
2553 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2554 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2555
2556 let mut block_info = pool.block_info();
2558 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2559 pool.set_block_info(block_info);
2560
2561 let validated = f.validated(tx.clone());
2562 let id = *validated.id();
2563 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2564
2565 assert!(pool.pending_pool.is_empty());
2567 assert_eq!(pool.blob_pool.len(), 1);
2568
2569 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2571 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2572 assert_eq!(internal_tx.subpool, SubPool::Blob);
2573
2574 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2576 pool.set_block_info(block_info);
2577
2578 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2580 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2581 assert_eq!(internal_tx.subpool, SubPool::Pending);
2582
2583 assert_eq!(pool.pending_pool.len(), 1);
2585 assert!(pool.blob_pool.is_empty());
2586 }
2587
2588 #[test]
2589 fn test_queued_count_includes_blob_pool() {
2590 let on_chain_balance = U256::MAX;
2591 let on_chain_nonce = 0;
2592 let mut f = MockTransactionFactory::default();
2593 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2594 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2595
2596 let mut block_info = pool.block_info();
2598 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2599 pool.set_block_info(block_info);
2600
2601 let validated = f.validated(tx);
2602 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2603
2604 assert_eq!(pool.blob_pool.len(), 1);
2605 assert!(pool.pending_pool.is_empty());
2606
2607 assert_eq!(pool.queued_transactions_count(), 1);
2609 }
2610
2611 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
2613 struct PromotionTest {
2614 basefee: u64,
2616 blobfee: u128,
2618 subpool: SubPool,
2620 basefee_update: u64,
2622 blobfee_update: u128,
2624 new_subpool: SubPool,
2626 }
2627
2628 impl PromotionTest {
2629 const fn opposite(&self) -> Self {
2631 Self {
2632 basefee: self.basefee_update,
2633 blobfee: self.blobfee_update,
2634 subpool: self.new_subpool,
2635 blobfee_update: self.blobfee,
2636 basefee_update: self.basefee,
2637 new_subpool: self.subpool,
2638 }
2639 }
2640
2641 fn assert_subpool_lengths<T: TransactionOrdering>(
2642 &self,
2643 pool: &TxPool<T>,
2644 failure_message: String,
2645 check_subpool: SubPool,
2646 ) {
2647 match check_subpool {
2648 SubPool::Blob => {
2649 assert_eq!(pool.blob_pool.len(), 1, "{failure_message}");
2650 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2651 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2652 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2653 }
2654 SubPool::Pending => {
2655 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2656 assert_eq!(pool.pending_pool.len(), 1, "{failure_message}");
2657 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2658 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2659 }
2660 SubPool::BaseFee => {
2661 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2662 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2663 assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}");
2664 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2665 }
2666 SubPool::Queued => {
2667 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2668 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2669 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2670 assert_eq!(pool.queued_pool.len(), 1, "{failure_message}");
2671 }
2672 }
2673 }
2674
2675 fn assert_single_tx_starting_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2679 self.assert_subpool_lengths(
2680 pool,
2681 format!("pool length check failed at start of test: {self:?}"),
2682 self.subpool,
2683 );
2684 }
2685
2686 fn assert_single_tx_ending_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2690 self.assert_subpool_lengths(
2691 pool,
2692 format!("pool length check failed at end of test: {self:?}"),
2693 self.new_subpool,
2694 );
2695 }
2696 }
2697
2698 #[test]
2699 fn test_promote_blob_tx_with_both_pending_fee_updates() {
2700 let on_chain_balance = U256::MAX;
2703 let on_chain_nonce = 0;
2704 let mut f = MockTransactionFactory::default();
2705 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2706
2707 let max_fee_per_blob_gas = tx.max_fee_per_blob_gas().unwrap();
2708 let max_fee_per_gas = tx.max_fee_per_gas() as u64;
2709
2710 let mut expected_promotions = vec![
2712 PromotionTest {
2713 blobfee: max_fee_per_blob_gas + 1,
2714 basefee: max_fee_per_gas + 1,
2715 subpool: SubPool::Blob,
2716 blobfee_update: max_fee_per_blob_gas + 1,
2717 basefee_update: max_fee_per_gas + 1,
2718 new_subpool: SubPool::Blob,
2719 },
2720 PromotionTest {
2721 blobfee: max_fee_per_blob_gas + 1,
2722 basefee: max_fee_per_gas + 1,
2723 subpool: SubPool::Blob,
2724 blobfee_update: max_fee_per_blob_gas,
2725 basefee_update: max_fee_per_gas + 1,
2726 new_subpool: SubPool::Blob,
2727 },
2728 PromotionTest {
2729 blobfee: max_fee_per_blob_gas + 1,
2730 basefee: max_fee_per_gas + 1,
2731 subpool: SubPool::Blob,
2732 blobfee_update: max_fee_per_blob_gas + 1,
2733 basefee_update: max_fee_per_gas,
2734 new_subpool: SubPool::Blob,
2735 },
2736 PromotionTest {
2737 blobfee: max_fee_per_blob_gas + 1,
2738 basefee: max_fee_per_gas + 1,
2739 subpool: SubPool::Blob,
2740 blobfee_update: max_fee_per_blob_gas,
2741 basefee_update: max_fee_per_gas,
2742 new_subpool: SubPool::Pending,
2743 },
2744 PromotionTest {
2745 blobfee: max_fee_per_blob_gas,
2746 basefee: max_fee_per_gas + 1,
2747 subpool: SubPool::Blob,
2748 blobfee_update: max_fee_per_blob_gas,
2749 basefee_update: max_fee_per_gas,
2750 new_subpool: SubPool::Pending,
2751 },
2752 PromotionTest {
2753 blobfee: max_fee_per_blob_gas + 1,
2754 basefee: max_fee_per_gas,
2755 subpool: SubPool::Blob,
2756 blobfee_update: max_fee_per_blob_gas,
2757 basefee_update: max_fee_per_gas,
2758 new_subpool: SubPool::Pending,
2759 },
2760 PromotionTest {
2761 blobfee: max_fee_per_blob_gas,
2762 basefee: max_fee_per_gas,
2763 subpool: SubPool::Pending,
2764 blobfee_update: max_fee_per_blob_gas,
2765 basefee_update: max_fee_per_gas,
2766 new_subpool: SubPool::Pending,
2767 },
2768 ];
2769
2770 let reversed = expected_promotions.iter().map(|test| test.opposite()).collect::<Vec<_>>();
2772 expected_promotions.extend(reversed);
2773
2774 let expected_promotions = expected_promotions.into_iter().collect::<HashSet<_>>();
2776
2777 for promotion_test in &expected_promotions {
2778 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2779
2780 let mut block_info = pool.block_info();
2782
2783 block_info.pending_blob_fee = Some(promotion_test.blobfee);
2784 block_info.pending_basefee = promotion_test.basefee;
2785 pool.set_block_info(block_info);
2786
2787 let validated = f.validated(tx.clone());
2788 let id = *validated.id();
2789 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2790
2791 promotion_test.assert_single_tx_starting_subpool(&pool);
2793
2794 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2796 assert_eq!(
2797 internal_tx.subpool, promotion_test.subpool,
2798 "Subpools do not match at start of test: {promotion_test:?}"
2799 );
2800
2801 block_info.pending_basefee = promotion_test.basefee_update;
2803 block_info.pending_blob_fee = Some(promotion_test.blobfee_update);
2804 pool.set_block_info(block_info);
2805
2806 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2808 assert_eq!(
2809 internal_tx.subpool, promotion_test.new_subpool,
2810 "Subpools do not match at end of test: {promotion_test:?}"
2811 );
2812
2813 promotion_test.assert_single_tx_ending_subpool(&pool);
2815 }
2816 }
2817
2818 #[test]
2819 fn test_insert_pending() {
2820 let on_chain_balance = U256::MAX;
2821 let on_chain_nonce = 0;
2822 let mut f = MockTransactionFactory::default();
2823 let mut pool = AllTransactions::default();
2824 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2825 let valid_tx = f.validated(tx);
2826 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2827 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2828 assert!(updates.is_empty());
2829 assert!(replaced_tx.is_none());
2830 assert!(state.contains(TxState::NO_NONCE_GAPS));
2831 assert!(state.contains(TxState::ENOUGH_BALANCE));
2832 assert_eq!(move_to, SubPool::Pending);
2833
2834 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2835 assert_eq!(inserted.subpool, SubPool::Pending);
2836 }
2837
2838 #[test]
2839 fn test_simple_insert() {
2840 let on_chain_balance = U256::ZERO;
2841 let on_chain_nonce = 0;
2842 let mut f = MockTransactionFactory::default();
2843 let mut pool = AllTransactions::default();
2844 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2845 tx.set_priority_fee(100);
2846 tx.set_max_fee(100);
2847 let valid_tx = f.validated(tx.clone());
2848 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2849 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2850 assert!(updates.is_empty());
2851 assert!(replaced_tx.is_none());
2852 assert!(state.contains(TxState::NO_NONCE_GAPS));
2853 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2854 assert_eq!(move_to, SubPool::Queued);
2855
2856 assert_eq!(pool.len(), 1);
2857 assert!(pool.contains(valid_tx.hash()));
2858 let expected_state = TxState::ENOUGH_FEE_CAP_BLOCK | TxState::NO_NONCE_GAPS;
2859 let inserted = pool.get(valid_tx.id()).unwrap();
2860 assert!(inserted.state.intersects(expected_state));
2861
2862 let res = pool.insert_tx(valid_tx, on_chain_balance, on_chain_nonce);
2864 res.unwrap_err();
2865 assert_eq!(pool.len(), 1);
2866
2867 let valid_tx = f.validated(tx.next());
2868 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2869 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2870
2871 assert!(updates.is_empty());
2872 assert!(replaced_tx.is_none());
2873 assert!(state.contains(TxState::NO_NONCE_GAPS));
2874 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2875 assert_eq!(move_to, SubPool::Queued);
2876
2877 assert!(pool.contains(valid_tx.hash()));
2878 assert_eq!(pool.len(), 2);
2879 let inserted = pool.get(valid_tx.id()).unwrap();
2880 assert!(inserted.state.intersects(expected_state));
2881 }
2882
2883 #[test]
2884 fn test_on_canonical_state_change_no_double_processing() {
2887 let mut tx_factory = MockTransactionFactory::default();
2888 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2889
2890 let tx = MockTransaction::eip1559().with_gas_price(50).with_gas_limit(30_000);
2892 let sender = tx.sender();
2893
2894 let mut block_info = pool.block_info();
2896 block_info.pending_basefee = 100;
2897 pool.set_block_info(block_info);
2898
2899 let validated = tx_factory.validated(tx);
2900 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2901
2902 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2904
2905 assert_eq!(pool.basefee_pool.len(), 1);
2906 assert_eq!(pool.pending_pool.len(), 0);
2907
2908 block_info.pending_basefee = 40;
2912
2913 let mut changed_senders = FxHashMap::default();
2914 changed_senders.insert(
2915 sender_id,
2916 SenderInfo {
2917 state_nonce: 0,
2918 balance: U256::from(20_000_000), },
2920 );
2921
2922 let outcome = pool.on_canonical_state_change(
2923 block_info,
2924 vec![], changed_senders,
2926 PoolUpdateKind::Commit,
2927 );
2928
2929 assert_eq!(pool.pending_pool.len(), 1, "Transaction should be in pending pool");
2931 assert_eq!(pool.basefee_pool.len(), 0, "Transaction should not be in basefee pool");
2932 assert_eq!(outcome.promoted.len(), 1, "Should report exactly one promotion");
2933 }
2934
2935 #[test]
2936 fn test_canonical_state_change_with_basefee_update_regression() {
2939 let mut tx_factory = MockTransactionFactory::default();
2940 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2941
2942 let sender_balance = U256::from(100_000_000);
2944
2945 let tx1 =
2947 MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000).with_nonce(0);
2948 let sender1 = tx1.sender();
2949
2950 let tx2 =
2952 MockTransaction::eip1559().with_gas_price(55).with_gas_limit(21_000).with_nonce(0);
2953 let sender2 = tx2.sender();
2954
2955 let tx3 =
2957 MockTransaction::eip1559().with_gas_price(45).with_gas_limit(21_000).with_nonce(0);
2958 let sender3 = tx3.sender();
2959
2960 let mut block_info = pool.block_info();
2962 block_info.pending_basefee = 70;
2963 pool.set_block_info(block_info);
2964
2965 let validated1 = tx_factory.validated(tx1);
2967 let validated2 = tx_factory.validated(tx2);
2968 let validated3 = tx_factory.validated(tx3);
2969
2970 pool.add_transaction(validated1, sender_balance, 0, None).unwrap();
2971 pool.add_transaction(validated2, sender_balance, 0, None).unwrap();
2972 pool.add_transaction(validated3, sender_balance, 0, None).unwrap();
2973
2974 let sender1_id = tx_factory.ids.sender_id(&sender1).unwrap();
2975 let sender2_id = tx_factory.ids.sender_id(&sender2).unwrap();
2976 let sender3_id = tx_factory.ids.sender_id(&sender3).unwrap();
2977
2978 assert_eq!(pool.basefee_pool.len(), 3, "All txs should be in basefee pool");
2980 assert_eq!(pool.pending_pool.len(), 0, "No txs should be in pending pool");
2981
2982 block_info.pending_basefee = 50;
2985
2986 let mut changed_senders = FxHashMap::default();
2988 changed_senders.insert(
2989 sender1_id,
2990 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2991 );
2992 changed_senders.insert(
2993 sender2_id,
2994 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2995 );
2996 changed_senders.insert(
2997 sender3_id,
2998 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2999 );
3000
3001 let outcome = pool.on_canonical_state_change(
3002 block_info,
3003 vec![],
3004 changed_senders,
3005 PoolUpdateKind::Commit,
3006 );
3007
3008 assert_eq!(pool.pending_pool.len(), 2, "tx1 and tx2 should be promoted");
3010 assert_eq!(pool.basefee_pool.len(), 1, "tx3 should remain in basefee");
3011
3012 assert_eq!(
3015 outcome.promoted.len(),
3016 2,
3017 "Should report exactly 2 promotions, not double-counted"
3018 );
3019
3020 let promoted_prices: Vec<u128> =
3022 outcome.promoted.iter().map(|tx| tx.max_fee_per_gas()).collect();
3023 assert!(promoted_prices.contains(&60));
3024 assert!(promoted_prices.contains(&55));
3025 }
3026
3027 #[test]
3028 fn test_basefee_decrease_with_empty_senders() {
3029 let mut tx_factory = MockTransactionFactory::default();
3032 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3033
3034 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
3036
3037 let mut block_info = pool.block_info();
3039 block_info.pending_basefee = 100;
3040 pool.set_block_info(block_info);
3041
3042 let validated = tx_factory.validated(tx);
3044 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
3045
3046 assert_eq!(pool.basefee_pool.len(), 1);
3047 assert_eq!(pool.pending_pool.len(), 0);
3048
3049 block_info.pending_basefee = 50;
3051 let outcome = pool.on_canonical_state_change(
3052 block_info,
3053 vec![],
3054 FxHashMap::default(), PoolUpdateKind::Commit,
3056 );
3057
3058 assert_eq!(pool.pending_pool.len(), 1, "Fee decrease should promote tx");
3060 assert_eq!(pool.basefee_pool.len(), 0);
3061 assert_eq!(outcome.promoted.len(), 1, "Should report promotion from fee update");
3062 }
3063
3064 #[test]
3065 fn test_basefee_decrease_account_makes_unfundable() {
3066 let mut tx_factory = MockTransactionFactory::default();
3069 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3070
3071 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
3072 let sender = tx.sender();
3073
3074 let mut block_info = pool.block_info();
3076 block_info.pending_basefee = 100;
3077 pool.set_block_info(block_info);
3078
3079 let validated = tx_factory.validated(tx);
3080 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
3081 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
3082
3083 assert_eq!(pool.basefee_pool.len(), 1);
3084
3085 block_info.pending_basefee = 50;
3087 let mut changed_senders = FxHashMap::default();
3088 changed_senders.insert(
3089 sender_id,
3090 SenderInfo {
3091 state_nonce: 0,
3092 balance: U256::from(100), },
3094 );
3095
3096 let outcome = pool.on_canonical_state_change(
3097 block_info,
3098 vec![],
3099 changed_senders,
3100 PoolUpdateKind::Commit,
3101 );
3102
3103 assert_eq!(pool.pending_pool.len(), 0, "Unfunded tx should not be in pending");
3105 assert_eq!(pool.basefee_pool.len(), 0, "Tx no longer in basefee pool");
3106 assert_eq!(pool.queued_pool.len(), 1, "Unfunded tx should be in queued pool");
3107
3108 let tx_count = pool.all_transactions.txs.len();
3110 assert_eq!(tx_count, 1, "Transaction should still be in pool (in queued)");
3111
3112 assert_eq!(outcome.promoted.len(), 0, "Should not report promotion");
3113 assert_eq!(outcome.discarded.len(), 0, "Queued tx is not reported as discarded");
3114 }
3115
3116 #[test]
3117 fn insert_already_imported() {
3118 let on_chain_balance = U256::ZERO;
3119 let on_chain_nonce = 0;
3120 let mut f = MockTransactionFactory::default();
3121 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3122 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3123 let tx = f.validated(tx);
3124 pool.add_transaction(tx.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3125 match pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap_err().kind {
3126 PoolErrorKind::AlreadyImported => {}
3127 _ => unreachable!(),
3128 }
3129 }
3130
3131 #[test]
3132 fn insert_replace() {
3133 let on_chain_balance = U256::ZERO;
3134 let on_chain_nonce = 0;
3135 let mut f = MockTransactionFactory::default();
3136 let mut pool = AllTransactions::default();
3137 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3138 let first = f.validated(tx.clone());
3139 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3140 let replacement = f.validated(tx.rng_hash().inc_price());
3141 let InsertOk { updates, replaced_tx, .. } =
3142 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
3143 assert!(updates.is_empty());
3144 let replaced = replaced_tx.unwrap();
3145 assert_eq!(replaced.0.hash(), first.hash());
3146
3147 assert!(!pool.contains(first.hash()));
3149 assert!(pool.contains(replacement.hash()));
3150 assert_eq!(pool.len(), 1);
3151 }
3152
3153 #[test]
3154 fn insert_replace_txpool() {
3155 let on_chain_balance = U256::ZERO;
3156 let on_chain_nonce = 0;
3157 let mut f = MockTransactionFactory::default();
3158 let mut pool = TxPool::mock();
3159
3160 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3161 let first = f.validated(tx.clone());
3162 let first_added =
3163 pool.add_transaction(first, on_chain_balance, on_chain_nonce, None).unwrap();
3164 let replacement = f.validated(tx.rng_hash().inc_price());
3165 let replacement_added = pool
3166 .add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce, None)
3167 .unwrap();
3168
3169 assert!(!pool.contains(first_added.hash()));
3171 assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));
3173
3174 assert!(pool.contains(replacement.hash()));
3175 let size = pool.size();
3176 assert_eq!(size.total, 1);
3177 size.assert_invariants();
3178 }
3179
3180 #[test]
3181 fn insert_replace_underpriced() {
3182 let on_chain_balance = U256::ZERO;
3183 let on_chain_nonce = 0;
3184 let mut f = MockTransactionFactory::default();
3185 let mut pool = AllTransactions::default();
3186 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3187 let first = f.validated(tx.clone());
3188 let _res = pool.insert_tx(first, on_chain_balance, on_chain_nonce);
3189 let mut replacement = f.validated(tx.rng_hash());
3190 replacement.transaction = replacement.transaction.decr_price();
3191 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3192 assert!(matches!(err, InsertErr::Underpriced { .. }));
3193 }
3194
3195 #[test]
3196 fn insert_replace_underpriced_not_enough_bump() {
3197 let on_chain_balance = U256::ZERO;
3198 let on_chain_nonce = 0;
3199 let mut f = MockTransactionFactory::default();
3200 let mut pool = AllTransactions::default();
3201 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3202 tx.set_priority_fee(100);
3203 tx.set_max_fee(100);
3204 let first = f.validated(tx.clone());
3205 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3206 let mut replacement = f.validated(tx.rng_hash().inc_price());
3207
3208 replacement.transaction.set_priority_fee(109);
3210 replacement.transaction.set_max_fee(109);
3211 let err =
3212 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3213 assert!(matches!(err, InsertErr::Underpriced { .. }));
3214 assert!(pool.contains(first.hash()));
3216 assert_eq!(pool.len(), 1);
3217
3218 replacement.transaction.set_priority_fee(110);
3220 replacement.transaction.set_max_fee(109);
3221 let err =
3222 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3223 assert!(matches!(err, InsertErr::Underpriced { .. }));
3224 assert!(pool.contains(first.hash()));
3225 assert_eq!(pool.len(), 1);
3226
3227 replacement.transaction.set_priority_fee(109);
3229 replacement.transaction.set_max_fee(110);
3230 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3231 assert!(matches!(err, InsertErr::Underpriced { .. }));
3232 assert!(pool.contains(first.hash()));
3233 assert_eq!(pool.len(), 1);
3234 }
3235
3236 #[test]
3237 fn insert_replace_underpriced_rounds_up_minimum_bump() {
3238 let on_chain_balance = U256::ZERO;
3239 let on_chain_nonce = 0;
3240 let mut f = MockTransactionFactory::default();
3241 let mut pool = AllTransactions { minimal_protocol_basefee: 0, ..Default::default() };
3242 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3243 tx.set_priority_fee(1);
3244 tx.set_max_fee(1);
3245
3246 let first = f.validated(tx.clone());
3247 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3248
3249 let mut replacement = f.validated(tx.rng_hash().inc_price());
3250 replacement.transaction.set_priority_fee(1);
3251 replacement.transaction.set_max_fee(2);
3252 let err =
3253 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3254 assert!(matches!(err, InsertErr::Underpriced { .. }));
3255 assert!(pool.contains(first.hash()));
3256 assert_eq!(pool.len(), 1);
3257
3258 replacement.transaction.set_priority_fee(2);
3259 replacement.transaction.set_max_fee(2);
3260 let replaced = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap();
3261 assert!(replaced.replaced_tx.is_some());
3262 assert_eq!(pool.len(), 1);
3263 }
3264
3265 #[test]
3266 fn insert_conflicting_type_normal_to_blob() {
3267 let on_chain_balance = U256::from(10_000);
3268 let on_chain_nonce = 0;
3269 let mut f = MockTransactionFactory::default();
3270 let mut pool = AllTransactions::default();
3271 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3272 let first = f.validated(tx.clone());
3273 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3274 let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3275 let blob = f.validated(tx);
3276 let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
3277 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3278 }
3279
3280 #[test]
3281 fn insert_conflicting_type_blob_to_normal() {
3282 let on_chain_balance = U256::from(10_000);
3283 let on_chain_nonce = 0;
3284 let mut f = MockTransactionFactory::default();
3285 let mut pool = AllTransactions::default();
3286 let tx = MockTransaction::eip4844().inc_price().inc_limit();
3287 let first = f.validated(tx.clone());
3288 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3289 let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3290 let tx = f.validated(tx);
3291 let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
3292 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3293 }
3294
3295 #[test]
3297 fn insert_previous() {
3298 let on_chain_balance = U256::ZERO;
3299 let on_chain_nonce = 0;
3300 let mut f = MockTransactionFactory::default();
3301 let mut pool = AllTransactions::default();
3302 let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
3303 let first = f.validated(tx.clone());
3304 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3305
3306 let first_in_pool = pool.get(first.id()).unwrap();
3307
3308 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3310
3311 let prev = f.validated(tx.prev());
3312 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3313 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3314
3315 assert!(updates.is_empty());
3317 assert!(replaced_tx.is_none());
3318 assert!(state.contains(TxState::NO_NONCE_GAPS));
3319 assert_eq!(move_to, SubPool::Queued);
3320
3321 let first_in_pool = pool.get(first.id()).unwrap();
3322 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3324 }
3325
3326 #[test]
3328 fn insert_with_updates() {
3329 let on_chain_balance = U256::from(10_000);
3330 let on_chain_nonce = 0;
3331 let mut f = MockTransactionFactory::default();
3332 let mut pool = AllTransactions::default();
3333 let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
3334 let first = f.validated(tx.clone());
3335 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3336
3337 let first_in_pool = pool.get(first.id()).unwrap();
3338 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3340 assert_eq!(SubPool::Queued, first_in_pool.subpool);
3341
3342 let prev = f.validated(tx.prev());
3343 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3344 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3345
3346 assert_eq!(updates.len(), 1);
3348 assert!(replaced_tx.is_none());
3349 assert!(state.contains(TxState::NO_NONCE_GAPS));
3350 assert_eq!(move_to, SubPool::Pending);
3351
3352 let first_in_pool = pool.get(first.id()).unwrap();
3353 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3355 assert_eq!(SubPool::Pending, first_in_pool.subpool);
3356 }
3357
3358 #[test]
3359 fn insert_previous_blocking() {
3360 let on_chain_balance = U256::from(1_000);
3361 let on_chain_nonce = 0;
3362 let mut f = MockTransactionFactory::default();
3363 let mut pool = AllTransactions::default();
3364 pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
3365 let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
3366 let first = f.validated(tx.clone());
3367
3368 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3369
3370 let first_in_pool = pool.get(first.id()).unwrap();
3371
3372 assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
3373 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3375
3376 let prev = f.validated(tx.prev());
3377 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3378 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3379
3380 assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3381 assert!(updates.is_empty());
3383 assert!(replaced_tx.is_none());
3384 assert!(state.contains(TxState::NO_NONCE_GAPS));
3385 assert_eq!(move_to, SubPool::BaseFee);
3386
3387 let first_in_pool = pool.get(first.id()).unwrap();
3388 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3390 }
3391
3392 #[test]
3393 fn rejects_spammer() {
3394 let on_chain_balance = U256::from(1_000);
3395 let on_chain_nonce = 0;
3396 let mut f = MockTransactionFactory::default();
3397 let mut pool = AllTransactions::default();
3398
3399 let mut tx = MockTransaction::eip1559();
3400 let unblocked_tx = tx.clone();
3401 for _ in 0..pool.max_account_slots {
3402 tx = tx.next();
3403 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
3404 }
3405
3406 assert_eq!(
3407 pool.max_account_slots,
3408 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3409 );
3410
3411 let err =
3412 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
3413 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
3414
3415 assert!(pool
3416 .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
3417 .is_ok());
3418 }
3419
3420 #[test]
3421 fn allow_local_spamming() {
3422 let on_chain_balance = U256::from(1_000);
3423 let on_chain_nonce = 0;
3424 let mut f = MockTransactionFactory::default();
3425 let mut pool = AllTransactions::default();
3426
3427 let mut tx = MockTransaction::eip1559();
3428 for _ in 0..pool.max_account_slots {
3429 tx = tx.next();
3430 pool.insert_tx(
3431 f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
3432 on_chain_balance,
3433 on_chain_nonce,
3434 )
3435 .unwrap();
3436 }
3437
3438 assert_eq!(
3439 pool.max_account_slots,
3440 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3441 );
3442
3443 pool.insert_tx(
3444 f.validated_with_origin(TransactionOrigin::Local, tx.next()),
3445 on_chain_balance,
3446 on_chain_nonce,
3447 )
3448 .unwrap();
3449 }
3450
3451 #[test]
3452 fn reject_tx_over_gas_limit() {
3453 let on_chain_balance = U256::from(1_000);
3454 let on_chain_nonce = 0;
3455 let mut f = MockTransactionFactory::default();
3456 let mut pool = AllTransactions::default();
3457
3458 let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
3459
3460 assert!(matches!(
3461 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
3462 Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
3463 ));
3464 }
3465
3466 #[test]
3467 fn test_tx_equal_gas_limit() {
3468 let on_chain_balance = U256::from(1_000);
3469 let on_chain_nonce = 0;
3470 let mut f = MockTransactionFactory::default();
3471 let mut pool = AllTransactions::default();
3472
3473 let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
3474
3475 let InsertOk { state, .. } =
3476 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
3477 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
3478 }
3479
3480 #[test]
3481 fn full_update_sender_heuristic() {
3482 let mut pool = AllTransactions::<MockTransaction>::default();
3483 for sender in 0..(FULL_UPDATE_MIN_SENDERS - 1) as u64 {
3484 pool.tx_counter.insert(sender.into(), 1);
3485 }
3486
3487 assert!(!pool.should_update_all_senders(250));
3489 assert!(pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS - 1));
3490
3491 pool.tx_counter.insert((FULL_UPDATE_MIN_SENDERS as u64 - 1).into(), 1);
3492
3493 assert!(
3495 !pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS / FULL_UPDATE_SENDER_RATIO - 1)
3496 );
3497 assert!(pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS / FULL_UPDATE_SENDER_RATIO));
3498 assert!(pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS + 1));
3499 }
3500
3501 #[test]
3502 fn update_only_visits_changed_senders_when_fees_are_unchanged() {
3503 let mut f = MockTransactionFactory::default();
3504 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3505
3506 let a = f.validated(MockTransaction::eip1559().inc_price_by(10));
3508 let b = f.validated(MockTransaction::eip1559().inc_price_by(10));
3509 let (a_id, b_id) = (*a.id(), *b.id());
3510 assert_ne!(a_id.sender, b_id.sender);
3511 pool.add_transaction(a, U256::from(1_000_000), 0, None).unwrap();
3512 pool.add_transaction(b, U256::from(1_000_000), 0, None).unwrap();
3513
3514 pool.all_transactions.update(&Default::default());
3516 assert_eq!(pool.all_transactions.last_full_update_fees, pool.all_transactions.pending_fees);
3517
3518 let b_state_before = pool.all_transactions.txs.get(&b_id).unwrap().state;
3521 let mut changed = FxHashMap::default();
3522 changed.insert(a_id.sender, SenderInfo { state_nonce: 1, balance: U256::from(1_000_000) });
3523
3524 let produced = pool.all_transactions.update(&changed);
3525
3526 assert_eq!(produced.len(), 1, "expected exactly the changed sender's update");
3527 assert_eq!(produced[0].id, a_id);
3528 assert!(matches!(produced[0].destination, Destination::Discard));
3529 assert_eq!(
3530 pool.all_transactions.txs.get(&b_id).unwrap().state,
3531 b_state_before,
3532 "unchanged sender was modified"
3533 );
3534 }
3535
3536 #[test]
3537 fn changed_sender_update_matches_full_update() {
3538 let senders = [
3539 address!("0x000000000000000000000000000000000000000a"),
3540 address!("0x000000000000000000000000000000000000000b"),
3541 address!("0x000000000000000000000000000000000000000c"),
3542 ];
3543 let starting_nonces = [5, 11, 17];
3544
3545 let build_pool = || {
3546 let mut f = MockTransactionFactory::default();
3547 let mut pool = AllTransactions::default();
3548 pool.pending_fees.base_fee = 1;
3549
3550 for (sender, starting_nonce) in senders.into_iter().zip(starting_nonces) {
3551 for nonce in starting_nonce..starting_nonce + 3 {
3552 let tx = MockTransaction::eip1559()
3553 .with_sender(sender)
3554 .with_nonce(nonce)
3555 .inc_price_by(10)
3556 .rng_hash();
3557 pool.insert_tx(f.validated(tx), U256::from(1_000_000), starting_nonce).unwrap();
3558 }
3559 }
3560
3561 pool.update(&Default::default());
3563 let sender_ids = senders.map(|sender| f.ids.sender_id(&sender).unwrap());
3564 (pool, sender_ids)
3565 };
3566
3567 let (mut changed_senders_only, sender_ids) = build_pool();
3568 let (mut full_update, full_update_sender_ids) = build_pool();
3569 assert_eq!(sender_ids, full_update_sender_ids);
3570
3571 let mut changed = FxHashMap::default();
3572 changed.insert(
3573 sender_ids[0],
3574 SenderInfo { state_nonce: starting_nonces[0] + 1, balance: U256::from(1_000_000) },
3575 );
3576 changed.insert(
3577 sender_ids[1],
3578 SenderInfo { state_nonce: starting_nonces[1], balance: U256::ZERO },
3579 );
3580
3581 let mut changed_sender_updates = changed_senders_only.update(&changed);
3582 full_update.last_full_update_fees.base_fee =
3584 full_update.last_full_update_fees.base_fee.saturating_add(1);
3585 let mut full_updates = full_update.update(&changed);
3586
3587 let update_key = |update: &PoolUpdate| {
3588 let destination = match &update.destination {
3589 Destination::Discard => None,
3590 Destination::Pool(pool) => Some(*pool),
3591 };
3592 (update.id, update.current, destination)
3593 };
3594 changed_sender_updates.sort_unstable_by_key(|update| update.id);
3595 full_updates.sort_unstable_by_key(|update| update.id);
3596 assert_eq!(
3597 changed_sender_updates.iter().map(update_key).collect::<Vec<_>>(),
3598 full_updates.iter().map(update_key).collect::<Vec<_>>()
3599 );
3600
3601 let metadata = |pool: &AllTransactions<MockTransaction>| {
3602 pool.txs
3603 .iter()
3604 .map(|(id, tx)| (*id, tx.state, tx.subpool, tx.cumulative_cost))
3605 .collect::<Vec<_>>()
3606 };
3607 assert_eq!(metadata(&changed_senders_only), metadata(&full_update));
3608 }
3609
3610 #[test]
3611 fn update_visits_every_sender_when_the_base_fee_moved() {
3612 let mut f = MockTransactionFactory::default();
3613 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3614
3615 let tx = MockTransaction::eip1559().inc_price_by(10);
3616 let validated = f.validated(tx.clone());
3617 let id = *validated.id();
3618 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3619
3620 pool.all_transactions.update(&Default::default());
3621 assert!(pool
3622 .all_transactions
3623 .txs
3624 .get(&id)
3625 .unwrap()
3626 .state
3627 .contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3628
3629 pool.all_transactions.pending_fees.base_fee = (tx.max_fee_per_gas() + 1) as u64;
3632 pool.all_transactions.update(&Default::default());
3633
3634 assert!(
3635 !pool
3636 .all_transactions
3637 .txs
3638 .get(&id)
3639 .unwrap()
3640 .state
3641 .contains(TxState::ENOUGH_FEE_CAP_BLOCK),
3642 "fee change was not applied to an unchanged sender"
3643 );
3644 }
3645
3646 #[test]
3647 fn blob_fee_change_records_full_update() {
3648 let mut pool = AllTransactions::<MockTransaction>::default();
3649 pool.pending_fees.blob_fee += 1;
3650
3651 pool.update(&Default::default());
3652
3653 assert_eq!(pool.last_full_update_fees, pool.pending_fees);
3654 }
3655
3656 #[test]
3657 fn gap_fill_rechecks_descendant_fee_eligibility() {
3658 let mut f = MockTransactionFactory::default();
3659 let mut pool = AllTransactions::default();
3660 let sender = address!("0x000000000000000000000000000000000000000d");
3661 let balance = U256::MAX;
3662
3663 pool.pending_fees.base_fee = 100;
3664 let descendant = MockTransaction::eip1559()
3665 .with_sender(sender)
3666 .with_nonce(1)
3667 .with_gas_limit(21_000)
3668 .with_max_fee(150)
3669 .with_priority_fee(1)
3670 .rng_hash();
3671 let descendant = f.validated(descendant);
3672 let descendant_id = *descendant.id();
3673 pool.insert_tx(descendant, balance, 0).unwrap();
3674 pool.update(&Default::default());
3675
3676 pool.pending_fees.base_fee = 200;
3679 pool.update(&Default::default());
3680 assert!(pool.get(&descendant_id).unwrap().state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3681
3682 let predecessor = MockTransaction::eip1559()
3683 .with_sender(sender)
3684 .with_nonce(0)
3685 .with_gas_limit(21_000)
3686 .with_max_fee(250)
3687 .with_priority_fee(1)
3688 .rng_hash();
3689 let InsertOk { move_to, .. } =
3690 pool.insert_tx(f.validated(predecessor), balance, 0).unwrap();
3691
3692 assert_eq!(move_to, SubPool::Pending);
3693 let descendant = pool.get(&descendant_id).unwrap();
3694 assert_eq!(descendant.subpool, SubPool::BaseFee);
3695 assert!(!descendant.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3696 assert!(descendant.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3697 }
3698
3699 #[test]
3700 fn base_fee_update_unparks_all_descendants() {
3701 let mut f = MockTransactionFactory::default();
3702 let mut pool = AllTransactions::default();
3703 let sender = address!("0x000000000000000000000000000000000000000e");
3704 let mut ids = Vec::new();
3705
3706 pool.pending_fees.base_fee = 200;
3707 for nonce in 0..3 {
3708 let tx = MockTransaction::eip1559()
3709 .with_sender(sender)
3710 .with_nonce(nonce)
3711 .with_gas_limit(21_000)
3712 .with_max_fee(150)
3713 .with_priority_fee(1)
3714 .rng_hash();
3715 let tx = f.validated(tx);
3716 ids.push(*tx.id());
3717 pool.insert_tx(tx, U256::MAX, 0).unwrap();
3718 }
3719
3720 pool.pending_fees.base_fee = 100;
3721 pool.update(&Default::default());
3722
3723 for id in ids {
3724 assert_eq!(pool.get(&id).unwrap().subpool, SubPool::Pending);
3725 }
3726 }
3727
3728 #[test]
3729 fn fee_update_keeps_descendants_of_underpriced_transaction_parked() {
3730 let mut f = MockTransactionFactory::default();
3731 let mut pool = AllTransactions::default();
3732 let sender = address!("0x0000000000000000000000000000000000000010");
3733 let fee_caps = [150, 50, 150];
3734 let mut ids = Vec::new();
3735
3736 pool.pending_fees.base_fee = 200;
3737 for (nonce, fee_cap) in fee_caps.into_iter().enumerate() {
3738 let tx = MockTransaction::eip1559()
3739 .with_sender(sender)
3740 .with_nonce(nonce as u64)
3741 .with_gas_limit(21_000)
3742 .with_max_fee(fee_cap)
3743 .with_priority_fee(1)
3744 .rng_hash();
3745 let tx = f.validated(tx);
3746 ids.push(*tx.id());
3747 pool.insert_tx(tx, U256::MAX, 0).unwrap();
3748 }
3749
3750 pool.pending_fees.base_fee = 100;
3751 pool.update(&Default::default());
3752
3753 assert_eq!(pool.get(&ids[0]).unwrap().subpool, SubPool::Pending);
3754 assert_eq!(pool.get(&ids[1]).unwrap().subpool, SubPool::BaseFee);
3755 assert_eq!(pool.get(&ids[2]).unwrap().subpool, SubPool::Queued);
3756 }
3757
3758 #[test]
3759 fn blob_fee_update_unparks_all_descendants() {
3760 let mut f = MockTransactionFactory::default();
3761 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3762 let sender = address!("0x000000000000000000000000000000000000000f");
3763 let mut block_info = pool.block_info();
3764 block_info.pending_blob_fee = Some(200);
3765 pool.set_block_info(block_info);
3766
3767 for nonce in 0..3 {
3768 let tx = MockTransaction::eip4844()
3769 .with_sender(sender)
3770 .with_nonce(nonce)
3771 .with_gas_limit(21_000)
3772 .with_max_fee(1_000)
3773 .with_priority_fee(1)
3774 .with_blob_fee(150)
3775 .rng_hash();
3776 pool.add_transaction(f.validated(tx), U256::MAX, 0, None).unwrap();
3777 }
3778 assert_eq!(pool.blob_pool.len(), 3);
3779
3780 block_info.pending_blob_fee = Some(100);
3781 pool.on_canonical_state_change(
3782 block_info,
3783 Vec::new(),
3784 FxHashMap::default(),
3785 PoolUpdateKind::Commit,
3786 );
3787
3788 let nonces = pool.best_transactions().map(|tx| tx.nonce()).collect::<Vec<_>>();
3789 assert_eq!(nonces, vec![0, 1, 2]);
3790 }
3791
3792 #[test]
3793 fn update_basefee_subpools() {
3794 let mut f = MockTransactionFactory::default();
3795 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3796
3797 let tx = MockTransaction::eip1559().inc_price_by(10);
3798 let validated = f.validated(tx.clone());
3799 let id = *validated.id();
3800 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3801
3802 assert_eq!(pool.pending_pool.len(), 1);
3803
3804 pool.update_basefee((tx.max_fee_per_gas() + 1) as u64, |_| {});
3805
3806 assert!(pool.pending_pool.is_empty());
3807 assert_eq!(pool.basefee_pool.len(), 1);
3808
3809 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3810 }
3811
3812 #[test]
3813 fn update_basefee_subpools_setting_block_info() {
3814 let mut f = MockTransactionFactory::default();
3815 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3816
3817 let tx = MockTransaction::eip1559().inc_price_by(10);
3818 let validated = f.validated(tx.clone());
3819 let id = *validated.id();
3820 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3821
3822 assert_eq!(pool.pending_pool.len(), 1);
3823
3824 let mut block_info = pool.block_info();
3826 block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
3827 pool.set_block_info(block_info);
3828
3829 assert!(pool.pending_pool.is_empty());
3830 assert_eq!(pool.basefee_pool.len(), 1);
3831
3832 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3833 }
3834
3835 #[test]
3836 fn basefee_decrease_promotes_affordable_and_keeps_unaffordable() {
3837 use alloy_primitives::address;
3838 let mut f = MockTransactionFactory::default();
3839 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3840
3841 let sender_a = address!("0x000000000000000000000000000000000000000a");
3844 let sender_b = address!("0x000000000000000000000000000000000000000b");
3845 let sender_c = address!("0x000000000000000000000000000000000000000c");
3846
3847 let tx1 = MockTransaction::eip1559()
3848 .set_sender(sender_a)
3849 .set_nonce(0)
3850 .set_max_fee(500)
3851 .inc_limit();
3852 let tx2 = MockTransaction::eip1559()
3853 .set_sender(sender_b)
3854 .set_nonce(0)
3855 .set_max_fee(600)
3856 .inc_limit();
3857 let tx3 = MockTransaction::eip1559()
3858 .set_sender(sender_c)
3859 .set_nonce(0)
3860 .set_max_fee(400)
3861 .inc_limit();
3862
3863 let mut block_info = pool.block_info();
3865 block_info.pending_basefee = 700;
3866 pool.set_block_info(block_info);
3867
3868 let validated1 = f.validated(tx1);
3869 let validated2 = f.validated(tx2);
3870 let validated3 = f.validated(tx3);
3871 let id1 = *validated1.id();
3872 let id2 = *validated2.id();
3873 let id3 = *validated3.id();
3874
3875 pool.add_transaction(validated1, U256::from(10_000), 0, None).unwrap();
3879 pool.add_transaction(validated2, U256::from(10_000), 0, None).unwrap();
3880 pool.add_transaction(validated3, U256::from(10_000), 0, None).unwrap();
3881
3882 println!("Basefee pool len: {}", pool.basefee_pool.len());
3884 println!("Pending pool len: {}", pool.pending_pool.len());
3885 println!("tx1 subpool: {:?}", pool.all_transactions.txs.get(&id1).unwrap().subpool);
3886 println!("tx2 subpool: {:?}", pool.all_transactions.txs.get(&id2).unwrap().subpool);
3887 println!("tx3 subpool: {:?}", pool.all_transactions.txs.get(&id3).unwrap().subpool);
3888
3889 assert_eq!(pool.basefee_pool.len(), 3);
3891 assert_eq!(pool.pending_pool.len(), 0);
3892 assert_eq!(pool.all_transactions.txs.get(&id1).unwrap().subpool, SubPool::BaseFee);
3893 assert_eq!(pool.all_transactions.txs.get(&id2).unwrap().subpool, SubPool::BaseFee);
3894 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3895
3896 let mut block_info = pool.block_info();
3898 block_info.pending_basefee = 450; pool.set_block_info(block_info);
3901
3902 assert_eq!(pool.basefee_pool.len(), 1);
3907 assert_eq!(pool.pending_pool.len(), 2);
3908
3909 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3911
3912 let tx1_meta = pool.all_transactions.txs.get(&id1).unwrap();
3914 let tx2_meta = pool.all_transactions.txs.get(&id2).unwrap();
3915 assert_eq!(tx1_meta.subpool, SubPool::Pending);
3916 assert_eq!(tx2_meta.subpool, SubPool::Pending);
3917 assert!(tx1_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3918 assert!(tx2_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3919
3920 let best: Vec<_> = pool.best_transactions().take(3).collect();
3922 assert_eq!(best.len(), 2); assert!(best.iter().any(|tx| tx.id() == &id1));
3924 assert!(best.iter().any(|tx| tx.id() == &id2));
3925 }
3926
3927 #[test]
3928 fn apply_fee_updates_records_promotions_after_basefee_drop() {
3929 let mut f = MockTransactionFactory::default();
3930 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3931
3932 let tx = MockTransaction::eip1559()
3933 .with_gas_limit(21_000)
3934 .with_max_fee(500)
3935 .with_priority_fee(1);
3936 let validated = f.validated(tx);
3937 let id = *validated.id();
3938 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3939
3940 assert_eq!(pool.pending_pool.len(), 1);
3941
3942 pool.update_basefee(600, |_| {});
3944 assert!(pool.pending_pool.is_empty());
3945 assert_eq!(pool.basefee_pool.len(), 1);
3946
3947 let prev_base_fee = 600;
3948 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3949
3950 pool.all_transactions.pending_fees.base_fee = 400;
3952
3953 let mut outcome = UpdateOutcome::default();
3954 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3955
3956 assert_eq!(pool.pending_pool.len(), 1);
3957 assert!(pool.basefee_pool.is_empty());
3958 assert_eq!(outcome.promoted.len(), 1);
3959 assert_eq!(outcome.promoted[0].id(), &id);
3960 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3961 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3962
3963 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3964 assert_eq!(tx_meta.subpool, SubPool::Pending);
3965 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3966 }
3967
3968 #[test]
3969 fn apply_fee_updates_records_promotions_after_blob_fee_drop() {
3970 let mut f = MockTransactionFactory::default();
3971 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3972
3973 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3974
3975 let tx = MockTransaction::eip4844().with_blob_fee(initial_blob_fee + 100);
3976 let validated = f.validated(tx.clone());
3977 let id = *validated.id();
3978 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3979
3980 assert_eq!(pool.pending_pool.len(), 1);
3981
3982 let increased_blob_fee = tx.max_fee_per_blob_gas().unwrap() + 200;
3984 pool.update_blob_fee(increased_blob_fee, Ordering::Equal, |_| {});
3985 assert!(pool.pending_pool.is_empty());
3986 assert_eq!(pool.blob_pool.len(), 1);
3987
3988 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3989 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3990
3991 pool.all_transactions.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
3993
3994 let mut outcome = UpdateOutcome::default();
3995 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3996
3997 assert_eq!(pool.pending_pool.len(), 1);
3998 assert!(pool.blob_pool.is_empty());
3999 assert_eq!(outcome.promoted.len(), 1);
4000 assert_eq!(outcome.promoted[0].id(), &id);
4001 assert_eq!(pool.all_transactions.pending_fees.base_fee, prev_base_fee);
4002 assert_eq!(pool.all_transactions.pending_fees.blob_fee, tx.max_fee_per_blob_gas().unwrap());
4003
4004 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
4005 assert_eq!(tx_meta.subpool, SubPool::Pending);
4006 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4007 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4008 }
4009
4010 #[test]
4011 fn apply_fee_updates_promotes_blob_after_basefee_drop() {
4012 let mut f = MockTransactionFactory::default();
4013 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4014
4015 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4016
4017 let tx = MockTransaction::eip4844()
4018 .with_max_fee(500)
4019 .with_priority_fee(1)
4020 .with_blob_fee(initial_blob_fee + 100);
4021 let validated = f.validated(tx);
4022 let id = *validated.id();
4023 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4024
4025 assert_eq!(pool.pending_pool.len(), 1);
4026
4027 let high_base_fee = 600;
4029 pool.update_basefee(high_base_fee, |_| {});
4030 assert!(pool.pending_pool.is_empty());
4031 assert_eq!(pool.blob_pool.len(), 1);
4032
4033 let prev_base_fee = high_base_fee;
4034 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4035
4036 pool.all_transactions.pending_fees.base_fee = 400;
4038
4039 let mut outcome = UpdateOutcome::default();
4040 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
4041
4042 assert_eq!(pool.pending_pool.len(), 1);
4043 assert!(pool.blob_pool.is_empty());
4044 assert_eq!(outcome.promoted.len(), 1);
4045 assert_eq!(outcome.promoted[0].id(), &id);
4046 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
4047 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
4048
4049 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
4050 assert_eq!(tx_meta.subpool, SubPool::Pending);
4051 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4052 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4053 }
4054
4055 #[test]
4056 fn queued_transactions_include_blob_pool() {
4057 let mut f = MockTransactionFactory::default();
4058 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4059
4060 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4061 let tx = MockTransaction::eip4844()
4062 .with_max_fee(500)
4063 .with_priority_fee(1)
4064 .with_blob_fee(initial_blob_fee + 100);
4065 let validated = f.validated(tx);
4066 let id = *validated.id();
4067 let sender = validated.sender_id();
4068 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4069
4070 pool.update_basefee(600, |_| {});
4072 assert_eq!(pool.blob_pool.len(), 1);
4073
4074 let queued = pool.queued_transactions();
4075 assert_eq!(queued.len(), 1);
4076 assert_eq!(queued[0].id(), &id);
4077
4078 let by_sender = pool.queued_txs_by_sender(sender);
4079 assert_eq!(by_sender.len(), 1);
4080 assert_eq!(by_sender[0].id(), &id);
4081 }
4082
4083 #[test]
4084 fn apply_fee_updates_demotes_after_basefee_rise() {
4085 let mut f = MockTransactionFactory::default();
4086 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4087
4088 let tx = MockTransaction::eip1559()
4089 .with_gas_limit(21_000)
4090 .with_max_fee(400)
4091 .with_priority_fee(1);
4092 let validated = f.validated(tx);
4093 let id = *validated.id();
4094 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4095
4096 assert_eq!(pool.pending_pool.len(), 1);
4097
4098 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
4099 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4100
4101 let new_base_fee = prev_base_fee + 1_000;
4103 pool.all_transactions.pending_fees.base_fee = new_base_fee;
4104
4105 let mut outcome = UpdateOutcome::default();
4106 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
4107
4108 assert!(pool.pending_pool.is_empty());
4109 assert_eq!(pool.basefee_pool.len(), 1);
4110 assert!(outcome.promoted.is_empty());
4111 assert_eq!(pool.all_transactions.pending_fees.base_fee, new_base_fee);
4112 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
4113
4114 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
4115 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
4116 assert!(!tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4117 }
4118
4119 #[test]
4120 fn get_highest_transaction_by_sender_and_nonce() {
4121 let mut f = MockTransactionFactory::default();
4123 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4124
4125 let tx = MockTransaction::eip1559();
4127 pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0, None).unwrap();
4128
4129 let tx1 = tx.inc_price().next();
4131
4132 let tx1_validated = f.validated(tx1.clone());
4134 pool.add_transaction(tx1_validated, U256::from(1_000), 0, None).unwrap();
4135
4136 assert_eq!(
4138 pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
4139 Some(1)
4140 );
4141
4142 let highest_tx = pool
4144 .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
4145 .expect("Failed to retrieve highest transaction");
4146
4147 assert_eq!(highest_tx.as_ref().transaction, tx1);
4149 }
4150
4151 #[test]
4152 fn get_highest_consecutive_transaction_by_sender() {
4153 let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
4155 let mut f = MockTransactionFactory::default();
4156
4157 let sender = Address::random();
4159 let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
4160 for nonce in txs {
4161 let mut mock_tx = MockTransaction::eip1559();
4162 mock_tx.set_sender(sender);
4163 mock_tx.set_nonce(nonce);
4164
4165 let validated_tx = f.validated(mock_tx);
4166 pool.add_transaction(validated_tx, U256::from(1000), 0, None).unwrap();
4167 }
4168
4169 let sender_id = f.ids.sender_id(&sender).unwrap();
4171 let next_tx =
4172 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
4173 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
4174
4175 let next_tx =
4176 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
4177 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
4178
4179 let next_tx =
4180 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
4181 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
4182
4183 let mut info = SenderInfo::default();
4185 info.update(8, U256::ZERO);
4186 pool.all_transactions.sender_info.insert(sender_id, info);
4187 let next_tx =
4188 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
4189 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
4190 }
4191
4192 #[test]
4193 fn discard_nonce_too_low() {
4194 let mut f = MockTransactionFactory::default();
4195 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4196
4197 let tx = MockTransaction::eip1559().inc_price_by(10);
4198 let validated = f.validated(tx.clone());
4199 let id = *validated.id();
4200 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4201
4202 let next = tx.next();
4203 let validated = f.validated(next.clone());
4204 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4205
4206 assert_eq!(pool.pending_pool.len(), 2);
4207
4208 let mut changed_senders = HashMap::default();
4209 changed_senders.insert(
4210 id.sender,
4211 SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
4212 );
4213 let outcome = pool.update_accounts(changed_senders);
4214 assert_eq!(outcome.discarded.len(), 1);
4215 assert_eq!(pool.pending_pool.len(), 1);
4216 }
4217
4218 #[test]
4219 fn discard_with_large_blob_txs() {
4220 reth_tracing::init_test_tracing();
4222
4223 let mut f = MockTransactionFactory::default();
4225 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4226 let default_limits = pool.config.blob_limit;
4227
4228 let a_sender = address!("0x000000000000000000000000000000000000000a");
4231
4232 let mut block_info = pool.block_info();
4234 block_info.pending_blob_fee = Some(100);
4235 block_info.pending_basefee = 100;
4236
4237 pool.set_block_info(block_info);
4239
4240 let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
4242 .into_iter()
4243 .map(|mut tx| {
4244 tx.set_size(default_limits.max_size / 2 + 1);
4245 tx.set_max_fee((block_info.pending_basefee - 1).into());
4246 tx
4247 })
4248 .collect::<Vec<_>>();
4249
4250 for tx in a_txs {
4252 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
4253 }
4254
4255 let removed = pool.discard_worst();
4257 assert_eq!(removed.len(), 1);
4258 }
4259
4260 #[test]
4261 fn discard_with_parked_large_txs() {
4262 reth_tracing::init_test_tracing();
4264
4265 let mut f = MockTransactionFactory::default();
4267 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4268 let default_limits = pool.config.queued_limit;
4269
4270 let a_sender = address!("0x000000000000000000000000000000000000000a");
4273
4274 let pool_base_fee = 100;
4276 pool.update_basefee(pool_base_fee, |_| {});
4277
4278 let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
4280 .into_iter()
4281 .map(|mut tx| {
4282 tx.set_size(default_limits.max_size / 2 + 1);
4283 tx.set_max_fee((pool_base_fee - 1).into());
4284 tx
4285 })
4286 .collect::<Vec<_>>();
4287
4288 for tx in a_txs {
4290 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
4291 }
4292
4293 let removed = pool.discard_worst();
4295 assert_eq!(removed.len(), 1);
4296 }
4297
4298 #[test]
4299 fn discard_at_capacity() {
4300 let mut f = MockTransactionFactory::default();
4301 let queued_limit = SubPoolLimit::new(1000, usize::MAX);
4302 let mut pool =
4303 TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
4304
4305 for _ in 0..queued_limit.max_txs {
4307 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
4308 let validated = f.validated(tx);
4309 let _id = *validated.id();
4310 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4311 }
4312
4313 let size = pool.size();
4314 assert_eq!(size.queued, queued_limit.max_txs);
4315
4316 for _ in 0..queued_limit.max_txs {
4317 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
4318 let validated = f.validated(tx);
4319 let _id = *validated.id();
4320 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4321
4322 pool.discard_worst();
4323 pool.assert_invariants();
4324 assert!(pool.size().queued <= queued_limit.max_txs);
4325 }
4326 }
4327
4328 #[test]
4329 fn discard_blobs_at_capacity() {
4330 let mut f = MockTransactionFactory::default();
4331 let blob_limit = SubPoolLimit::new(1000, usize::MAX);
4332 let mut pool =
4333 TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
4334 pool.all_transactions.pending_fees.blob_fee = 10000;
4335 for _ in 0..blob_limit.max_txs {
4337 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
4338 let validated = f.validated(tx);
4339 let _id = *validated.id();
4340 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4341 }
4342
4343 let size = pool.size();
4344 assert_eq!(size.blob, blob_limit.max_txs);
4345
4346 for _ in 0..blob_limit.max_txs {
4347 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
4348 let validated = f.validated(tx);
4349 let _id = *validated.id();
4350 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4351
4352 pool.discard_worst();
4353 pool.assert_invariants();
4354 assert!(pool.size().blob <= blob_limit.max_txs);
4355 }
4356 }
4357
4358 #[test]
4359 fn reuse_update_buffer_across_pool_operations() {
4360 let mut f = MockTransactionFactory::default();
4361 let mut pool = TxPool::new(
4362 MockOrdering::default(),
4363 PoolConfig { max_account_slots: 128, ..Default::default() },
4364 );
4365 let mut tx = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4366 let first = f.validated(tx.clone());
4367 for _ in 0..80 {
4368 pool.add_transaction(f.validated(tx.clone()), U256::MAX, 0, None).unwrap();
4369 tx = tx.next();
4370 }
4371 assert_eq!(pool.all_transactions.update_buffer.capacity(), 0);
4372
4373 let changed = |balance, state_nonce| {
4374 FxHashMap::from_iter([(first.sender_id(), SenderInfo { balance, state_nonce })])
4375 };
4376 let outcome = pool.update_accounts(changed(U256::ZERO, 0));
4377 assert!(outcome.promoted.is_empty());
4378 assert!(outcome.discarded.is_empty());
4379 assert_eq!(pool.queued_pool.len(), 80);
4380 let capacity = pool.all_transactions.update_buffer.capacity();
4381 assert!(capacity >= 80);
4382 let buffer = pool.all_transactions.update_buffer.as_ptr();
4383
4384 for _ in 0..2 {
4385 let outcome = pool.update_accounts(changed(U256::MAX, 0));
4386 assert_eq!(outcome.promoted.len(), 80);
4387 assert!(outcome.discarded.is_empty());
4388 assert_eq!(pool.pending_pool.len(), 80);
4389
4390 let outcome = pool.update_accounts(FxHashMap::default());
4391 assert!(outcome.promoted.is_empty());
4392 assert!(outcome.discarded.is_empty());
4393
4394 pool.remove_transaction_by_hash(first.hash()).unwrap();
4395 assert_eq!(pool.queued_pool.len(), 79);
4396
4397 let replacement = f.validated(first.transaction.next());
4399 assert!(pool.add_transaction(replacement, U256::MAX, 0, None).is_err());
4400 assert_eq!(pool.all_transactions.update_buffer.as_ptr(), buffer);
4401
4402 let added = pool.add_transaction(first.clone(), U256::MAX, 0, None).unwrap();
4403 let AddedTransaction::Pending(added) = added else { panic!("expected pending") };
4404 assert_eq!(added.promoted.len(), 79);
4405 assert!(added.discarded.is_empty());
4406 assert_eq!(pool.pending_pool.len(), 80);
4407
4408 pool.update_accounts(changed(U256::ZERO, 0));
4409 assert_eq!(pool.queued_pool.len(), 80);
4410 assert!(pool.all_transactions.update_buffer.is_empty());
4411 assert_eq!(pool.all_transactions.update_buffer.capacity(), capacity);
4412 assert_eq!(pool.all_transactions.update_buffer.as_ptr(), buffer);
4413 pool.assert_invariants();
4414 }
4415
4416 let outcome = pool.update_accounts(changed(U256::MAX, 80));
4417 assert_eq!(outcome.discarded.len(), 80);
4418 assert!(outcome.promoted.is_empty());
4419 assert!(pool.is_empty());
4420 assert!(pool.all_transactions.update_buffer.is_empty());
4421 assert_eq!(pool.all_transactions.update_buffer.as_ptr(), buffer);
4422 }
4423
4424 #[test]
4425 fn account_updates_sender_balance() {
4426 let mut on_chain_balance = U256::from(100);
4427 let on_chain_nonce = 0;
4428 let mut f = MockTransactionFactory::default();
4429 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4430
4431 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4432 let tx_1 = tx_0.next();
4433 let tx_2 = tx_1.next();
4434
4435 let v0 = f.validated(tx_0);
4437 let v1 = f.validated(tx_1);
4438 let v2 = f.validated(tx_2);
4439
4440 let _res =
4441 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4442 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4443 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4444
4445 assert_eq!(1, pool.pending_transactions().len());
4447 assert_eq!(2, pool.queued_transactions().len());
4448
4449 let mut updated_accounts = HashMap::default();
4451 on_chain_balance = U256::from(300);
4452 updated_accounts.insert(
4453 v0.sender_id(),
4454 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4455 );
4456 pool.update_accounts(updated_accounts.clone());
4457
4458 assert_eq!(3, pool.pending_transactions().len());
4459 assert!(pool.queued_transactions().is_empty());
4460
4461 updated_accounts.entry(v0.sender_id()).and_modify(|v| v.balance = U256::from(1));
4463 pool.update_accounts(updated_accounts);
4464
4465 assert!(pool.pending_transactions().is_empty());
4466 assert_eq!(3, pool.queued_transactions().len());
4467 }
4468
4469 #[test]
4470 fn account_updates_nonce_gap() {
4471 let on_chain_balance = U256::from(10_000);
4472 let mut on_chain_nonce = 0;
4473 let mut f = MockTransactionFactory::default();
4474 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4475
4476 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4477 let tx_1 = tx_0.next();
4478 let tx_2 = tx_1.next();
4479
4480 let v0 = f.validated(tx_0);
4482 let v1 = f.validated(tx_1);
4483 let v2 = f.validated(tx_2);
4484
4485 let _res =
4487 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4488 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4489
4490 assert!(pool.queued_transactions().is_empty());
4491 assert_eq!(2, pool.pending_transactions().len());
4492
4493 pool.remove_transaction_by_hash(v0.hash());
4495
4496 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4498
4499 assert_eq!(2, pool.queued_transactions().len());
4501 assert!(pool.pending_transactions().is_empty());
4502
4503 let mut updated_accounts = HashMap::default();
4505 on_chain_nonce += 1;
4506 updated_accounts.insert(
4507 v0.sender_id(),
4508 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4509 );
4510 pool.update_accounts(updated_accounts);
4511
4512 assert!(pool.queued_transactions().is_empty());
4514 assert_eq!(2, pool.pending_transactions().len());
4515 }
4516 #[test]
4517 fn test_transaction_removal() {
4518 let on_chain_balance = U256::from(10_000);
4519 let on_chain_nonce = 0;
4520 let mut f = MockTransactionFactory::default();
4521 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4522
4523 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4524 let tx_1 = tx_0.next();
4525
4526 let v0 = f.validated(tx_0);
4528 let v1 = f.validated(tx_1);
4529
4530 let _res =
4532 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4533 let _res =
4534 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4535
4536 assert_eq!(0, pool.queued_transactions().len());
4537 assert_eq!(2, pool.pending_transactions().len());
4538
4539 pool.remove_transaction(v0.id());
4541 let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
4543 assert_eq!(vec![v1.nonce()], pool_txs);
4544 }
4545 #[test]
4546 fn test_remove_transactions() {
4547 let on_chain_balance = U256::from(10_000);
4548 let on_chain_nonce = 0;
4549 let mut f = MockTransactionFactory::default();
4550 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4551
4552 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4553 let tx_1 = tx_0.next();
4554 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4555 let tx_3 = tx_2.next();
4556
4557 let v0 = f.validated(tx_0);
4559 let v1 = f.validated(tx_1);
4560 let v2 = f.validated(tx_2);
4561 let v3 = f.validated(tx_3);
4562
4563 let _res =
4565 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4566 let _res =
4567 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4568 let _res =
4569 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4570 let _res =
4571 pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4572
4573 assert_eq!(0, pool.queued_transactions().len());
4574 assert_eq!(4, pool.pending_transactions().len());
4575
4576 pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
4577
4578 assert_eq!(2, pool.queued_transactions().len());
4579 assert!(pool.pending_transactions().is_empty());
4580 assert!(pool.contains(v1.hash()));
4581 assert!(pool.contains(v3.hash()));
4582 }
4583
4584 #[test]
4585 fn test_remove_transactions_middle_pending_hash() {
4586 let on_chain_balance = U256::from(10_000);
4587 let on_chain_nonce = 0;
4588 let mut f = MockTransactionFactory::default();
4589 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4590
4591 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4592 let tx_1 = tx_0.next();
4593 let tx_2 = tx_1.next();
4594 let tx_3 = tx_2.next();
4595
4596 let v0 = f.validated(tx_0);
4598 let v1 = f.validated(tx_1);
4599 let v2 = f.validated(tx_2);
4600 let v3 = f.validated(tx_3);
4601
4602 let _res = pool.add_transaction(v0, on_chain_balance, on_chain_nonce, None).unwrap();
4604 let _res =
4605 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4606 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4607 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4608
4609 assert_eq!(0, pool.queued_transactions().len());
4610 assert_eq!(4, pool.pending_transactions().len());
4611
4612 let mut removed_txs = pool.remove_transactions(vec![*v1.hash()]);
4613 assert_eq!(1, removed_txs.len());
4614
4615 assert_eq!(2, pool.queued_transactions().len());
4616 assert_eq!(1, pool.pending_transactions().len());
4617
4618 let removed_tx = removed_txs.pop().unwrap();
4620 let v1 = f.validated(removed_tx.transaction.clone());
4621 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4622 assert_eq!(0, pool.queued_transactions().len());
4623 assert_eq!(4, pool.pending_transactions().len());
4624 }
4625
4626 #[test]
4627 fn test_remove_transactions_and_descendants() {
4628 let on_chain_balance = U256::from(10_000);
4629 let on_chain_nonce = 0;
4630 let mut f = MockTransactionFactory::default();
4631 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4632
4633 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4634 let tx_1 = tx_0.next();
4635 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4636 let tx_3 = tx_2.next();
4637 let tx_4 = tx_3.next();
4638
4639 let v0 = f.validated(tx_0);
4641 let v1 = f.validated(tx_1);
4642 let v2 = f.validated(tx_2);
4643 let v3 = f.validated(tx_3);
4644 let v4 = f.validated(tx_4);
4645
4646 let _res =
4648 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4649 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4650 let _res =
4651 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4652 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4653 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4654
4655 assert_eq!(0, pool.queued_transactions().len());
4656 assert_eq!(5, pool.pending_transactions().len());
4657
4658 pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
4659
4660 assert_eq!(0, pool.queued_transactions().len());
4661 assert_eq!(0, pool.pending_transactions().len());
4662 }
4663 #[test]
4664 fn test_remove_descendants() {
4665 let on_chain_balance = U256::from(10_000);
4666 let on_chain_nonce = 0;
4667 let mut f = MockTransactionFactory::default();
4668 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4669
4670 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4671 let tx_1 = tx_0.next();
4672 let tx_2 = tx_1.next();
4673 let tx_3 = tx_2.next();
4674
4675 let v0 = f.validated(tx_0);
4677 let v1 = f.validated(tx_1);
4678 let v2 = f.validated(tx_2);
4679 let v3 = f.validated(tx_3);
4680
4681 let _res =
4683 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4684 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4685 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4686 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4687
4688 assert_eq!(0, pool.queued_transactions().len());
4689 assert_eq!(4, pool.pending_transactions().len());
4690
4691 let mut removed = Vec::new();
4692 pool.remove_transaction(v0.id());
4693 pool.remove_descendants(v0.id(), &mut removed);
4694
4695 assert_eq!(0, pool.queued_transactions().len());
4696 assert_eq!(0, pool.pending_transactions().len());
4697 assert_eq!(3, removed.len());
4698 }
4699 #[test]
4700 fn test_remove_transactions_by_sender() {
4701 let on_chain_balance = U256::from(10_000);
4702 let on_chain_nonce = 0;
4703 let mut f = MockTransactionFactory::default();
4704 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4705
4706 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4707 let tx_1 = tx_0.next();
4708 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4709 let tx_3 = tx_2.next();
4710 let tx_4 = tx_3.next();
4711
4712 let v0 = f.validated(tx_0);
4714 let v1 = f.validated(tx_1);
4715 let v2 = f.validated(tx_2);
4716 let v3 = f.validated(tx_3);
4717 let v4 = f.validated(tx_4);
4718
4719 let _res =
4721 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4722 let _res =
4723 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4724 let _res =
4725 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4726 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4727 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4728
4729 assert_eq!(0, pool.queued_transactions().len());
4730 assert_eq!(5, pool.pending_transactions().len());
4731
4732 pool.remove_transactions_by_sender(v2.sender_id());
4733
4734 assert_eq!(0, pool.queued_transactions().len());
4735 assert_eq!(2, pool.pending_transactions().len());
4736 assert!(pool.contains(v0.hash()));
4737 assert!(pool.contains(v1.hash()));
4738 }
4739 #[test]
4740 fn wrong_best_order_of_transactions() {
4741 let on_chain_balance = U256::from(10_000);
4742 let mut on_chain_nonce = 0;
4743 let mut f = MockTransactionFactory::default();
4744 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4745
4746 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4747 let tx_1 = tx_0.next();
4748 let tx_2 = tx_1.next();
4749 let tx_3 = tx_2.next();
4750
4751 let v0 = f.validated(tx_0);
4753 let v1 = f.validated(tx_1);
4754 let v2 = f.validated(tx_2);
4755 let v3 = f.validated(tx_3);
4756
4757 let _res =
4759 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4760 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4761
4762 assert_eq!(0, pool.queued_transactions().len());
4763 assert_eq!(2, pool.pending_transactions().len());
4764
4765 pool.remove_transaction(v0.id());
4767
4768 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4770
4771 assert_eq!(1, pool.queued_transactions().len());
4773 assert_eq!(1, pool.pending_transactions().len());
4774
4775 let mut updated_accounts = HashMap::default();
4777 on_chain_nonce += 1;
4778 updated_accounts.insert(
4779 v0.sender_id(),
4780 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4781 );
4782 pool.update_accounts(updated_accounts);
4783
4784 assert_eq!(0, pool.queued_transactions().len());
4787 assert_eq!(2, pool.pending_transactions().len());
4788
4789 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4791 assert_eq!(0, pool.queued_transactions().len());
4792 assert_eq!(3, pool.pending_transactions().len());
4793
4794 assert_eq!(
4797 pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
4798 vec![1, 2, 3]
4799 );
4800 }
4801
4802 #[test]
4803 fn test_best_with_attributes() {
4804 let on_chain_balance = U256::MAX;
4805 let on_chain_nonce = 0;
4806 let mut f = MockTransactionFactory::default();
4807 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4808
4809 let base_fee: u128 = 100;
4810 let blob_fee: u128 = 100;
4811
4812 let mut block_info = pool.block_info();
4814 block_info.pending_basefee = base_fee as u64;
4815 block_info.pending_blob_fee = Some(blob_fee);
4816 pool.set_block_info(block_info);
4817
4818 let tx1 = MockTransaction::eip4844()
4820 .with_sender(Address::with_last_byte(1))
4821 .with_max_fee(base_fee + 10)
4822 .with_blob_fee(blob_fee + 10);
4823 let tx2 = MockTransaction::eip4844()
4824 .with_sender(Address::with_last_byte(2))
4825 .with_max_fee(base_fee + 10)
4826 .with_blob_fee(blob_fee);
4827 let tx3 = MockTransaction::eip4844()
4828 .with_sender(Address::with_last_byte(3))
4829 .with_max_fee(base_fee)
4830 .with_blob_fee(blob_fee + 10);
4831 let tx4 = MockTransaction::eip4844()
4832 .with_sender(Address::with_last_byte(4))
4833 .with_max_fee(base_fee)
4834 .with_blob_fee(blob_fee);
4835 let tx5 = MockTransaction::eip4844()
4836 .with_sender(Address::with_last_byte(5))
4837 .with_max_fee(base_fee)
4838 .with_blob_fee(blob_fee - 10);
4839 let tx6 = MockTransaction::eip4844()
4840 .with_sender(Address::with_last_byte(6))
4841 .with_max_fee(base_fee - 10)
4842 .with_blob_fee(blob_fee);
4843 let tx7 = MockTransaction::eip4844()
4844 .with_sender(Address::with_last_byte(7))
4845 .with_max_fee(base_fee - 10)
4846 .with_blob_fee(blob_fee - 10);
4847
4848 for tx in vec![
4849 tx1.clone(),
4850 tx2.clone(),
4851 tx3.clone(),
4852 tx4.clone(),
4853 tx5.clone(),
4854 tx6.clone(),
4855 tx7.clone(),
4856 ] {
4857 pool.add_transaction(f.validated(tx), on_chain_balance, on_chain_nonce, None).unwrap();
4858 }
4859
4860 let base_fee = base_fee as u64;
4861 let blob_fee = blob_fee as u64;
4862
4863 let cases = vec![
4864 (BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee + 5)), vec![tx1.clone()]),
4866 (
4868 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee)),
4869 vec![tx1.clone(), tx2.clone()],
4870 ),
4871 (
4873 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee - 5)),
4874 vec![tx1.clone(), tx2.clone()],
4875 ),
4876 (
4878 BestTransactionsAttributes::new(base_fee, Some(blob_fee + 5)),
4879 vec![tx1.clone(), tx3.clone()],
4880 ),
4881 (
4883 BestTransactionsAttributes::new(base_fee, Some(blob_fee)),
4884 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
4885 ),
4886 (
4888 BestTransactionsAttributes::new(base_fee, Some(blob_fee - 10)),
4889 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx5.clone()],
4890 ),
4891 (
4893 BestTransactionsAttributes::new(base_fee - 5, Some(blob_fee + 5)),
4894 vec![tx1.clone(), tx3.clone()],
4895 ),
4896 (
4898 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee)),
4899 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx6.clone()],
4900 ),
4901 (
4903 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee - 10)),
4904 vec![tx1, tx2, tx5, tx3, tx4, tx6, tx7],
4905 ),
4906 ];
4907
4908 for (idx, (attribute, expected)) in cases.into_iter().enumerate() {
4909 let mut best = pool.best_transactions_with_attributes(attribute);
4910
4911 for (tx_idx, expected_tx) in expected.into_iter().enumerate() {
4912 let tx = best.next().expect("Transaction should be returned");
4913 assert_eq!(
4914 tx.transaction,
4915 expected_tx,
4916 "Failed tx {} in case {}",
4917 tx_idx + 1,
4918 idx + 1
4919 );
4920 }
4921
4922 assert!(best.next().is_none());
4924 }
4925 }
4926
4927 #[test]
4928 fn test_pending_ordering() {
4929 let mut f = MockTransactionFactory::default();
4930 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4931
4932 let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
4933 let tx_1 = tx_0.next();
4934
4935 let v0 = f.validated(tx_0);
4936 let v1 = f.validated(tx_1);
4937
4938 pool.add_transaction(v0.clone(), U256::MAX, 0, None).unwrap();
4940 assert_eq!(1, pool.queued_transactions().len());
4941
4942 pool.add_transaction(v1, U256::MAX, 1, None).unwrap();
4944
4945 assert_eq!(2, pool.pending_transactions().len());
4946 assert_eq!(0, pool.queued_transactions().len());
4947
4948 assert_eq!(
4949 pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
4950 v0.nonce()
4951 );
4952 }
4953
4954 #[test]
4956 fn one_sender_one_independent_transaction() {
4957 let mut on_chain_balance = U256::from(4_999); let mut on_chain_nonce = 40;
4959 let mut f = MockTransactionFactory::default();
4960 let mut pool = TxPool::mock();
4961 let mut submitted_txs = Vec::new();
4962
4963 let template =
4965 MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
4966
4967 for tx_nonce in 40..48 {
4970 let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
4971 submitted_txs.push(*tx.id());
4972 pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap();
4973 }
4974
4975 on_chain_balance = U256::from(999_999);
4978 on_chain_nonce = 42;
4979 pool.remove_transaction(&submitted_txs[0]);
4980 pool.remove_transaction(&submitted_txs[1]);
4981
4982 for tx_nonce in 48..52 {
4984 pool.add_transaction(
4985 f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
4986 on_chain_balance,
4987 on_chain_nonce,
4988 None,
4989 )
4990 .unwrap();
4991 }
4992
4993 let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
4994 assert_eq!(best_txs.len(), 10); assert_eq!(pool.pending_pool.independent().len(), 1);
4997 }
4998
4999 #[test]
5000 fn test_insertion_disorder() {
5001 let mut f = MockTransactionFactory::default();
5002 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5003
5004 let sender = address!("0x1234567890123456789012345678901234567890");
5005 let tx0 = f.validated_arc(
5006 MockTransaction::legacy().with_sender(sender).with_nonce(0).with_gas_price(10),
5007 );
5008 let tx1 = f.validated_arc(
5009 MockTransaction::eip1559()
5010 .with_sender(sender)
5011 .with_nonce(1)
5012 .with_gas_limit(1000)
5013 .with_gas_price(10),
5014 );
5015 let tx2 = f.validated_arc(
5016 MockTransaction::legacy().with_sender(sender).with_nonce(2).with_gas_price(10),
5017 );
5018 let tx3 = f.validated_arc(
5019 MockTransaction::legacy().with_sender(sender).with_nonce(3).with_gas_price(10),
5020 );
5021
5022 pool.add_transaction((*tx0).clone(), U256::from(1000), 0, None).unwrap();
5024 let mut best = pool.best_transactions();
5025 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5026 assert_eq!(t0.id(), tx0.id());
5027 pool.add_transaction((*tx1).clone(), U256::from(1000), 0, None).unwrap();
5029 let mut best = pool.best_transactions();
5030 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5031 assert_eq!(t0.id(), tx0.id());
5032 assert!(best.next().is_none());
5033
5034 pool.add_transaction((*tx2).clone(), U256::MAX, 0, None).unwrap();
5036
5037 let mut best = pool.best_transactions();
5038
5039 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5040 let t1 = best.next().expect("tx1 should be put in the pending subpool");
5041 let t2 = best.next().expect("tx2 should be put in the pending subpool");
5042 assert_eq!(t0.id(), tx0.id());
5043 assert_eq!(t1.id(), tx1.id());
5044 assert_eq!(t2.id(), tx2.id());
5045
5046 pool.add_transaction((*tx3).clone(), U256::MAX, 0, None).unwrap();
5048 let mut best = pool.best_transactions();
5049 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5050 let t1 = best.next().expect("tx1 should be put in the pending subpool");
5051 let t2 = best.next().expect("tx2 should be put in the pending subpool");
5052 let t3 = best.next().expect("tx3 should be put in the pending subpool");
5053 assert_eq!(t0.id(), tx0.id());
5054 assert_eq!(t1.id(), tx1.id());
5055 assert_eq!(t2.id(), tx2.id());
5056 assert_eq!(t3.id(), tx3.id());
5057 }
5058
5059 #[test]
5060 fn test_non_4844_blob_fee_bit_invariant() {
5061 let mut f = MockTransactionFactory::default();
5062 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5063
5064 let non_4844_tx = MockTransaction::eip1559().set_max_fee(200).inc_limit();
5065 let validated = f.validated(non_4844_tx.clone());
5066
5067 assert!(!non_4844_tx.is_eip4844());
5068 pool.add_transaction(validated.clone(), U256::from(10_000), 0, None).unwrap();
5069
5070 let tx_meta = pool.all_transactions.txs.get(validated.id()).unwrap();
5072 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
5073 assert_eq!(tx_meta.subpool, SubPool::Pending);
5074 }
5075
5076 #[test]
5077 fn test_blob_fee_enforcement_only_applies_to_eip4844() {
5078 let mut f = MockTransactionFactory::default();
5079 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5080
5081 let mut block_info = pool.block_info();
5083 block_info.pending_blob_fee = Some(160);
5084 block_info.pending_basefee = 100;
5085 pool.set_block_info(block_info);
5086
5087 let eip4844_tx = MockTransaction::eip4844()
5088 .with_sender(address!("0x000000000000000000000000000000000000000a"))
5089 .with_max_fee(200)
5090 .with_blob_fee(150) .inc_limit();
5092
5093 let non_4844_tx = MockTransaction::eip1559()
5094 .with_sender(address!("0x000000000000000000000000000000000000000b"))
5095 .set_max_fee(200)
5096 .inc_limit();
5097
5098 let validated_4844 = f.validated(eip4844_tx);
5099 let validated_non_4844 = f.validated(non_4844_tx);
5100
5101 pool.add_transaction(validated_4844.clone(), U256::from(10_000), 0, None).unwrap();
5102 pool.add_transaction(validated_non_4844.clone(), U256::from(10_000), 0, None).unwrap();
5103
5104 let tx_4844_meta = pool.all_transactions.txs.get(validated_4844.id()).unwrap();
5105 let tx_non_4844_meta = pool.all_transactions.txs.get(validated_non_4844.id()).unwrap();
5106
5107 assert!(!tx_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
5109 assert_eq!(tx_4844_meta.subpool, SubPool::Blob);
5110
5111 assert!(tx_non_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
5113 assert_eq!(tx_non_4844_meta.subpool, SubPool::Pending);
5114 }
5115
5116 #[test]
5117 fn test_basefee_decrease_preserves_non_4844_blob_fee_bit() {
5118 let mut f = MockTransactionFactory::default();
5119 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5120
5121 let non_4844_tx = MockTransaction::eip1559()
5123 .with_sender(address!("0x000000000000000000000000000000000000000a"))
5124 .set_max_fee(500) .inc_limit();
5126
5127 pool.update_basefee(600, |_| {});
5129
5130 let validated = f.validated(non_4844_tx);
5131 let tx_id = *validated.id();
5132 pool.add_transaction(validated, U256::from(10_000), 0, None).unwrap();
5133
5134 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
5136 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
5137 assert!(
5138 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
5139 "Non-4844 tx in BaseFee pool must retain ENOUGH_BLOB_FEE_CAP_BLOCK bit"
5140 );
5141
5142 pool.update_basefee(400, |_| {});
5145
5146 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
5148 assert_eq!(
5149 tx_meta.subpool,
5150 SubPool::Pending,
5151 "Non-4844 tx should be promoted from BaseFee to Pending after basefee decrease"
5152 );
5153 assert!(
5154 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
5155 "Non-4844 tx must NEVER lose ENOUGH_BLOB_FEE_CAP_BLOCK bit during basefee promotion"
5156 );
5157 assert!(
5158 tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK),
5159 "Non-4844 tx should gain ENOUGH_FEE_CAP_BLOCK bit after basefee decrease"
5160 );
5161 }
5162
5163 #[test]
5169 fn best_transactions_nonce_order_on_balance_unlock() {
5170 let mut f = MockTransactionFactory::default();
5171 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5172
5173 let sender = Address::random();
5174 let on_chain_balance = U256::from(10_000);
5175
5176 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
5178 let tx1 = tx0.next().inc_limit().with_value(U256::from(on_chain_balance));
5180 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
5182
5183 let v0 = f.validated(tx0);
5184 let v1 = f.validated(tx1);
5185 let v2 = f.validated(tx2);
5186
5187 pool.add_transaction(v0, on_chain_balance, 0, None).unwrap();
5189
5190 let mut best = pool.best_transactions();
5192
5193 let first = best.next().expect("should yield tx0");
5195 assert_eq!(first.id().nonce, 0);
5196
5197 pool.add_transaction(v1, on_chain_balance, 0, None).unwrap();
5200
5201 assert!(best.next().is_none(), "tx1 should be queued, not pending");
5203
5204 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
5208
5209 let t1 = best.next().expect("should yield a transaction");
5210 let t2 = best.next().expect("should yield a transaction");
5211
5212 assert_eq!(
5214 t1.id().nonce,
5215 1,
5216 "first yielded tx should be nonce 1, got nonce {}",
5217 t1.id().nonce
5218 );
5219 assert_eq!(
5220 t2.id().nonce,
5221 2,
5222 "second yielded tx should be nonce 2, got nonce {}",
5223 t2.id().nonce
5224 );
5225 }
5226
5227 #[test]
5231 fn best_transactions_nonce_order_on_gap_fill() {
5232 let mut f = MockTransactionFactory::default();
5233 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5234
5235 let sender = Address::random();
5236 let balance = U256::MAX;
5237
5238 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
5240 let tx1 = tx0.next().inc_limit();
5242
5243 let v0 = f.validated(tx0);
5244 let v1 = f.validated(tx1);
5245
5246 pool.add_transaction(v1, balance, 0, None).unwrap();
5248
5249 let mut best = pool.best_transactions();
5251 assert!(best.next().is_none(), "pool should have no pending txs yet");
5252
5253 pool.add_transaction(v0, balance, 0, None).unwrap();
5255
5256 let t0 = best.next().expect("should yield a transaction");
5257 let t1 = best.next().expect("should yield a transaction");
5258
5259 assert_eq!(t0.id().nonce, 0, "first yielded tx should be nonce 0, got {}", t0.id().nonce);
5260 assert_eq!(t1.id().nonce, 1, "second yielded tx should be nonce 1, got {}", t1.id().nonce);
5261 }
5262
5263 #[test]
5267 fn best_transactions_nonce_order_mixed_promotions() {
5268 let mut f = MockTransactionFactory::default();
5269 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5270
5271 let sender = Address::random();
5272 let low_balance = U256::from(10_000);
5273
5274 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
5276 let tx1 = tx0.next().inc_limit().with_value(U256::from(low_balance));
5278 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
5280 let tx3 = tx2.next().inc_limit().with_value(U256::ZERO);
5282
5283 let v0 = f.validated(tx0);
5284 let v1 = f.validated(tx1);
5285 let v2 = f.validated(tx2);
5286 let v3 = f.validated(tx3);
5287
5288 pool.add_transaction(v0, low_balance, 0, None).unwrap();
5290
5291 pool.add_transaction(v1, low_balance, 0, None).unwrap();
5293
5294 pool.add_transaction(v3, low_balance, 0, None).unwrap();
5296
5297 let mut best = pool.best_transactions();
5298
5299 let first = best.next().expect("should yield tx0");
5301 assert_eq!(first.id().nonce, 0);
5302 assert!(best.next().is_none(), "only tx0 should be pending");
5303
5304 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
5309
5310 let t1 = best.next().expect("should yield nonce 1");
5311 let t2 = best.next().expect("should yield nonce 2");
5312 let t3 = best.next().expect("should yield nonce 3");
5313
5314 assert_eq!(t1.id().nonce, 1, "expected nonce 1, got {}", t1.id().nonce);
5315 assert_eq!(t2.id().nonce, 2, "expected nonce 2, got {}", t2.id().nonce);
5316 assert_eq!(t3.id().nonce, 3, "expected nonce 3, got {}", t3.id().nonce);
5317 }
5318}