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 reth_primitives_traits::transaction::error::InvalidTransactionError;
39use rustc_hash::FxHashMap;
40use smallvec::SmallVec;
41#[cfg(test)]
42use std::collections::{HashMap, HashSet};
43use std::{
44 cmp::Ordering,
45 collections::{btree_map::Entry, hash_map, BTreeMap},
46 fmt,
47 ops::Bound::{Excluded, Unbounded},
48 sync::Arc,
49};
50use tracing::{trace, warn};
51
52#[cfg_attr(doc, aquamarine::aquamarine)]
53pub struct TxPool<T: TransactionOrdering> {
95 pending_pool: PendingPool<T>,
99 config: PoolConfig,
101 queued_pool: ParkedPool<QueuedOrd<T::Transaction>>,
108 basefee_pool: ParkedPool<BasefeeOrd<T::Transaction>>,
113 blob_pool: BlobTransactions<T::Transaction>,
120 all_transactions: AllTransactions<T::Transaction>,
122 metrics: TxPoolMetrics,
124}
125
126impl<T: TransactionOrdering> TxPool<T> {
129 pub fn new(ordering: T, config: PoolConfig) -> Self {
131 Self {
132 pending_pool: PendingPool::with_buffer(
133 ordering,
134 config.max_new_pending_txs_notifications,
135 ),
136 queued_pool: Default::default(),
137 basefee_pool: Default::default(),
138 blob_pool: Default::default(),
139 all_transactions: AllTransactions::new(&config),
140 config,
141 metrics: Default::default(),
142 }
143 }
144
145 pub fn get_highest_nonce_by_sender(&self, sender: SenderId) -> Option<u64> {
147 self.all().txs_iter(sender).last().map(|(_, tx)| tx.transaction.nonce())
148 }
149
150 pub fn get_highest_transaction_by_sender(
153 &self,
154 sender: SenderId,
155 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
156 self.all().txs_iter(sender).last().map(|(_, tx)| Arc::clone(&tx.transaction))
157 }
158
159 pub(crate) fn get_highest_consecutive_transaction_by_sender(
166 &self,
167 mut on_chain: TransactionId,
168 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
169 let mut last_consecutive_tx = None;
170
171 if let Some(current) = self.all_transactions.sender_info.get(&on_chain.sender) {
173 on_chain.nonce = on_chain.nonce.max(current.state_nonce);
174 }
175
176 let mut next_expected_nonce = on_chain.nonce;
177 for (id, tx) in self.all().descendant_txs_inclusive(&on_chain) {
178 if next_expected_nonce != id.nonce {
179 break
180 }
181 next_expected_nonce = id.next_nonce();
182 last_consecutive_tx = Some(tx);
183 }
184
185 last_consecutive_tx.map(|tx| Arc::clone(&tx.transaction))
186 }
187
188 pub(crate) const fn all(&self) -> &AllTransactions<T::Transaction> {
190 &self.all_transactions
191 }
192
193 pub(crate) fn unique_senders(&self) -> AddressSet {
195 self.all_transactions.txs.values().map(|tx| tx.transaction.sender()).collect()
196 }
197
198 pub fn size(&self) -> PoolSize {
200 PoolSize {
201 pending: self.pending_pool.len(),
202 pending_size: self.pending_pool.size(),
203 basefee: self.basefee_pool.len(),
204 basefee_size: self.basefee_pool.size(),
205 queued: self.queued_pool.len(),
206 queued_size: self.queued_pool.size(),
207 blob: self.blob_pool.len(),
208 blob_size: self.blob_pool.size(),
209 total: self.all_transactions.len(),
210 }
211 }
212
213 pub const fn block_info(&self) -> BlockInfo {
215 BlockInfo {
216 block_gas_limit: self.all_transactions.block_gas_limit,
217 last_seen_block_hash: self.all_transactions.last_seen_block_hash,
218 last_seen_block_number: self.all_transactions.last_seen_block_number,
219 pending_basefee: self.all_transactions.pending_fees.base_fee,
220 pending_blob_fee: Some(self.all_transactions.pending_fees.blob_fee),
221 }
222 }
223
224 fn update_blob_fee<F>(
226 &mut self,
227 mut pending_blob_fee: u128,
228 base_fee_update: Ordering,
229 mut on_promoted: F,
230 ) where
231 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
232 {
233 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut pending_blob_fee);
234 match (self.all_transactions.pending_fees.blob_fee.cmp(&pending_blob_fee), base_fee_update)
235 {
236 (Ordering::Equal, Ordering::Equal | Ordering::Greater) => {
237 }
239 (Ordering::Greater, Ordering::Equal | Ordering::Greater) => {
240 let removed =
242 self.pending_pool.update_blob_fee(self.all_transactions.pending_fees.blob_fee);
243 for tx in removed {
244 let to = {
245 let tx =
246 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
247
248 tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
250 tx.subpool = tx.state.into();
251 tx.subpool
252 };
253 self.add_transaction_to_subpool(to, tx);
254 }
255 }
256 (Ordering::Greater, Ordering::Less) => {
257 let removed =
262 self.pending_pool.update_blob_fee(self.all_transactions.pending_fees.blob_fee);
263 for tx in removed {
264 let to = {
265 let tx =
266 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
267
268 tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
269 tx.subpool = tx.state.into();
270 tx.subpool
271 };
272 self.add_transaction_to_subpool(to, tx);
273 }
274
275 let removed =
276 self.blob_pool.enforce_pending_fees(&self.all_transactions.pending_fees);
277 for tx in removed {
278 let subpool = {
279 let tx_meta =
280 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
281 tx_meta.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
282 tx_meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
283 tx_meta.subpool = tx_meta.state.into();
284 tx_meta.subpool
285 };
286
287 if subpool.is_pending() {
288 on_promoted(&tx);
289 }
290
291 self.add_transaction_to_subpool(subpool, tx);
292 }
293 }
294 (Ordering::Less, _) | (Ordering::Equal, Ordering::Less) => {
295 let removed =
297 self.blob_pool.enforce_pending_fees(&self.all_transactions.pending_fees);
298 for tx in removed {
299 let subpool = {
300 let tx_meta =
301 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
302 tx_meta.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
303 tx_meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
304 tx_meta.subpool = tx_meta.state.into();
305 tx_meta.subpool
306 };
307
308 if subpool.is_pending() {
309 on_promoted(&tx);
310 }
311
312 self.add_transaction_to_subpool(subpool, tx);
313 }
314 }
315 }
316 }
317
318 fn update_basefee<F>(&mut self, mut pending_basefee: u64, mut on_promoted: F) -> Ordering
323 where
324 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
325 {
326 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut pending_basefee);
327 match self.all_transactions.pending_fees.base_fee.cmp(&pending_basefee) {
328 Ordering::Equal => {
329 Ordering::Equal
331 }
332 Ordering::Greater => {
333 let removed =
335 self.pending_pool.update_base_fee(self.all_transactions.pending_fees.base_fee);
336 for tx in removed {
337 let to = {
338 let tx =
339 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
340 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
341 tx.subpool = tx.state.into();
342 tx.subpool
343 };
344 self.add_transaction_to_subpool(to, tx);
345 }
346
347 Ordering::Greater
348 }
349 Ordering::Less => {
350 let current_base_fee = self.all_transactions.pending_fees.base_fee;
359 self.basefee_pool.enforce_basefee_with(current_base_fee, |tx| {
360 let subpool = {
362 let meta =
363 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
364 meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
365 meta.subpool = meta.state.into();
366 meta.subpool
367 };
368
369 if subpool.is_pending() {
370 on_promoted(&tx);
371 }
372
373 trace!(target: "txpool", hash=%tx.transaction.hash(), pool=?subpool, "Adding transaction to a subpool");
374 match subpool {
375 SubPool::Queued => self.queued_pool.add_transaction(tx),
376 SubPool::Pending => {
377 self.pending_pool.add_transaction(tx, current_base_fee);
378 }
379 SubPool::Blob => {
380 self.blob_pool.add_transaction(tx);
381 }
382 SubPool::BaseFee => {
383 warn!(target: "txpool", "BaseFee transactions should become Pending after basefee decrease");
386 }
387 }
388 });
389
390 Ordering::Less
391 }
392 }
393 }
394
395 pub fn set_block_info(&mut self, info: BlockInfo) -> UpdateOutcome<T::Transaction> {
401 let mut outcome = UpdateOutcome::default();
402
403 let basefee_ordering = self.update_basefee(info.pending_basefee, |tx| {
405 outcome.promoted.push(tx.clone());
406 });
407 if let Some(blob_fee) = info.pending_blob_fee {
408 self.update_blob_fee(blob_fee, basefee_ordering, |tx| {
409 outcome.promoted.push(tx.clone());
410 })
411 }
412 self.all_transactions.set_block_info(info);
414
415 outcome
416 }
417
418 pub(crate) fn best_transactions(&self) -> BestTransactions<T> {
421 self.pending_pool.best()
422 }
423
424 pub(crate) fn best_transactions_with_attributes(
431 &self,
432 best_transactions_attributes: BestTransactionsAttributes,
433 ) -> Box<dyn crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T::Transaction>>>>
434 {
435 match best_transactions_attributes.basefee.cmp(&self.all_transactions.pending_fees.base_fee)
438 {
439 Ordering::Equal => {
440 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
444 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
445 Ordering::Less => {
446 let unlocked =
448 self.blob_pool.satisfy_attributes(best_transactions_attributes);
449 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
450 unlocked,
451 best_transactions_attributes.basefee,
452 new_blob_fee,
453 ))
454 }
455 Ordering::Equal => Box::new(self.pending_pool.best()),
456 Ordering::Greater => {
457 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
459 best_transactions_attributes.basefee,
460 best_transactions_attributes.blob_fee.unwrap_or_default(),
461 ))
462 }
463 }
464 }
465 Ordering::Greater => {
466 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
468 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
469 Ordering::Less => {
470 let unlocked =
472 self.blob_pool.satisfy_attributes(best_transactions_attributes);
473 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
474 unlocked,
475 best_transactions_attributes.basefee,
476 new_blob_fee,
477 ))
478 }
479 Ordering::Equal | Ordering::Greater => {
480 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
482 best_transactions_attributes.basefee,
483 new_blob_fee,
484 ))
485 }
486 }
487 }
488 Ordering::Less => {
489 let mut unlocked = self
492 .basefee_pool
493 .satisfy_base_fee_transactions(best_transactions_attributes.basefee);
494
495 unlocked.extend(self.blob_pool.satisfy_attributes(best_transactions_attributes));
497
498 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
499 unlocked,
500 best_transactions_attributes.basefee,
501 best_transactions_attributes.blob_fee.unwrap_or_default(),
502 ))
503 }
504 }
505 }
506
507 pub(crate) fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
509 self.pending_pool.all().collect()
510 }
511 pub(crate) fn pending_transactions_iter(
513 &self,
514 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
515 self.pending_pool.all()
516 }
517
518 pub(crate) fn pending_transactions_count(&self) -> usize {
520 self.pending_pool.len()
521 }
522
523 pub(crate) fn pending_transactions_with_predicate(
525 &self,
526 mut predicate: impl FnMut(&ValidPoolTransaction<T::Transaction>) -> bool,
527 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
528 self.pending_transactions_iter().filter(|tx| predicate(tx)).collect()
529 }
530
531 pub(crate) fn pending_txs_by_sender(
533 &self,
534 sender: SenderId,
535 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
536 self.pending_pool.txs_by_sender(sender).collect()
537 }
538
539 pub(crate) fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
541 self.basefee_pool.all().chain(self.queued_pool.all()).chain(self.blob_pool.all()).collect()
542 }
543
544 pub(crate) fn queued_transactions_count(&self) -> usize {
546 self.basefee_pool.len() + self.queued_pool.len() + self.blob_pool.len()
547 }
548
549 pub fn queued_and_pending_txs_by_sender(
551 &self,
552 sender: SenderId,
553 ) -> (SmallVec<[TransactionId; TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER]>, Vec<TransactionId>) {
554 (self.queued_pool.get_txs_by_sender(sender), self.pending_pool.get_txs_by_sender(sender))
555 }
556
557 pub(crate) fn queued_txs_by_sender(
559 &self,
560 sender: SenderId,
561 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
562 self.basefee_pool
563 .txs_by_sender(sender)
564 .chain(self.queued_pool.txs_by_sender(sender))
565 .chain(self.blob_pool.txs_by_sender(sender))
566 .collect()
567 }
568
569 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
571 self.all_transactions.contains(tx_hash)
572 }
573
574 #[cfg(test)]
576 pub(crate) fn subpool_contains(&self, subpool: SubPool, id: &TransactionId) -> bool {
577 match subpool {
578 SubPool::Queued => self.queued_pool.contains(id),
579 SubPool::Pending => self.pending_pool.contains(id),
580 SubPool::BaseFee => self.basefee_pool.contains(id),
581 SubPool::Blob => self.blob_pool.contains(id),
582 }
583 }
584
585 #[inline]
587 pub(crate) fn is_exceeded(&self) -> bool {
588 self.config.is_exceeded(self.size())
589 }
590
591 pub(crate) fn get(
593 &self,
594 tx_hash: &TxHash,
595 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
596 self.all_transactions.by_hash.get(tx_hash).cloned()
597 }
598
599 pub(crate) fn get_all(
601 &self,
602 txs: Vec<TxHash>,
603 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
604 txs.into_iter().filter_map(|tx| self.get(&tx))
605 }
606
607 pub(crate) fn get_transactions_by_sender(
609 &self,
610 sender: SenderId,
611 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
612 self.all_transactions.txs_iter(sender).map(|(_, tx)| Arc::clone(&tx.transaction)).collect()
613 }
614
615 pub(crate) fn get_pending_transaction_by_sender_and_nonce(
617 &self,
618 sender: SenderId,
619 nonce: u64,
620 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
621 self.all_transactions
622 .txs_iter(sender)
623 .find(|(id, tx)| id.nonce == nonce && tx.subpool.is_pending())
624 .map(|(_, tx)| Arc::clone(&tx.transaction))
625 }
626
627 const fn update_pending_fees_only(
630 &mut self,
631 mut new_base_fee: u64,
632 new_blob_fee: Option<u128>,
633 ) -> (u64, u128) {
634 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut new_base_fee);
635
636 let prev_blob_fee = if let Some(mut blob_fee) = new_blob_fee {
637 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut blob_fee);
638 blob_fee
639 } else {
640 self.all_transactions.pending_fees.blob_fee
641 };
642
643 (new_base_fee, prev_blob_fee)
644 }
645
646 fn apply_fee_updates(
653 &mut self,
654 prev_base_fee: u64,
655 prev_blob_fee: u128,
656 outcome: &mut UpdateOutcome<T::Transaction>,
657 ) {
658 let new_base_fee = self.all_transactions.pending_fees.base_fee;
659 let new_blob_fee = self.all_transactions.pending_fees.blob_fee;
660
661 if new_base_fee == prev_base_fee && new_blob_fee == prev_blob_fee {
662 return;
664 }
665
666 self.all_transactions.pending_fees.base_fee = prev_base_fee;
669 self.all_transactions.pending_fees.blob_fee = prev_blob_fee;
670
671 let base_fee_ordering = self.update_basefee(new_base_fee, |tx| {
672 outcome.promoted.push(tx.clone());
673 });
674
675 self.update_blob_fee(new_blob_fee, base_fee_ordering, |tx| {
676 outcome.promoted.push(tx.clone());
677 });
678 }
679
680 pub(crate) fn update_accounts(
682 &mut self,
683 changed_senders: FxHashMap<SenderId, SenderInfo>,
684 ) -> UpdateOutcome<T::Transaction> {
685 let mut updates = self.all_transactions.update(&changed_senders);
687
688 let mut outcome = UpdateOutcome::default();
690 if self.config.enforce_tracked_nonce {
691 #[expect(clippy::iter_with_drain, reason = "retain the scratch buffer allocation")]
695 self.process_updates(updates.drain(..), &mut outcome);
696 self.all_transactions.sender_info.extend(changed_senders);
697 } else {
698 self.all_transactions.sender_info.extend(changed_senders);
699 #[expect(clippy::iter_with_drain, reason = "retain the scratch buffer allocation")]
700 self.process_updates(updates.drain(..), &mut outcome);
701 }
702 self.all_transactions.update_buffer = updates;
703 self.update_size_metrics();
705 outcome
706 }
707
708 pub(crate) fn on_canonical_state_change(
713 &mut self,
714 block_info: BlockInfo,
715 mined_transactions: Vec<TxHash>,
716 changed_senders: FxHashMap<SenderId, SenderInfo>,
717 _update_kind: PoolUpdateKind,
718 ) -> OnNewCanonicalStateOutcome<T::Transaction> {
719 let block_hash = block_info.last_seen_block_hash;
721
722 let mut removed_txs_count = 0;
724 for tx_hash in &mined_transactions {
725 if self.prune_transaction_by_hash(tx_hash).is_some() {
726 removed_txs_count += 1;
727 }
728 }
729
730 self.metrics.removed_transactions.increment(removed_txs_count);
732
733 let (prev_base_fee, prev_blob_fee) =
738 self.update_pending_fees_only(block_info.pending_basefee, block_info.pending_blob_fee);
739
740 let mut outcome = self.update_accounts(changed_senders);
742
743 self.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
746
747 self.all_transactions.set_block_info(block_info);
749
750 self.update_transaction_type_metrics();
751 self.metrics.performed_state_updates.increment(1);
752
753 OnNewCanonicalStateOutcome {
754 block_hash,
755 mined: mined_transactions,
756 promoted: outcome.promoted,
757 discarded: outcome.discarded,
758 }
759 }
760
761 pub(crate) fn update_size_metrics(&self) {
763 self.all_transactions.update_size_metrics();
764 let stats = self.size();
765 self.metrics.pending_pool_transactions.set(stats.pending as f64);
766 self.metrics.pending_pool_size_bytes.set(stats.pending_size as f64);
767 self.metrics.basefee_pool_transactions.set(stats.basefee as f64);
768 self.metrics.basefee_pool_size_bytes.set(stats.basefee_size as f64);
769 self.metrics.queued_pool_transactions.set(stats.queued as f64);
770 self.metrics.queued_pool_size_bytes.set(stats.queued_size as f64);
771 self.metrics.blob_pool_transactions.set(stats.blob as f64);
772 self.metrics.blob_pool_size_bytes.set(stats.blob_size as f64);
773 self.metrics.total_transactions.set(stats.total as f64);
774 }
775
776 pub(crate) fn update_transaction_type_metrics(&self) {
778 let counts = &self.all_transactions.tx_type_counts;
779 self.metrics.total_legacy_transactions.set(counts.legacy as f64);
780 self.metrics.total_eip2930_transactions.set(counts.eip2930 as f64);
781 self.metrics.total_eip1559_transactions.set(counts.eip1559 as f64);
782 self.metrics.total_eip4844_transactions.set(counts.eip4844 as f64);
783 self.metrics.total_eip7702_transactions.set(counts.eip7702 as f64);
784 self.metrics.total_other_transactions.set(counts.other as f64);
785 }
786
787 pub(crate) fn add_transaction(
788 &mut self,
789 tx: ValidPoolTransaction<T::Transaction>,
790 mut on_chain_balance: U256,
791 mut on_chain_nonce: u64,
792 on_chain_code_hash: Option<B256>,
793 ) -> PoolResult<AddedTransaction<T::Transaction>> {
794 if self.contains(tx.hash()) {
795 return Err(PoolError::new(*tx.hash(), PoolErrorKind::AlreadyImported))
796 }
797
798 if self.config.enforce_tracked_nonce &&
802 tx.transaction.requires_nonce_check() &&
803 let Some(info) = self.all_transactions.sender_info.get(&tx.sender_id()) &&
804 info.state_nonce > on_chain_nonce
805 {
806 if tx.nonce() < info.state_nonce {
810 return Err(PoolError::new(
811 *tx.hash(),
812 InvalidPoolTransactionError::Consensus(
813 InvalidTransactionError::NonceNotConsistent {
814 tx: tx.nonce(),
815 state: info.state_nonce,
816 },
817 ),
818 ))
819 }
820 on_chain_nonce = info.state_nonce;
821 on_chain_balance = info.balance;
822 }
823
824 self.validate_auth(&tx, on_chain_nonce, on_chain_code_hash)?;
825
826 self.all_transactions
828 .sender_info
829 .entry(tx.sender_id())
830 .or_default()
831 .update(on_chain_nonce, on_chain_balance);
832
833 match self.all_transactions.insert_tx(tx, on_chain_balance, on_chain_nonce) {
834 Ok(InsertOk { transaction, move_to, replaced_tx, mut updates, state }) => {
835 let new_nonce = transaction.id().nonce;
843 let split = updates.partition_point(|u| u.id.nonce < new_nonce);
844 let mut outcome = UpdateOutcome::default();
845 #[expect(clippy::iter_with_drain, reason = "retain the scratch buffer allocation")]
846 let mut drain = updates.drain(..);
847 self.process_updates(drain.by_ref().take(split), &mut outcome);
848 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
849 self.process_updates(drain, &mut outcome);
850 self.all_transactions.update_buffer = updates;
851 let UpdateOutcome { promoted, discarded } = outcome;
852 self.metrics.inserted_transactions.increment(1);
853
854 let replaced = replaced_tx.map(|(tx, _)| tx);
855
856 let res = if move_to.is_pending() {
858 AddedTransaction::Pending(AddedPendingTransaction {
859 transaction,
860 promoted,
861 discarded,
862 replaced,
863 })
864 } else {
865 let queued_reason = state.determine_queued_reason(move_to);
867 AddedTransaction::Parked {
868 transaction,
869 subpool: move_to,
870 replaced,
871 queued_reason,
872 promoted,
873 }
874 };
875
876 Ok(res)
877 }
878 Err(err) => {
879 self.metrics.invalid_transactions.increment(1);
881 match err {
882 InsertErr::Underpriced { existing: _, transaction } => Err(PoolError::new(
883 *transaction.hash(),
884 PoolErrorKind::ReplacementUnderpriced,
885 )),
886 InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap } => {
887 Err(PoolError::new(
888 *transaction.hash(),
889 PoolErrorKind::FeeCapBelowMinimumProtocolFeeCap(fee_cap),
890 ))
891 }
892 InsertErr::ExceededSenderTransactionsCapacity { transaction } => {
893 Err(PoolError::new(
894 *transaction.hash(),
895 PoolErrorKind::SpammerExceededCapacity(transaction.sender()),
896 ))
897 }
898 InsertErr::TxGasLimitMoreThanAvailableBlockGas {
899 transaction,
900 block_gas_limit,
901 tx_gas_limit,
902 } => Err(PoolError::new(
903 *transaction.hash(),
904 PoolErrorKind::InvalidTransaction(
905 InvalidPoolTransactionError::ExceedsGasLimit(
906 tx_gas_limit,
907 block_gas_limit,
908 ),
909 ),
910 )),
911 InsertErr::BlobTxHasNonceGap { transaction } => Err(PoolError::new(
912 *transaction.hash(),
913 PoolErrorKind::InvalidTransaction(
914 Eip4844PoolTransactionError::Eip4844NonceGap.into(),
915 ),
916 )),
917 InsertErr::Overdraft { transaction } => Err(PoolError::new(
918 *transaction.hash(),
919 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Overdraft {
920 cost: *transaction.cost(),
921 balance: on_chain_balance,
922 }),
923 )),
924 InsertErr::TxTypeConflict { transaction } => Err(PoolError::new(
925 *transaction.hash(),
926 PoolErrorKind::ExistingConflictingTransactionType(
927 transaction.sender(),
928 transaction.tx_type(),
929 ),
930 )),
931 }
932 }
933 }
934 }
935
936 fn check_delegation_limit(
940 &self,
941 transaction: &ValidPoolTransaction<T::Transaction>,
942 on_chain_nonce: u64,
943 on_chain_code_hash: Option<B256>,
944 ) -> Result<(), PoolError> {
945 if (on_chain_code_hash.is_none() || on_chain_code_hash == Some(KECCAK_EMPTY)) &&
947 !self.all_transactions.auths.contains_key(&transaction.sender_id())
948 {
949 return Ok(())
950 }
951
952 let mut txs_by_sender =
953 self.pending_pool.iter_txs_by_sender(transaction.sender_id()).peekable();
954
955 if txs_by_sender.peek().is_none() {
956 let nonce_gap_distance = transaction.nonce().saturating_sub(on_chain_nonce);
961 if nonce_gap_distance >= self.config.max_inflight_delegated_slot_limit as u64 {
962 return Err(PoolError::new(
963 *transaction.hash(),
964 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
965 Eip7702PoolTransactionError::OutOfOrderTxFromDelegated,
966 )),
967 ))
968 }
969 return Ok(())
970 }
971
972 let mut count = 0;
973 for id in txs_by_sender {
974 if id == &transaction.transaction_id {
975 return Ok(())
977 }
978 count += 1;
979 }
980
981 if count < self.config.max_inflight_delegated_slot_limit {
982 return Ok(())
984 }
985
986 Err(PoolError::new(
987 *transaction.hash(),
988 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
989 Eip7702PoolTransactionError::InflightTxLimitReached,
990 )),
991 ))
992 }
993
994 fn validate_auth(
1004 &self,
1005 transaction: &ValidPoolTransaction<T::Transaction>,
1006 on_chain_nonce: u64,
1007 on_chain_code_hash: Option<B256>,
1008 ) -> Result<(), PoolError> {
1009 self.check_delegation_limit(transaction, on_chain_nonce, on_chain_code_hash)?;
1011
1012 if let Some(authority_list) = &transaction.authority_ids {
1013 for sender_id in authority_list {
1014 if self
1016 .all_transactions
1017 .txs_iter(*sender_id)
1018 .nth(self.config.max_inflight_delegated_slot_limit)
1019 .is_some()
1020 {
1021 return Err(PoolError::new(
1022 *transaction.hash(),
1023 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
1024 Eip7702PoolTransactionError::AuthorityReserved,
1025 )),
1026 ))
1027 }
1028 }
1029 }
1030
1031 Ok(())
1032 }
1033
1034 fn process_updates(
1038 &mut self,
1039 updates: impl IntoIterator<Item = PoolUpdate>,
1040 outcome: &mut UpdateOutcome<T::Transaction>,
1041 ) {
1042 let mut removed = 0;
1043 for PoolUpdate { id, current, destination } in updates {
1044 match destination {
1045 Destination::Discard => {
1046 if let Some(tx) = self.prune_transaction_by_id(&id) {
1048 outcome.discarded.push(tx);
1049 }
1050 removed += 1;
1051 }
1052 Destination::Pool(move_to) => {
1053 debug_assert_ne!(&move_to, ¤t, "destination must be different");
1054 let moved = self.move_transaction(current, move_to, &id);
1055 if move_to.is_pending() &&
1056 let Some(tx) = moved
1057 {
1058 trace!(target: "txpool", hash=%tx.transaction.hash(), "Promoted transaction to pending");
1059 outcome.promoted.push(tx);
1060 }
1061 }
1062 }
1063 }
1064
1065 if removed > 0 {
1066 self.metrics.removed_transactions.increment(removed);
1067 }
1068 }
1069
1070 fn move_transaction(
1075 &mut self,
1076 from: SubPool,
1077 to: SubPool,
1078 id: &TransactionId,
1079 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1080 let tx = self.remove_from_subpool(from, id)?;
1081 self.add_transaction_to_subpool(to, tx.clone());
1082 Some(tx)
1083 }
1084
1085 pub(crate) fn remove_transactions(
1090 &mut self,
1091 hashes: Vec<TxHash>,
1092 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1093 let txs =
1094 hashes.into_iter().filter_map(|hash| self.remove_transaction_by_hash(&hash)).collect();
1095 self.update_size_metrics();
1096 txs
1097 }
1098
1099 pub(crate) fn remove_transactions_and_descendants(
1101 &mut self,
1102 hashes: Vec<TxHash>,
1103 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1104 let mut removed = Vec::new();
1105 for hash in hashes {
1106 if let Some(tx) = self.remove_transaction_by_hash(&hash) {
1107 removed.push(tx.clone());
1108 self.remove_descendants(tx.id(), &mut removed);
1109 }
1110 }
1111 self.update_size_metrics();
1112 removed
1113 }
1114
1115 pub(crate) fn remove_transactions_by_sender(
1117 &mut self,
1118 sender_id: SenderId,
1119 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1120 let mut removed = Vec::new();
1121 let txs = self.get_transactions_by_sender(sender_id);
1122 for tx in txs {
1123 if let Some(tx) = self.remove_transaction(tx.id()) {
1124 removed.push(tx);
1125 }
1126 }
1127 self.update_size_metrics();
1128 removed
1129 }
1130
1131 pub(crate) fn prune_transactions(
1137 &mut self,
1138 hashes: Vec<TxHash>,
1139 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1140 let txs =
1141 hashes.into_iter().filter_map(|hash| self.prune_transaction_by_hash(&hash)).collect();
1142 self.update_size_metrics();
1143 txs
1144 }
1145
1146 fn remove_transaction(
1150 &mut self,
1151 id: &TransactionId,
1152 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1153 let (tx, pool) = self.all_transactions.remove_transaction(id)?;
1154 self.remove_from_subpool(pool, tx.id())
1155 }
1156
1157 fn remove_transaction_by_hash(
1163 &mut self,
1164 tx_hash: &B256,
1165 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1166 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1167
1168 let mut updates = self.all_transactions.park_descendant_transactions(tx.id());
1170 #[expect(clippy::iter_with_drain, reason = "retain the scratch buffer allocation")]
1171 self.process_updates(updates.drain(..), &mut UpdateOutcome::default());
1172 self.all_transactions.update_buffer = updates;
1173 self.remove_from_subpool(pool, tx.id())
1174 }
1175
1176 fn prune_transaction_by_hash(
1183 &mut self,
1184 tx_hash: &B256,
1185 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1186 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1187 self.remove_from_subpool(pool, tx.id())
1188 }
1189 fn prune_transaction_by_id(
1194 &mut self,
1195 tx_id: &TransactionId,
1196 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1197 let (tx, pool) = self.all_transactions.remove_transaction_by_id(tx_id)?;
1198 self.remove_from_subpool(pool, tx.id())
1199 }
1200
1201 fn remove_from_subpool(
1205 &mut self,
1206 pool: SubPool,
1207 tx: &TransactionId,
1208 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1209 let tx = match pool {
1210 SubPool::Queued => self.queued_pool.remove_transaction(tx),
1211 SubPool::Pending => self.pending_pool.remove_transaction(tx),
1212 SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
1213 SubPool::Blob => self.blob_pool.remove_transaction(tx),
1214 };
1215
1216 if let Some(ref tx) = tx {
1217 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Removed transaction from a subpool");
1221 }
1222
1223 tx
1224 }
1225
1226 fn remove_descendants(
1230 &mut self,
1231 tx: &TransactionId,
1232 removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
1233 ) {
1234 let mut id = *tx;
1235
1236 loop {
1238 let descendant =
1239 self.all_transactions.descendant_txs_exclusive(&id).map(|(id, _)| *id).next();
1240 if let Some(descendant) = descendant {
1241 if let Some(tx) = self.remove_transaction(&descendant) {
1242 removed.push(tx)
1243 }
1244 id = descendant;
1245 } else {
1246 return
1247 }
1248 }
1249 }
1250
1251 fn add_transaction_to_subpool(
1253 &mut self,
1254 pool: SubPool,
1255 tx: Arc<ValidPoolTransaction<T::Transaction>>,
1256 ) {
1257 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Adding transaction to a subpool");
1261 match pool {
1262 SubPool::Queued => self.queued_pool.add_transaction(tx),
1263 SubPool::Pending => {
1264 self.pending_pool.add_transaction(tx, self.all_transactions.pending_fees.base_fee);
1265 }
1266 SubPool::BaseFee => {
1267 self.basefee_pool.add_transaction(tx);
1268 }
1269 SubPool::Blob => {
1270 self.blob_pool.add_transaction(tx);
1271 }
1272 }
1273 }
1274
1275 fn add_new_transaction(
1278 &mut self,
1279 transaction: Arc<ValidPoolTransaction<T::Transaction>>,
1280 replaced: Option<(Arc<ValidPoolTransaction<T::Transaction>>, SubPool)>,
1281 pool: SubPool,
1282 ) {
1283 if let Some((replaced, replaced_pool)) = replaced {
1284 self.remove_from_subpool(replaced_pool, replaced.id());
1286 }
1287
1288 self.add_transaction_to_subpool(pool, transaction)
1289 }
1290
1291 pub(crate) fn discard_worst(&mut self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1298 let mut removed = Vec::new();
1299
1300 macro_rules! discard_worst {
1302 ($this:ident, $removed:ident, [$($limit:ident => ($pool:ident, $metric:ident)),* $(,)*]) => {
1303 $ (
1304 while $this.$pool.exceeds(&$this.config.$limit)
1305 {
1306 trace!(
1307 target: "txpool",
1308 "discarding transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1309 stringify!($pool),
1310 $this.config.$limit,
1311 $this.$pool.size(),
1312 $this.$pool.len(),
1313 );
1314
1315 let removed_from_subpool = $this.$pool.truncate_pool($this.config.$limit.clone());
1317
1318 trace!(
1319 target: "txpool",
1320 "removed {} transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1321 removed_from_subpool.len(),
1322 stringify!($pool),
1323 $this.config.$limit,
1324 $this.$pool.size(),
1325 $this.$pool.len()
1326 );
1327 $this.metrics.$metric.increment(removed_from_subpool.len() as u64);
1328
1329 for tx in removed_from_subpool {
1331 $this.all_transactions.remove_transaction(tx.id());
1332
1333 let id = *tx.id();
1334
1335 removed.push(tx);
1337
1338 $this.remove_descendants(&id, &mut $removed);
1340 }
1341 }
1342
1343 )*
1344 };
1345 }
1346
1347 discard_worst!(
1348 self, removed, [
1349 pending_limit => (pending_pool, pending_transactions_evicted),
1350 basefee_limit => (basefee_pool, basefee_transactions_evicted),
1351 blob_limit => (blob_pool, blob_transactions_evicted),
1352 queued_limit => (queued_pool, queued_transactions_evicted),
1353 ]
1354 );
1355
1356 removed
1357 }
1358
1359 pub(crate) fn len(&self) -> usize {
1361 self.all_transactions.len()
1362 }
1363
1364 pub(crate) fn is_empty(&self) -> bool {
1366 self.all_transactions.is_empty()
1367 }
1368
1369 #[cfg(any(test, feature = "test-utils"))]
1377 pub fn assert_invariants(&self) {
1378 let size = self.size();
1379 let actual = size.basefee + size.pending + size.queued + size.blob;
1380 assert_eq!(
1381 size.total, actual,
1382 "total size must be equal to the sum of all sub-pools, basefee:{}, pending:{}, queued:{}, blob:{}",
1383 size.basefee, size.pending, size.queued, size.blob
1384 );
1385 self.all_transactions.assert_invariants();
1386 self.pending_pool.assert_invariants();
1387 self.basefee_pool.assert_invariants();
1388 self.queued_pool.assert_invariants();
1389 self.blob_pool.assert_invariants();
1390 }
1391}
1392
1393#[cfg(any(test, feature = "test-utils"))]
1394impl TxPool<crate::test_utils::MockOrdering> {
1395 pub fn mock() -> Self {
1397 Self::new(crate::test_utils::MockOrdering::default(), PoolConfig::default())
1398 }
1399}
1400
1401#[cfg(test)]
1402impl<T: TransactionOrdering> Drop for TxPool<T> {
1403 fn drop(&mut self) {
1404 self.assert_invariants();
1405 }
1406}
1407
1408impl<T: TransactionOrdering> TxPool<T> {
1409 pub const fn pending(&self) -> &PendingPool<T> {
1411 &self.pending_pool
1412 }
1413
1414 pub const fn base_fee(&self) -> &ParkedPool<BasefeeOrd<T::Transaction>> {
1416 &self.basefee_pool
1417 }
1418
1419 pub const fn queued(&self) -> &ParkedPool<QueuedOrd<T::Transaction>> {
1421 &self.queued_pool
1422 }
1423}
1424
1425impl<T: TransactionOrdering> fmt::Debug for TxPool<T> {
1426 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1427 f.debug_struct("TxPool").field("config", &self.config).finish_non_exhaustive()
1428 }
1429}
1430
1431const FULL_UPDATE_MIN_SENDERS: usize = 1_000;
1436
1437const FULL_UPDATE_SENDER_RATIO: usize = 4;
1439
1440pub(crate) struct AllTransactions<T: PoolTransaction> {
1445 minimal_protocol_basefee: u64,
1449 block_gas_limit: u64,
1451 max_account_slots: usize,
1453 by_hash: B256Map<Arc<ValidPoolTransaction<T>>>,
1455 txs: BTreeMap<TransactionId, PoolInternalTransaction<T>>,
1457 sender_info: FxHashMap<SenderId, SenderInfo>,
1463 tx_counter: FxHashMap<SenderId, usize>,
1465 last_seen_block_number: u64,
1467 last_seen_block_hash: B256,
1469 pending_fees: PendingFees,
1471 last_full_update_fees: PendingFees,
1479 price_bumps: PriceBumpConfig,
1481 local_transactions_config: LocalTransactionConfig,
1483 auths: FxHashMap<SenderId, B256Set>,
1485 tx_type_counts: TxTypeCounts,
1488 update_buffer: Vec<PoolUpdate>,
1491 metrics: AllTransactionsMetrics,
1493}
1494
1495impl<T: PoolTransaction> AllTransactions<T> {
1496 fn new(config: &PoolConfig) -> Self {
1498 Self {
1499 max_account_slots: config.max_account_slots,
1500 price_bumps: config.price_bumps,
1501 local_transactions_config: config.local_transactions_config.clone(),
1502 minimal_protocol_basefee: config.minimal_protocol_basefee,
1503 block_gas_limit: config.gas_limit,
1504 ..Default::default()
1505 }
1506 }
1507
1508 #[expect(dead_code)]
1510 pub(crate) fn hashes_iter(&self) -> impl Iterator<Item = TxHash> + '_ {
1511 self.by_hash.keys().copied()
1512 }
1513
1514 pub(crate) fn transactions_iter(
1516 &self,
1517 ) -> impl Iterator<Item = &Arc<ValidPoolTransaction<T>>> + '_ {
1518 self.by_hash.values()
1519 }
1520
1521 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
1523 self.by_hash.contains_key(tx_hash)
1524 }
1525
1526 pub(crate) fn get(&self, id: &TransactionId) -> Option<&PoolInternalTransaction<T>> {
1528 self.txs.get(id)
1529 }
1530
1531 pub(crate) fn tx_inc(&mut self, sender: SenderId) {
1533 let count = self.tx_counter.entry(sender).or_default();
1534 *count += 1;
1535 self.metrics.all_transactions_by_all_senders.increment(1.0);
1536 }
1537
1538 pub(crate) fn tx_decr(&mut self, sender: SenderId) {
1540 if let hash_map::Entry::Occupied(mut entry) = self.tx_counter.entry(sender) {
1541 let count = entry.get_mut();
1542 if *count == 1 {
1543 entry.remove();
1544 self.sender_info.remove(&sender);
1545 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1546 return
1547 }
1548 *count -= 1;
1549 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1550 }
1551 }
1552
1553 fn set_block_info(&mut self, block_info: BlockInfo) {
1555 let BlockInfo {
1556 block_gas_limit,
1557 last_seen_block_hash,
1558 last_seen_block_number,
1559 pending_basefee,
1560 pending_blob_fee,
1561 } = block_info;
1562 self.last_seen_block_number = last_seen_block_number;
1563 self.last_seen_block_hash = last_seen_block_hash;
1564
1565 self.pending_fees.base_fee = pending_basefee;
1566 self.metrics.base_fee.set(pending_basefee as f64);
1567
1568 self.block_gas_limit = block_gas_limit;
1569
1570 if let Some(pending_blob_fee) = pending_blob_fee {
1571 self.pending_fees.blob_fee = pending_blob_fee;
1572 self.metrics.blob_base_fee.set(pending_blob_fee as f64);
1573 }
1574 }
1575
1576 pub(crate) fn update_size_metrics(&self) {
1578 self.metrics.all_transactions_by_hash.set(self.by_hash.len() as f64);
1579 self.metrics.all_transactions_by_id.set(self.txs.len() as f64);
1580 }
1581
1582 pub(crate) fn update(
1599 &mut self,
1600 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1601 ) -> Vec<PoolUpdate> {
1602 let mut updates = std::mem::take(&mut self.update_buffer);
1603 let pending_fees = self.pending_fees;
1604
1605 let update_all = self.last_full_update_fees != self.pending_fees ||
1606 self.should_update_all_senders(changed_accounts.len());
1607
1608 if update_all {
1609 Self::update_txs(pending_fees, changed_accounts, &mut updates, self.txs.iter_mut());
1610 self.last_full_update_fees = self.pending_fees;
1611 } else {
1612 for sender in changed_accounts.keys() {
1615 let range = TransactionId::new(*sender, 0)..=TransactionId::new(*sender, u64::MAX);
1616 Self::update_txs(
1617 pending_fees,
1618 changed_accounts,
1619 &mut updates,
1620 self.txs.range_mut(range),
1621 );
1622 }
1623 }
1624
1625 updates
1626 }
1627
1628 fn should_update_all_senders(&self, changed_sender_count: usize) -> bool {
1630 let pool_sender_count = self.tx_counter.len();
1631 changed_sender_count >= pool_sender_count ||
1632 (pool_sender_count >= FULL_UPDATE_MIN_SENDERS &&
1633 changed_sender_count >= pool_sender_count.div_ceil(FULL_UPDATE_SENDER_RATIO))
1634 }
1635
1636 fn update_txs<'a, I>(
1641 pending_fees: PendingFees,
1642 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1643 updates: &mut Vec<PoolUpdate>,
1644 txs: I,
1645 ) where
1646 T: 'a,
1647 I: Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)>,
1648 {
1649 let mut iter = txs.peekable();
1650
1651 'transactions: while let Some((id, tx)) = iter.next() {
1661 macro_rules! next_sender {
1662 ($iter:ident) => {
1663 'this: while let Some((peek, _)) = iter.peek() {
1664 if peek.sender != id.sender {
1665 break 'this
1666 }
1667 iter.next();
1668 }
1669 };
1670 }
1671
1672 let changed_balance = if let Some(info) = changed_accounts.get(&id.sender) {
1675 if id.nonce < info.state_nonce {
1677 updates.push(PoolUpdate {
1678 id: *tx.transaction.id(),
1679 current: tx.subpool,
1680 destination: Destination::Discard,
1681 });
1682 continue 'transactions
1683 }
1684
1685 let ancestor = TransactionId::ancestor(id.nonce, info.state_nonce, id.sender);
1686 if ancestor.is_none() {
1688 tx.state.insert(TxState::NO_NONCE_GAPS);
1689 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1690 tx.cumulative_cost = U256::ZERO;
1691 if tx.transaction.cost() > &info.balance {
1692 tx.state.remove(TxState::ENOUGH_BALANCE);
1694 } else {
1695 tx.state.insert(TxState::ENOUGH_BALANCE);
1696 }
1697 }
1698
1699 Some(&info.balance)
1700 } else {
1701 None
1702 };
1703
1704 if tx.state.has_nonce_gap() {
1706 next_sender!(iter);
1707 continue 'transactions
1708 }
1709
1710 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1712
1713 Self::update_tx_fees(pending_fees, tx);
1715 Self::record_subpool_update(updates, tx);
1717
1718 let mut has_parked_ancestor = !tx.state.is_pending();
1720
1721 let mut cumulative_cost = tx.next_cumulative_cost();
1722
1723 let mut next_nonce_in_line = tx.transaction.nonce().saturating_add(1);
1725
1726 while let Some((peek, tx)) = iter.peek_mut() {
1728 if peek.sender != id.sender {
1729 continue 'transactions
1731 }
1732
1733 if tx.transaction.nonce() == next_nonce_in_line {
1734 tx.state.insert(TxState::NO_NONCE_GAPS);
1736 } else {
1737 next_sender!(iter);
1739 continue 'transactions
1740 }
1741
1742 next_nonce_in_line = next_nonce_in_line.saturating_add(1);
1744
1745 tx.cumulative_cost = cumulative_cost;
1747 cumulative_cost = tx.next_cumulative_cost();
1749
1750 if let Some(changed_balance) = changed_balance {
1752 if &cumulative_cost > changed_balance {
1753 tx.state.remove(TxState::ENOUGH_BALANCE);
1755 } else {
1756 tx.state.insert(TxState::ENOUGH_BALANCE);
1757 }
1758 }
1759
1760 if has_parked_ancestor {
1762 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1763 } else {
1764 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1765 }
1766
1767 Self::update_tx_fees(pending_fees, tx);
1769 Self::record_subpool_update(updates, tx);
1770 has_parked_ancestor = !tx.state.is_pending();
1771
1772 iter.next();
1774 }
1775 }
1776 }
1777
1778 fn record_subpool_update(updates: &mut Vec<PoolUpdate>, tx: &mut PoolInternalTransaction<T>) {
1783 let current_pool = tx.subpool;
1784 tx.subpool = tx.state.into();
1785 if current_pool != tx.subpool {
1786 updates.push(PoolUpdate {
1787 id: *tx.transaction.id(),
1788 current: current_pool,
1789 destination: tx.subpool.into(),
1790 })
1791 }
1792 }
1793
1794 fn update_tx_fees(pending_fees: PendingFees, tx: &mut PoolInternalTransaction<T>) {
1796 match tx.transaction.max_fee_per_gas().cmp(&(pending_fees.base_fee as u128)) {
1797 Ordering::Greater | Ordering::Equal => {
1798 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1799 }
1800 Ordering::Less => {
1801 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
1802 }
1803 }
1804
1805 match tx.transaction.max_fee_per_blob_gas() {
1806 Some(blob_fee_cap) if blob_fee_cap < pending_fees.blob_fee => {
1807 tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1808 }
1809 _ => {
1810 tx.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1811 }
1812 }
1813 }
1814
1815 pub(crate) fn txs_iter(
1818 &self,
1819 sender: SenderId,
1820 ) -> impl Iterator<Item = (&TransactionId, &PoolInternalTransaction<T>)> + '_ {
1821 self.txs
1822 .range((sender.start_bound(), Unbounded))
1823 .take_while(move |(other, _)| sender == other.sender)
1824 }
1825
1826 #[cfg(test)]
1829 #[expect(dead_code)]
1830 pub(crate) fn txs_iter_mut(
1831 &mut self,
1832 sender: SenderId,
1833 ) -> impl Iterator<Item = (&TransactionId, &mut PoolInternalTransaction<T>)> + '_ {
1834 self.txs
1835 .range_mut((sender.start_bound(), Unbounded))
1836 .take_while(move |(other, _)| sender == other.sender)
1837 }
1838
1839 pub(crate) fn descendant_txs_exclusive<'a, 'b: 'a>(
1843 &'a self,
1844 id: &'b TransactionId,
1845 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1846 self.txs.range((Excluded(id), Unbounded)).take_while(|(other, _)| id.sender == other.sender)
1847 }
1848
1849 pub(crate) fn descendant_txs_inclusive<'a, 'b: 'a>(
1854 &'a self,
1855 id: &'b TransactionId,
1856 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1857 self.txs.range(id..).take_while(|(other, _)| id.sender == other.sender)
1858 }
1859
1860 pub(crate) fn descendant_txs_mut<'a, 'b: 'a>(
1865 &'a mut self,
1866 id: &'b TransactionId,
1867 ) -> impl Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)> + 'a {
1868 self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender)
1869 }
1870
1871 pub(crate) fn remove_transaction_by_hash(
1873 &mut self,
1874 tx_hash: &B256,
1875 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1876 let tx = self.by_hash.remove(tx_hash)?;
1877 let internal = self.txs.remove(&tx.transaction_id)?;
1878 self.remove_auths(&internal);
1879 self.tx_type_counts.dec(internal.transaction.transaction.ty());
1880 self.tx_decr(tx.sender_id());
1882 Some((tx, internal.subpool))
1883 }
1884
1885 pub(crate) fn remove_transaction_by_id(
1889 &mut self,
1890 tx_id: &TransactionId,
1891 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1892 let internal = self.txs.remove(tx_id)?;
1893 let tx = self.by_hash.remove(internal.transaction.hash())?;
1894 self.remove_auths(&internal);
1895 self.tx_type_counts.dec(internal.transaction.transaction.ty());
1896 self.tx_decr(tx.sender_id());
1898 Some((tx, internal.subpool))
1899 }
1900
1901 pub(crate) fn park_descendant_transactions(
1903 &mut self,
1904 tx_id: &TransactionId,
1905 ) -> Vec<PoolUpdate> {
1906 let mut updates = std::mem::take(&mut self.update_buffer);
1907
1908 for (id, tx) in self.descendant_txs_mut(tx_id) {
1909 let current_pool = tx.subpool;
1910
1911 tx.state.remove(TxState::NO_NONCE_GAPS);
1912
1913 tx.subpool = tx.state.into();
1915
1916 if current_pool != tx.subpool {
1918 updates.push(PoolUpdate {
1919 id: *id,
1920 current: current_pool,
1921 destination: tx.subpool.into(),
1922 })
1923 }
1924 }
1925
1926 updates
1927 }
1928
1929 pub(crate) fn remove_transaction(
1935 &mut self,
1936 id: &TransactionId,
1937 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1938 let internal = self.txs.remove(id)?;
1939
1940 self.tx_decr(internal.transaction.sender_id());
1942 self.tx_type_counts.dec(internal.transaction.transaction.ty());
1943
1944 let result =
1945 self.by_hash.remove(internal.transaction.hash()).map(|tx| (tx, internal.subpool));
1946
1947 self.remove_auths(&internal);
1948
1949 result
1950 }
1951
1952 fn remove_auths(&mut self, tx: &PoolInternalTransaction<T>) {
1956 let Some(auths) = &tx.transaction.authority_ids else { return };
1957
1958 let tx_hash = tx.transaction.hash();
1959 for auth in auths {
1960 if let Some(list) = self.auths.get_mut(auth) {
1961 list.remove(tx_hash);
1962 if list.is_empty() {
1963 self.auths.remove(auth);
1964 }
1965 }
1966 }
1967 }
1968
1969 #[inline]
1975 fn contains_conflicting_transaction(&self, tx: &ValidPoolTransaction<T>) -> bool {
1976 self.txs_iter(tx.transaction_id.sender)
1977 .next()
1978 .is_some_and(|(_, existing)| tx.tx_type_conflicts_with(&existing.transaction))
1979 }
1980
1981 fn ensure_valid(
1990 &self,
1991 transaction: ValidPoolTransaction<T>,
1992 on_chain_nonce: u64,
1993 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1994 if !self.local_transactions_config.is_local(transaction.origin, transaction.sender_ref()) {
1995 let current_txs =
1996 self.tx_counter.get(&transaction.sender_id()).copied().unwrap_or_default();
1997
1998 if current_txs >= self.max_account_slots && transaction.nonce() > on_chain_nonce {
2001 return Err(InsertErr::ExceededSenderTransactionsCapacity {
2002 transaction: Arc::new(transaction),
2003 })
2004 }
2005 }
2006 if transaction.gas_limit() > self.block_gas_limit {
2007 return Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas {
2008 block_gas_limit: self.block_gas_limit,
2009 tx_gas_limit: transaction.gas_limit(),
2010 transaction: Arc::new(transaction),
2011 })
2012 }
2013
2014 if self.contains_conflicting_transaction(&transaction) {
2015 return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) })
2017 }
2018
2019 Ok(transaction)
2020 }
2021
2022 fn ensure_valid_blob_transaction(
2028 &self,
2029 new_blob_tx: ValidPoolTransaction<T>,
2030 on_chain_balance: U256,
2031 ancestor: Option<TransactionId>,
2032 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
2033 if let Some(ancestor) = ancestor {
2034 let Some(ancestor_tx) = self.txs.get(&ancestor) else {
2035 self.metrics.blob_transactions_nonce_gaps.increment(1);
2037 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
2038 };
2039 if ancestor_tx.state.has_nonce_gap() {
2040 self.metrics.blob_transactions_nonce_gaps.increment(1);
2043 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
2044 }
2045
2046 let mut cumulative_cost = ancestor_tx.next_cumulative_cost() + new_blob_tx.cost();
2048
2049 if cumulative_cost > on_chain_balance {
2051 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
2053 }
2054
2055 let id = new_blob_tx.transaction_id;
2058 let mut descendants = self.descendant_txs_inclusive(&id).peekable();
2059 if let Some((maybe_replacement, _)) = descendants.peek() &&
2060 **maybe_replacement == new_blob_tx.transaction_id
2061 {
2062 descendants.next();
2064
2065 for (_, tx) in descendants {
2067 cumulative_cost += tx.transaction.cost();
2068 if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance {
2069 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
2071 }
2072 }
2073 }
2074 } else if new_blob_tx.cost() > &on_chain_balance {
2075 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
2077 }
2078
2079 Ok(new_blob_tx)
2080 }
2081
2082 pub(crate) fn insert_tx(
2114 &mut self,
2115 transaction: ValidPoolTransaction<T>,
2116 on_chain_balance: U256,
2117 on_chain_nonce: u64,
2118 ) -> InsertResult<T> {
2119 assert!(on_chain_nonce <= transaction.nonce(), "Invalid transaction");
2120
2121 let mut transaction = self.ensure_valid(transaction, on_chain_nonce)?;
2122
2123 let inserted_tx_id = *transaction.id();
2124 let mut state = TxState::default();
2125 let mut cumulative_cost = U256::ZERO;
2126
2127 state.insert(TxState::NOT_TOO_MUCH_GAS);
2129
2130 let ancestor = TransactionId::ancestor(
2133 transaction.transaction.nonce(),
2134 on_chain_nonce,
2135 inserted_tx_id.sender,
2136 );
2137
2138 if transaction.is_eip4844() {
2141 state.insert(TxState::BLOB_TRANSACTION);
2142
2143 transaction =
2144 self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
2145 let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();
2146 if blob_fee_cap >= self.pending_fees.blob_fee {
2147 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
2148 }
2149 } else {
2150 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
2152 }
2153
2154 let transaction = Arc::new(transaction);
2155
2156 if ancestor.is_none() {
2158 state.insert(TxState::NO_NONCE_GAPS);
2159 state.insert(TxState::NO_PARKED_ANCESTORS);
2160 }
2161
2162 let fee_cap = transaction.max_fee_per_gas();
2164
2165 if fee_cap < self.minimal_protocol_basefee as u128 {
2166 return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap })
2167 }
2168 if fee_cap >= self.pending_fees.base_fee as u128 {
2169 state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
2170 }
2171
2172 let mut replaced_tx = None;
2174
2175 let pool_tx = PoolInternalTransaction {
2176 transaction: Arc::clone(&transaction),
2177 subpool: state.into(),
2178 state,
2179 cumulative_cost,
2180 };
2181
2182 match self.txs.entry(*transaction.id()) {
2184 Entry::Vacant(entry) => {
2185 self.by_hash.insert(*pool_tx.transaction.hash(), pool_tx.transaction.clone());
2187 self.tx_type_counts.inc(pool_tx.transaction.transaction.ty());
2188 entry.insert(pool_tx);
2189 }
2190 Entry::Occupied(mut entry) => {
2191 let existing_transaction = entry.get().transaction.as_ref();
2193 let maybe_replacement = transaction.as_ref();
2194
2195 if existing_transaction
2197 .is_replacement_underpriced(maybe_replacement, &self.price_bumps)
2198 {
2199 return Err(InsertErr::Underpriced {
2200 transaction: pool_tx.transaction,
2201 existing: *entry.get().transaction.hash(),
2202 })
2203 }
2204 let new_hash = *pool_tx.transaction.hash();
2205 let new_transaction = pool_tx.transaction.clone();
2206 self.tx_type_counts.inc(pool_tx.transaction.transaction.ty());
2207 let replaced = entry.insert(pool_tx);
2208 self.tx_type_counts.dec(replaced.transaction.transaction.ty());
2209 self.by_hash.remove(replaced.transaction.hash());
2210 self.by_hash.insert(new_hash, new_transaction);
2211
2212 self.remove_auths(&replaced);
2213
2214 replaced_tx = Some((replaced.transaction, replaced.subpool));
2216 }
2217 }
2218
2219 if let Some(auths) = &transaction.authority_ids {
2220 let tx_hash = transaction.hash();
2221 for auth in auths {
2222 self.auths.entry(*auth).or_default().insert(*tx_hash);
2223 }
2224 }
2225
2226 let mut updates = std::mem::take(&mut self.update_buffer);
2228
2229 let on_chain_id = TransactionId::new(transaction.sender_id(), on_chain_nonce);
2231 let pending_fees = self.pending_fees;
2232 {
2233 let mut next_nonce = on_chain_id.nonce;
2235
2236 let mut has_parked_ancestor = false;
2240
2241 for (id, tx) in self.descendant_txs_mut(&on_chain_id) {
2244 let current_pool = tx.subpool;
2245
2246 if next_nonce != id.nonce {
2248 break
2249 }
2250
2251 tx.state.insert(TxState::NO_NONCE_GAPS);
2253
2254 tx.cumulative_cost = cumulative_cost;
2256
2257 cumulative_cost = tx.next_cumulative_cost();
2259
2260 if cumulative_cost > on_chain_balance {
2261 tx.state.remove(TxState::ENOUGH_BALANCE);
2263 } else {
2264 tx.state.insert(TxState::ENOUGH_BALANCE);
2265 }
2266
2267 if has_parked_ancestor {
2269 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
2270 } else {
2271 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
2272 }
2273
2274 Self::update_tx_fees(pending_fees, tx);
2275
2276 tx.subpool = tx.state.into();
2278 has_parked_ancestor = !tx.state.is_pending();
2279
2280 if inserted_tx_id.eq(id) {
2281 state = tx.state;
2283 } else {
2284 if current_pool != tx.subpool {
2286 updates.push(PoolUpdate {
2287 id: *id,
2288 current: current_pool,
2289 destination: tx.subpool.into(),
2290 })
2291 }
2292 }
2293
2294 next_nonce = id.next_nonce();
2296 }
2297 }
2298
2299 if replaced_tx.is_none() {
2301 self.tx_inc(inserted_tx_id.sender);
2302 }
2303
2304 Ok(InsertOk { transaction, move_to: state.into(), state, replaced_tx, updates })
2305 }
2306
2307 pub(crate) fn len(&self) -> usize {
2309 self.txs.len()
2310 }
2311
2312 pub(crate) fn is_empty(&self) -> bool {
2314 self.txs.is_empty()
2315 }
2316
2317 #[cfg(any(test, feature = "test-utils"))]
2319 pub(crate) fn assert_invariants(&self) {
2320 assert_eq!(self.by_hash.len(), self.txs.len(), "by_hash.len() != txs.len()");
2321 assert!(self.auths.len() <= self.txs.len(), "auths.len() > txs.len()");
2322 }
2323}
2324
2325#[cfg(test)]
2326impl<T: PoolTransaction> AllTransactions<T> {
2327 pub(crate) fn tx_count(&self, sender: SenderId) -> usize {
2331 self.tx_counter.get(&sender).copied().unwrap_or_default()
2332 }
2333}
2334
2335impl<T: PoolTransaction> Default for AllTransactions<T> {
2336 fn default() -> Self {
2337 Self {
2338 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
2339 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
2340 block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
2341 by_hash: Default::default(),
2342 txs: Default::default(),
2343 sender_info: Default::default(),
2344 tx_counter: Default::default(),
2345 last_seen_block_number: Default::default(),
2346 last_seen_block_hash: Default::default(),
2347 pending_fees: Default::default(),
2348 last_full_update_fees: Default::default(),
2350 price_bumps: Default::default(),
2351 local_transactions_config: Default::default(),
2352 auths: Default::default(),
2353 tx_type_counts: Default::default(),
2354 update_buffer: Default::default(),
2355 metrics: Default::default(),
2356 }
2357 }
2358}
2359
2360#[derive(Debug, Clone, Copy, Default)]
2365pub(crate) struct TxTypeCounts {
2366 legacy: u64,
2367 eip2930: u64,
2368 eip1559: u64,
2369 eip4844: u64,
2370 eip7702: u64,
2371 other: u64,
2372}
2373
2374impl TxTypeCounts {
2375 const fn counter_mut(&mut self, tx_type: u8) -> &mut u64 {
2377 match tx_type {
2378 LEGACY_TX_TYPE_ID => &mut self.legacy,
2379 EIP2930_TX_TYPE_ID => &mut self.eip2930,
2380 EIP1559_TX_TYPE_ID => &mut self.eip1559,
2381 EIP4844_TX_TYPE_ID => &mut self.eip4844,
2382 EIP7702_TX_TYPE_ID => &mut self.eip7702,
2383 _ => &mut self.other,
2384 }
2385 }
2386
2387 const fn inc(&mut self, tx_type: u8) {
2389 *self.counter_mut(tx_type) += 1;
2390 }
2391
2392 const fn dec(&mut self, tx_type: u8) {
2394 *self.counter_mut(tx_type) -= 1;
2395 }
2396}
2397
2398#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2400pub(crate) struct PendingFees {
2401 pub(crate) base_fee: u64,
2403 pub(crate) blob_fee: u128,
2405}
2406
2407impl Default for PendingFees {
2408 fn default() -> Self {
2409 Self { base_fee: Default::default(), blob_fee: BLOB_TX_MIN_BLOB_GASPRICE }
2410 }
2411}
2412
2413pub(crate) type InsertResult<T> = Result<InsertOk<T>, InsertErr<T>>;
2415
2416#[derive(Debug)]
2418pub(crate) enum InsertErr<T: PoolTransaction> {
2419 Underpriced {
2421 transaction: Arc<ValidPoolTransaction<T>>,
2422 #[expect(dead_code)]
2423 existing: TxHash,
2424 },
2425 BlobTxHasNonceGap { transaction: Arc<ValidPoolTransaction<T>> },
2427 Overdraft { transaction: Arc<ValidPoolTransaction<T>> },
2430 FeeCapBelowMinimumProtocolFeeCap { transaction: Arc<ValidPoolTransaction<T>>, fee_cap: u128 },
2434 ExceededSenderTransactionsCapacity { transaction: Arc<ValidPoolTransaction<T>> },
2438 TxGasLimitMoreThanAvailableBlockGas {
2440 transaction: Arc<ValidPoolTransaction<T>>,
2441 block_gas_limit: u64,
2442 tx_gas_limit: u64,
2443 },
2444 TxTypeConflict { transaction: Arc<ValidPoolTransaction<T>> },
2446}
2447
2448#[derive(Debug)]
2450pub(crate) struct InsertOk<T: PoolTransaction> {
2451 transaction: Arc<ValidPoolTransaction<T>>,
2453 move_to: SubPool,
2455 state: TxState,
2457 replaced_tx: Option<(Arc<ValidPoolTransaction<T>>, SubPool)>,
2459 updates: Vec<PoolUpdate>,
2461}
2462
2463#[derive(Debug)]
2466pub(crate) struct PoolInternalTransaction<T: PoolTransaction> {
2467 pub(crate) transaction: Arc<ValidPoolTransaction<T>>,
2469 pub(crate) subpool: SubPool,
2471 pub(crate) state: TxState,
2474 pub(crate) cumulative_cost: U256,
2479}
2480
2481impl<T: PoolTransaction> PoolInternalTransaction<T> {
2484 fn next_cumulative_cost(&self) -> U256 {
2485 self.cumulative_cost + self.transaction.cost()
2486 }
2487}
2488
2489#[derive(Debug, Clone, Default)]
2491pub(crate) struct SenderInfo {
2492 pub(crate) state_nonce: u64,
2494 pub(crate) balance: U256,
2496}
2497
2498impl SenderInfo {
2501 const fn update(&mut self, state_nonce: u64, balance: U256) {
2503 *self = Self { state_nonce, balance };
2504 }
2505}
2506
2507#[cfg(test)]
2508mod tests {
2509 use super::*;
2510 use crate::{
2511 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
2512 traits::TransactionOrigin,
2513 SubPoolLimit,
2514 };
2515 use alloy_consensus::{Transaction, TxType};
2516 use alloy_primitives::address;
2517
2518 #[test]
2519 fn test_insert_blob() {
2520 let on_chain_balance = U256::MAX;
2521 let on_chain_nonce = 0;
2522 let mut f = MockTransactionFactory::default();
2523 let mut pool = AllTransactions::default();
2524 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2525 let valid_tx = f.validated(tx);
2526 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2527 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2528 assert!(updates.is_empty());
2529 assert!(replaced_tx.is_none());
2530 assert!(state.contains(TxState::NO_NONCE_GAPS));
2531 assert!(state.contains(TxState::ENOUGH_BALANCE));
2532 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2533 assert_eq!(move_to, SubPool::Pending);
2534
2535 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2536 assert_eq!(inserted.subpool, SubPool::Pending);
2537 }
2538
2539 #[test]
2540 fn test_insert_blob_not_enough_blob_fee() {
2541 let on_chain_balance = U256::MAX;
2542 let on_chain_nonce = 0;
2543 let mut f = MockTransactionFactory::default();
2544 let mut pool = AllTransactions {
2545 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2546 ..Default::default()
2547 };
2548 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2549 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2550 let valid_tx = f.validated(tx);
2551 let InsertOk { state, .. } =
2552 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2553 assert!(state.contains(TxState::NO_NONCE_GAPS));
2554 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2555
2556 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2557 }
2558
2559 #[test]
2560 fn test_valid_tx_with_decreasing_blob_fee() {
2561 let on_chain_balance = U256::MAX;
2562 let on_chain_nonce = 0;
2563 let mut f = MockTransactionFactory::default();
2564 let mut pool = AllTransactions {
2565 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2566 ..Default::default()
2567 };
2568 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2569
2570 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2571 let valid_tx = f.validated(tx.clone());
2572 let InsertOk { state, .. } =
2573 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2574 assert!(state.contains(TxState::NO_NONCE_GAPS));
2575 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2576
2577 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2578 pool.remove_transaction(&valid_tx.transaction_id);
2579
2580 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
2581 let InsertOk { state, .. } =
2582 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2583 assert!(state.contains(TxState::NO_NONCE_GAPS));
2584 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2585 }
2586
2587 #[test]
2588 fn test_demote_valid_tx_with_increasing_blob_fee() {
2589 let on_chain_balance = U256::MAX;
2590 let on_chain_nonce = 0;
2591 let mut f = MockTransactionFactory::default();
2592 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2593 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2594
2595 let mut block_info = pool.block_info();
2597 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2598 pool.set_block_info(block_info);
2599
2600 let validated = f.validated(tx.clone());
2601 let id = *validated.id();
2602 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2603
2604 assert!(pool.blob_pool.is_empty());
2606 assert_eq!(pool.pending_pool.len(), 1);
2607
2608 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2610 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2611 assert_eq!(internal_tx.subpool, SubPool::Pending);
2612
2613 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2615 pool.set_block_info(block_info);
2616
2617 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2619 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2620 assert_eq!(internal_tx.subpool, SubPool::Blob);
2621
2622 assert_eq!(pool.blob_pool.len(), 1);
2624 assert!(pool.pending_pool.is_empty());
2625 }
2626
2627 #[test]
2628 fn test_promote_valid_tx_with_decreasing_blob_fee() {
2629 let on_chain_balance = U256::MAX;
2630 let on_chain_nonce = 0;
2631 let mut f = MockTransactionFactory::default();
2632 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2633 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2634
2635 let mut block_info = pool.block_info();
2637 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2638 pool.set_block_info(block_info);
2639
2640 let validated = f.validated(tx.clone());
2641 let id = *validated.id();
2642 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2643
2644 assert!(pool.pending_pool.is_empty());
2646 assert_eq!(pool.blob_pool.len(), 1);
2647
2648 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2650 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2651 assert_eq!(internal_tx.subpool, SubPool::Blob);
2652
2653 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2655 pool.set_block_info(block_info);
2656
2657 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2659 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2660 assert_eq!(internal_tx.subpool, SubPool::Pending);
2661
2662 assert_eq!(pool.pending_pool.len(), 1);
2664 assert!(pool.blob_pool.is_empty());
2665 }
2666
2667 #[test]
2668 fn test_queued_count_includes_blob_pool() {
2669 let on_chain_balance = U256::MAX;
2670 let on_chain_nonce = 0;
2671 let mut f = MockTransactionFactory::default();
2672 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2673 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2674
2675 let mut block_info = pool.block_info();
2677 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2678 pool.set_block_info(block_info);
2679
2680 let validated = f.validated(tx);
2681 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2682
2683 assert_eq!(pool.blob_pool.len(), 1);
2684 assert!(pool.pending_pool.is_empty());
2685
2686 assert_eq!(pool.queued_transactions_count(), 1);
2688 }
2689
2690 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
2692 struct PromotionTest {
2693 basefee: u64,
2695 blobfee: u128,
2697 subpool: SubPool,
2699 basefee_update: u64,
2701 blobfee_update: u128,
2703 new_subpool: SubPool,
2705 }
2706
2707 impl PromotionTest {
2708 const fn opposite(&self) -> Self {
2710 Self {
2711 basefee: self.basefee_update,
2712 blobfee: self.blobfee_update,
2713 subpool: self.new_subpool,
2714 blobfee_update: self.blobfee,
2715 basefee_update: self.basefee,
2716 new_subpool: self.subpool,
2717 }
2718 }
2719
2720 fn assert_subpool_lengths<T: TransactionOrdering>(
2721 &self,
2722 pool: &TxPool<T>,
2723 failure_message: String,
2724 check_subpool: SubPool,
2725 ) {
2726 match check_subpool {
2727 SubPool::Blob => {
2728 assert_eq!(pool.blob_pool.len(), 1, "{failure_message}");
2729 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2730 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2731 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2732 }
2733 SubPool::Pending => {
2734 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2735 assert_eq!(pool.pending_pool.len(), 1, "{failure_message}");
2736 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2737 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2738 }
2739 SubPool::BaseFee => {
2740 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2741 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2742 assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}");
2743 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2744 }
2745 SubPool::Queued => {
2746 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2747 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2748 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2749 assert_eq!(pool.queued_pool.len(), 1, "{failure_message}");
2750 }
2751 }
2752 }
2753
2754 fn assert_single_tx_starting_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2758 self.assert_subpool_lengths(
2759 pool,
2760 format!("pool length check failed at start of test: {self:?}"),
2761 self.subpool,
2762 );
2763 }
2764
2765 fn assert_single_tx_ending_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2769 self.assert_subpool_lengths(
2770 pool,
2771 format!("pool length check failed at end of test: {self:?}"),
2772 self.new_subpool,
2773 );
2774 }
2775 }
2776
2777 #[test]
2778 fn test_promote_blob_tx_with_both_pending_fee_updates() {
2779 let on_chain_balance = U256::MAX;
2782 let on_chain_nonce = 0;
2783 let mut f = MockTransactionFactory::default();
2784 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2785
2786 let max_fee_per_blob_gas = tx.max_fee_per_blob_gas().unwrap();
2787 let max_fee_per_gas = tx.max_fee_per_gas() as u64;
2788
2789 let mut expected_promotions = vec![
2791 PromotionTest {
2792 blobfee: max_fee_per_blob_gas + 1,
2793 basefee: max_fee_per_gas + 1,
2794 subpool: SubPool::Blob,
2795 blobfee_update: max_fee_per_blob_gas + 1,
2796 basefee_update: max_fee_per_gas + 1,
2797 new_subpool: SubPool::Blob,
2798 },
2799 PromotionTest {
2800 blobfee: max_fee_per_blob_gas + 1,
2801 basefee: max_fee_per_gas + 1,
2802 subpool: SubPool::Blob,
2803 blobfee_update: max_fee_per_blob_gas,
2804 basefee_update: max_fee_per_gas + 1,
2805 new_subpool: SubPool::Blob,
2806 },
2807 PromotionTest {
2808 blobfee: max_fee_per_blob_gas + 1,
2809 basefee: max_fee_per_gas + 1,
2810 subpool: SubPool::Blob,
2811 blobfee_update: max_fee_per_blob_gas + 1,
2812 basefee_update: max_fee_per_gas,
2813 new_subpool: SubPool::Blob,
2814 },
2815 PromotionTest {
2816 blobfee: max_fee_per_blob_gas + 1,
2817 basefee: max_fee_per_gas + 1,
2818 subpool: SubPool::Blob,
2819 blobfee_update: max_fee_per_blob_gas,
2820 basefee_update: max_fee_per_gas,
2821 new_subpool: SubPool::Pending,
2822 },
2823 PromotionTest {
2824 blobfee: max_fee_per_blob_gas,
2825 basefee: max_fee_per_gas + 1,
2826 subpool: SubPool::Blob,
2827 blobfee_update: max_fee_per_blob_gas,
2828 basefee_update: max_fee_per_gas,
2829 new_subpool: SubPool::Pending,
2830 },
2831 PromotionTest {
2832 blobfee: max_fee_per_blob_gas + 1,
2833 basefee: max_fee_per_gas,
2834 subpool: SubPool::Blob,
2835 blobfee_update: max_fee_per_blob_gas,
2836 basefee_update: max_fee_per_gas,
2837 new_subpool: SubPool::Pending,
2838 },
2839 PromotionTest {
2840 blobfee: max_fee_per_blob_gas,
2841 basefee: max_fee_per_gas,
2842 subpool: SubPool::Pending,
2843 blobfee_update: max_fee_per_blob_gas,
2844 basefee_update: max_fee_per_gas,
2845 new_subpool: SubPool::Pending,
2846 },
2847 ];
2848
2849 let reversed = expected_promotions.iter().map(|test| test.opposite()).collect::<Vec<_>>();
2851 expected_promotions.extend(reversed);
2852
2853 let expected_promotions = expected_promotions.into_iter().collect::<HashSet<_>>();
2855
2856 for promotion_test in &expected_promotions {
2857 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2858
2859 let mut block_info = pool.block_info();
2861
2862 block_info.pending_blob_fee = Some(promotion_test.blobfee);
2863 block_info.pending_basefee = promotion_test.basefee;
2864 pool.set_block_info(block_info);
2865
2866 let validated = f.validated(tx.clone());
2867 let id = *validated.id();
2868 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2869
2870 promotion_test.assert_single_tx_starting_subpool(&pool);
2872
2873 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2875 assert_eq!(
2876 internal_tx.subpool, promotion_test.subpool,
2877 "Subpools do not match at start of test: {promotion_test:?}"
2878 );
2879
2880 block_info.pending_basefee = promotion_test.basefee_update;
2882 block_info.pending_blob_fee = Some(promotion_test.blobfee_update);
2883 pool.set_block_info(block_info);
2884
2885 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2887 assert_eq!(
2888 internal_tx.subpool, promotion_test.new_subpool,
2889 "Subpools do not match at end of test: {promotion_test:?}"
2890 );
2891
2892 promotion_test.assert_single_tx_ending_subpool(&pool);
2894 }
2895 }
2896
2897 #[test]
2898 fn test_insert_pending() {
2899 let on_chain_balance = U256::MAX;
2900 let on_chain_nonce = 0;
2901 let mut f = MockTransactionFactory::default();
2902 let mut pool = AllTransactions::default();
2903 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2904 let valid_tx = f.validated(tx);
2905 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2906 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2907 assert!(updates.is_empty());
2908 assert!(replaced_tx.is_none());
2909 assert!(state.contains(TxState::NO_NONCE_GAPS));
2910 assert!(state.contains(TxState::ENOUGH_BALANCE));
2911 assert_eq!(move_to, SubPool::Pending);
2912
2913 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2914 assert_eq!(inserted.subpool, SubPool::Pending);
2915 }
2916
2917 #[test]
2918 fn test_simple_insert() {
2919 let on_chain_balance = U256::ZERO;
2920 let on_chain_nonce = 0;
2921 let mut f = MockTransactionFactory::default();
2922 let mut pool = AllTransactions::default();
2923 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2924 tx.set_priority_fee(100);
2925 tx.set_max_fee(100);
2926 let valid_tx = f.validated(tx.clone());
2927 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2928 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2929 assert!(updates.is_empty());
2930 assert!(replaced_tx.is_none());
2931 assert!(state.contains(TxState::NO_NONCE_GAPS));
2932 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2933 assert_eq!(move_to, SubPool::Queued);
2934
2935 assert_eq!(pool.len(), 1);
2936 assert!(pool.contains(valid_tx.hash()));
2937 let expected_state = TxState::ENOUGH_FEE_CAP_BLOCK | TxState::NO_NONCE_GAPS;
2938 let inserted = pool.get(valid_tx.id()).unwrap();
2939 assert!(inserted.state.intersects(expected_state));
2940
2941 let res = pool.insert_tx(valid_tx, on_chain_balance, on_chain_nonce);
2943 res.unwrap_err();
2944 assert_eq!(pool.len(), 1);
2945
2946 let valid_tx = f.validated(tx.next());
2947 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2948 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2949
2950 assert!(updates.is_empty());
2951 assert!(replaced_tx.is_none());
2952 assert!(state.contains(TxState::NO_NONCE_GAPS));
2953 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2954 assert_eq!(move_to, SubPool::Queued);
2955
2956 assert!(pool.contains(valid_tx.hash()));
2957 assert_eq!(pool.len(), 2);
2958 let inserted = pool.get(valid_tx.id()).unwrap();
2959 assert!(inserted.state.intersects(expected_state));
2960 }
2961
2962 #[test]
2963 fn test_on_canonical_state_change_no_double_processing() {
2966 let mut tx_factory = MockTransactionFactory::default();
2967 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2968
2969 let tx = MockTransaction::eip1559().with_gas_price(50).with_gas_limit(30_000);
2971 let sender = tx.sender();
2972
2973 let mut block_info = pool.block_info();
2975 block_info.pending_basefee = 100;
2976 pool.set_block_info(block_info);
2977
2978 let validated = tx_factory.validated(tx);
2979 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2980
2981 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2983
2984 assert_eq!(pool.basefee_pool.len(), 1);
2985 assert_eq!(pool.pending_pool.len(), 0);
2986
2987 block_info.pending_basefee = 40;
2991
2992 let mut changed_senders = FxHashMap::default();
2993 changed_senders.insert(
2994 sender_id,
2995 SenderInfo {
2996 state_nonce: 0,
2997 balance: U256::from(20_000_000), },
2999 );
3000
3001 let outcome = pool.on_canonical_state_change(
3002 block_info,
3003 vec![], changed_senders,
3005 PoolUpdateKind::Commit,
3006 );
3007
3008 assert_eq!(pool.pending_pool.len(), 1, "Transaction should be in pending pool");
3010 assert_eq!(pool.basefee_pool.len(), 0, "Transaction should not be in basefee pool");
3011 assert_eq!(outcome.promoted.len(), 1, "Should report exactly one promotion");
3012 }
3013
3014 #[test]
3015 fn test_canonical_state_change_with_basefee_update_regression() {
3018 let mut tx_factory = MockTransactionFactory::default();
3019 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3020
3021 let sender_balance = U256::from(100_000_000);
3023
3024 let tx1 =
3026 MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000).with_nonce(0);
3027 let sender1 = tx1.sender();
3028
3029 let tx2 =
3031 MockTransaction::eip1559().with_gas_price(55).with_gas_limit(21_000).with_nonce(0);
3032 let sender2 = tx2.sender();
3033
3034 let tx3 =
3036 MockTransaction::eip1559().with_gas_price(45).with_gas_limit(21_000).with_nonce(0);
3037 let sender3 = tx3.sender();
3038
3039 let mut block_info = pool.block_info();
3041 block_info.pending_basefee = 70;
3042 pool.set_block_info(block_info);
3043
3044 let validated1 = tx_factory.validated(tx1);
3046 let validated2 = tx_factory.validated(tx2);
3047 let validated3 = tx_factory.validated(tx3);
3048
3049 pool.add_transaction(validated1, sender_balance, 0, None).unwrap();
3050 pool.add_transaction(validated2, sender_balance, 0, None).unwrap();
3051 pool.add_transaction(validated3, sender_balance, 0, None).unwrap();
3052
3053 let sender1_id = tx_factory.ids.sender_id(&sender1).unwrap();
3054 let sender2_id = tx_factory.ids.sender_id(&sender2).unwrap();
3055 let sender3_id = tx_factory.ids.sender_id(&sender3).unwrap();
3056
3057 assert_eq!(pool.basefee_pool.len(), 3, "All txs should be in basefee pool");
3059 assert_eq!(pool.pending_pool.len(), 0, "No txs should be in pending pool");
3060
3061 block_info.pending_basefee = 50;
3064
3065 let mut changed_senders = FxHashMap::default();
3067 changed_senders.insert(
3068 sender1_id,
3069 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
3070 );
3071 changed_senders.insert(
3072 sender2_id,
3073 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
3074 );
3075 changed_senders.insert(
3076 sender3_id,
3077 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
3078 );
3079
3080 let outcome = pool.on_canonical_state_change(
3081 block_info,
3082 vec![],
3083 changed_senders,
3084 PoolUpdateKind::Commit,
3085 );
3086
3087 assert_eq!(pool.pending_pool.len(), 2, "tx1 and tx2 should be promoted");
3089 assert_eq!(pool.basefee_pool.len(), 1, "tx3 should remain in basefee");
3090
3091 assert_eq!(
3094 outcome.promoted.len(),
3095 2,
3096 "Should report exactly 2 promotions, not double-counted"
3097 );
3098
3099 let promoted_prices: Vec<u128> =
3101 outcome.promoted.iter().map(|tx| tx.max_fee_per_gas()).collect();
3102 assert!(promoted_prices.contains(&60));
3103 assert!(promoted_prices.contains(&55));
3104 }
3105
3106 #[test]
3107 fn test_basefee_decrease_with_empty_senders() {
3108 let mut tx_factory = MockTransactionFactory::default();
3111 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3112
3113 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
3115
3116 let mut block_info = pool.block_info();
3118 block_info.pending_basefee = 100;
3119 pool.set_block_info(block_info);
3120
3121 let validated = tx_factory.validated(tx);
3123 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
3124
3125 assert_eq!(pool.basefee_pool.len(), 1);
3126 assert_eq!(pool.pending_pool.len(), 0);
3127
3128 block_info.pending_basefee = 50;
3130 let outcome = pool.on_canonical_state_change(
3131 block_info,
3132 vec![],
3133 FxHashMap::default(), PoolUpdateKind::Commit,
3135 );
3136
3137 assert_eq!(pool.pending_pool.len(), 1, "Fee decrease should promote tx");
3139 assert_eq!(pool.basefee_pool.len(), 0);
3140 assert_eq!(outcome.promoted.len(), 1, "Should report promotion from fee update");
3141 }
3142
3143 #[test]
3144 fn test_basefee_decrease_account_makes_unfundable() {
3145 let mut tx_factory = MockTransactionFactory::default();
3148 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3149
3150 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
3151 let sender = tx.sender();
3152
3153 let mut block_info = pool.block_info();
3155 block_info.pending_basefee = 100;
3156 pool.set_block_info(block_info);
3157
3158 let validated = tx_factory.validated(tx);
3159 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
3160 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
3161
3162 assert_eq!(pool.basefee_pool.len(), 1);
3163
3164 block_info.pending_basefee = 50;
3166 let mut changed_senders = FxHashMap::default();
3167 changed_senders.insert(
3168 sender_id,
3169 SenderInfo {
3170 state_nonce: 0,
3171 balance: U256::from(100), },
3173 );
3174
3175 let outcome = pool.on_canonical_state_change(
3176 block_info,
3177 vec![],
3178 changed_senders,
3179 PoolUpdateKind::Commit,
3180 );
3181
3182 assert_eq!(pool.pending_pool.len(), 0, "Unfunded tx should not be in pending");
3184 assert_eq!(pool.basefee_pool.len(), 0, "Tx no longer in basefee pool");
3185 assert_eq!(pool.queued_pool.len(), 1, "Unfunded tx should be in queued pool");
3186
3187 let tx_count = pool.all_transactions.txs.len();
3189 assert_eq!(tx_count, 1, "Transaction should still be in pool (in queued)");
3190
3191 assert_eq!(outcome.promoted.len(), 0, "Should not report promotion");
3192 assert_eq!(outcome.discarded.len(), 0, "Queued tx is not reported as discarded");
3193 }
3194
3195 #[test]
3196 fn insert_already_imported() {
3197 let on_chain_balance = U256::ZERO;
3198 let on_chain_nonce = 0;
3199 let mut f = MockTransactionFactory::default();
3200 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3201 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3202 let tx = f.validated(tx);
3203 pool.add_transaction(tx.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3204 match pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap_err().kind {
3205 PoolErrorKind::AlreadyImported => {}
3206 _ => unreachable!(),
3207 }
3208 }
3209
3210 #[test]
3211 fn insert_replace() {
3212 let on_chain_balance = U256::ZERO;
3213 let on_chain_nonce = 0;
3214 let mut f = MockTransactionFactory::default();
3215 let mut pool = AllTransactions::default();
3216 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3217 let first = f.validated(tx.clone());
3218 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3219 let replacement = f.validated(tx.rng_hash().inc_price());
3220 let InsertOk { updates, replaced_tx, .. } =
3221 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
3222 assert!(updates.is_empty());
3223 let replaced = replaced_tx.unwrap();
3224 assert_eq!(replaced.0.hash(), first.hash());
3225
3226 assert!(!pool.contains(first.hash()));
3228 assert!(pool.contains(replacement.hash()));
3229 assert_eq!(pool.len(), 1);
3230 }
3231
3232 #[test]
3233 fn insert_replace_txpool() {
3234 let on_chain_balance = U256::ZERO;
3235 let on_chain_nonce = 0;
3236 let mut f = MockTransactionFactory::default();
3237 let mut pool = TxPool::mock();
3238
3239 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3240 let first = f.validated(tx.clone());
3241 let first_added =
3242 pool.add_transaction(first, on_chain_balance, on_chain_nonce, None).unwrap();
3243 let replacement = f.validated(tx.rng_hash().inc_price());
3244 let replacement_added = pool
3245 .add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce, None)
3246 .unwrap();
3247
3248 assert!(!pool.contains(first_added.hash()));
3250 assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));
3252
3253 assert!(pool.contains(replacement.hash()));
3254 let size = pool.size();
3255 assert_eq!(size.total, 1);
3256 size.assert_invariants();
3257 }
3258
3259 #[test]
3260 fn insert_replace_underpriced() {
3261 let on_chain_balance = U256::ZERO;
3262 let on_chain_nonce = 0;
3263 let mut f = MockTransactionFactory::default();
3264 let mut pool = AllTransactions::default();
3265 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3266 let first = f.validated(tx.clone());
3267 let _res = pool.insert_tx(first, on_chain_balance, on_chain_nonce);
3268 let mut replacement = f.validated(tx.rng_hash());
3269 replacement.transaction = replacement.transaction.decr_price();
3270 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3271 assert!(matches!(err, InsertErr::Underpriced { .. }));
3272 }
3273
3274 #[test]
3275 fn insert_replace_underpriced_not_enough_bump() {
3276 let on_chain_balance = U256::ZERO;
3277 let on_chain_nonce = 0;
3278 let mut f = MockTransactionFactory::default();
3279 let mut pool = AllTransactions::default();
3280 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3281 tx.set_priority_fee(100);
3282 tx.set_max_fee(100);
3283 let first = f.validated(tx.clone());
3284 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3285 let mut replacement = f.validated(tx.rng_hash().inc_price());
3286
3287 replacement.transaction.set_priority_fee(109);
3289 replacement.transaction.set_max_fee(109);
3290 let err =
3291 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3292 assert!(matches!(err, InsertErr::Underpriced { .. }));
3293 assert!(pool.contains(first.hash()));
3295 assert_eq!(pool.len(), 1);
3296
3297 replacement.transaction.set_priority_fee(110);
3299 replacement.transaction.set_max_fee(109);
3300 let err =
3301 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3302 assert!(matches!(err, InsertErr::Underpriced { .. }));
3303 assert!(pool.contains(first.hash()));
3304 assert_eq!(pool.len(), 1);
3305
3306 replacement.transaction.set_priority_fee(109);
3308 replacement.transaction.set_max_fee(110);
3309 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3310 assert!(matches!(err, InsertErr::Underpriced { .. }));
3311 assert!(pool.contains(first.hash()));
3312 assert_eq!(pool.len(), 1);
3313 }
3314
3315 #[test]
3316 fn insert_replace_underpriced_rounds_up_minimum_bump() {
3317 let on_chain_balance = U256::ZERO;
3318 let on_chain_nonce = 0;
3319 let mut f = MockTransactionFactory::default();
3320 let mut pool = AllTransactions { minimal_protocol_basefee: 0, ..Default::default() };
3321 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3322 tx.set_priority_fee(1);
3323 tx.set_max_fee(1);
3324
3325 let first = f.validated(tx.clone());
3326 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3327
3328 let mut replacement = f.validated(tx.rng_hash().inc_price());
3329 replacement.transaction.set_priority_fee(1);
3330 replacement.transaction.set_max_fee(2);
3331 let err =
3332 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3333 assert!(matches!(err, InsertErr::Underpriced { .. }));
3334 assert!(pool.contains(first.hash()));
3335 assert_eq!(pool.len(), 1);
3336
3337 replacement.transaction.set_priority_fee(2);
3338 replacement.transaction.set_max_fee(2);
3339 let replaced = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap();
3340 assert!(replaced.replaced_tx.is_some());
3341 assert_eq!(pool.len(), 1);
3342 }
3343
3344 #[test]
3345 fn insert_conflicting_type_normal_to_blob() {
3346 let on_chain_balance = U256::from(10_000);
3347 let on_chain_nonce = 0;
3348 let mut f = MockTransactionFactory::default();
3349 let mut pool = AllTransactions::default();
3350 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3351 let first = f.validated(tx.clone());
3352 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3353 let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3354 let blob = f.validated(tx);
3355 let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
3356 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3357 }
3358
3359 #[test]
3360 fn insert_conflicting_type_blob_to_normal() {
3361 let on_chain_balance = U256::from(10_000);
3362 let on_chain_nonce = 0;
3363 let mut f = MockTransactionFactory::default();
3364 let mut pool = AllTransactions::default();
3365 let tx = MockTransaction::eip4844().inc_price().inc_limit();
3366 let first = f.validated(tx.clone());
3367 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3368 let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3369 let tx = f.validated(tx);
3370 let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
3371 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3372 }
3373
3374 #[test]
3376 fn insert_previous() {
3377 let on_chain_balance = U256::ZERO;
3378 let on_chain_nonce = 0;
3379 let mut f = MockTransactionFactory::default();
3380 let mut pool = AllTransactions::default();
3381 let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
3382 let first = f.validated(tx.clone());
3383 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3384
3385 let first_in_pool = pool.get(first.id()).unwrap();
3386
3387 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3389
3390 let prev = f.validated(tx.prev());
3391 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3392 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3393
3394 assert!(updates.is_empty());
3396 assert!(replaced_tx.is_none());
3397 assert!(state.contains(TxState::NO_NONCE_GAPS));
3398 assert_eq!(move_to, SubPool::Queued);
3399
3400 let first_in_pool = pool.get(first.id()).unwrap();
3401 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3403 }
3404
3405 #[test]
3407 fn insert_with_updates() {
3408 let on_chain_balance = U256::from(10_000);
3409 let on_chain_nonce = 0;
3410 let mut f = MockTransactionFactory::default();
3411 let mut pool = AllTransactions::default();
3412 let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
3413 let first = f.validated(tx.clone());
3414 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3415
3416 let first_in_pool = pool.get(first.id()).unwrap();
3417 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3419 assert_eq!(SubPool::Queued, first_in_pool.subpool);
3420
3421 let prev = f.validated(tx.prev());
3422 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3423 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3424
3425 assert_eq!(updates.len(), 1);
3427 assert!(replaced_tx.is_none());
3428 assert!(state.contains(TxState::NO_NONCE_GAPS));
3429 assert_eq!(move_to, SubPool::Pending);
3430
3431 let first_in_pool = pool.get(first.id()).unwrap();
3432 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3434 assert_eq!(SubPool::Pending, first_in_pool.subpool);
3435 }
3436
3437 #[test]
3438 fn insert_previous_blocking() {
3439 let on_chain_balance = U256::from(1_000);
3440 let on_chain_nonce = 0;
3441 let mut f = MockTransactionFactory::default();
3442 let mut pool = AllTransactions::default();
3443 pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
3444 let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
3445 let first = f.validated(tx.clone());
3446
3447 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3448
3449 let first_in_pool = pool.get(first.id()).unwrap();
3450
3451 assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
3452 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3454
3455 let prev = f.validated(tx.prev());
3456 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3457 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3458
3459 assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3460 assert!(updates.is_empty());
3462 assert!(replaced_tx.is_none());
3463 assert!(state.contains(TxState::NO_NONCE_GAPS));
3464 assert_eq!(move_to, SubPool::BaseFee);
3465
3466 let first_in_pool = pool.get(first.id()).unwrap();
3467 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3469 }
3470
3471 #[test]
3472 fn rejects_spammer() {
3473 let on_chain_balance = U256::from(1_000);
3474 let on_chain_nonce = 0;
3475 let mut f = MockTransactionFactory::default();
3476 let mut pool = AllTransactions::default();
3477
3478 let mut tx = MockTransaction::eip1559();
3479 let unblocked_tx = tx.clone();
3480 for _ in 0..pool.max_account_slots {
3481 tx = tx.next();
3482 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
3483 }
3484
3485 assert_eq!(
3486 pool.max_account_slots,
3487 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3488 );
3489
3490 let err =
3491 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
3492 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
3493
3494 assert!(pool
3495 .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
3496 .is_ok());
3497 }
3498
3499 #[test]
3500 fn allow_local_spamming() {
3501 let on_chain_balance = U256::from(1_000);
3502 let on_chain_nonce = 0;
3503 let mut f = MockTransactionFactory::default();
3504 let mut pool = AllTransactions::default();
3505
3506 let mut tx = MockTransaction::eip1559();
3507 for _ in 0..pool.max_account_slots {
3508 tx = tx.next();
3509 pool.insert_tx(
3510 f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
3511 on_chain_balance,
3512 on_chain_nonce,
3513 )
3514 .unwrap();
3515 }
3516
3517 assert_eq!(
3518 pool.max_account_slots,
3519 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3520 );
3521
3522 pool.insert_tx(
3523 f.validated_with_origin(TransactionOrigin::Local, tx.next()),
3524 on_chain_balance,
3525 on_chain_nonce,
3526 )
3527 .unwrap();
3528 }
3529
3530 #[test]
3531 fn reject_tx_over_gas_limit() {
3532 let on_chain_balance = U256::from(1_000);
3533 let on_chain_nonce = 0;
3534 let mut f = MockTransactionFactory::default();
3535 let mut pool = AllTransactions::default();
3536
3537 let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
3538
3539 assert!(matches!(
3540 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
3541 Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
3542 ));
3543 }
3544
3545 #[test]
3546 fn test_tx_equal_gas_limit() {
3547 let on_chain_balance = U256::from(1_000);
3548 let on_chain_nonce = 0;
3549 let mut f = MockTransactionFactory::default();
3550 let mut pool = AllTransactions::default();
3551
3552 let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
3553
3554 let InsertOk { state, .. } =
3555 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
3556 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
3557 }
3558
3559 #[test]
3560 fn full_update_sender_heuristic() {
3561 let mut pool = AllTransactions::<MockTransaction>::default();
3562 for sender in 0..(FULL_UPDATE_MIN_SENDERS - 1) as u64 {
3563 pool.tx_counter.insert(sender.into(), 1);
3564 }
3565
3566 assert!(!pool.should_update_all_senders(250));
3568 assert!(pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS - 1));
3569
3570 pool.tx_counter.insert((FULL_UPDATE_MIN_SENDERS as u64 - 1).into(), 1);
3571
3572 assert!(
3574 !pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS / FULL_UPDATE_SENDER_RATIO - 1)
3575 );
3576 assert!(pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS / FULL_UPDATE_SENDER_RATIO));
3577 assert!(pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS + 1));
3578 }
3579
3580 #[test]
3581 fn changed_sender_update_matches_full_update() {
3582 let senders = [
3583 Address::with_last_byte(0x0a),
3584 Address::with_last_byte(0x0b),
3585 Address::with_last_byte(0x0c),
3586 ];
3587 let starting_nonces = [5, 11, 17];
3588
3589 let build_pool = || {
3590 let mut f = MockTransactionFactory::default();
3591 let mut pool = AllTransactions::default();
3592 pool.pending_fees.base_fee = 1;
3593
3594 for (sender, starting_nonce) in senders.into_iter().zip(starting_nonces) {
3595 for nonce in starting_nonce..starting_nonce + 3 {
3596 let tx = MockTransaction::eip1559()
3597 .with_sender(sender)
3598 .with_nonce(nonce)
3599 .inc_price_by(10)
3600 .rng_hash();
3601 pool.insert_tx(f.validated(tx), U256::from(1_000_000), starting_nonce).unwrap();
3602 }
3603 }
3604
3605 pool.update(&Default::default());
3607 let sender_ids = senders.map(|sender| f.ids.sender_id(&sender).unwrap());
3608 (pool, sender_ids)
3609 };
3610
3611 let (mut changed_senders_only, sender_ids) = build_pool();
3612 let (mut full_update, full_update_sender_ids) = build_pool();
3613 assert_eq!(sender_ids, full_update_sender_ids);
3614
3615 let mut changed = FxHashMap::default();
3616 changed.insert(
3617 sender_ids[0],
3618 SenderInfo { state_nonce: starting_nonces[0] + 1, balance: U256::from(1_000_000) },
3619 );
3620 changed.insert(
3621 sender_ids[1],
3622 SenderInfo { state_nonce: starting_nonces[1], balance: U256::ZERO },
3623 );
3624
3625 let mut changed_sender_updates = changed_senders_only.update(&changed);
3626 full_update.last_full_update_fees.base_fee =
3628 full_update.last_full_update_fees.base_fee.saturating_add(1);
3629 let mut full_updates = full_update.update(&changed);
3630
3631 let update_key = |update: &PoolUpdate| {
3632 let destination = match &update.destination {
3633 Destination::Discard => None,
3634 Destination::Pool(pool) => Some(*pool),
3635 };
3636 (update.id, update.current, destination)
3637 };
3638 changed_sender_updates.sort_unstable_by_key(|update| update.id);
3639 full_updates.sort_unstable_by_key(|update| update.id);
3640 assert_eq!(
3641 changed_sender_updates.iter().map(update_key).collect::<Vec<_>>(),
3642 full_updates.iter().map(update_key).collect::<Vec<_>>()
3643 );
3644
3645 let metadata = |pool: &AllTransactions<MockTransaction>| {
3646 pool.txs
3647 .iter()
3648 .map(|(id, tx)| (*id, tx.state, tx.subpool, tx.cumulative_cost))
3649 .collect::<Vec<_>>()
3650 };
3651 assert_eq!(metadata(&changed_senders_only), metadata(&full_update));
3652 }
3653
3654 #[test]
3655 fn update_visits_every_sender_when_the_base_fee_moved() {
3656 let mut f = MockTransactionFactory::default();
3657 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3658
3659 let tx = MockTransaction::eip1559().inc_price_by(10);
3660 let validated = f.validated(tx.clone());
3661 let id = *validated.id();
3662 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3663
3664 pool.all_transactions.update(&Default::default());
3665 assert!(pool
3666 .all_transactions
3667 .txs
3668 .get(&id)
3669 .unwrap()
3670 .state
3671 .contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3672
3673 pool.all_transactions.pending_fees.base_fee = (tx.max_fee_per_gas() + 1) as u64;
3676 pool.all_transactions.update(&Default::default());
3677
3678 assert!(
3679 !pool
3680 .all_transactions
3681 .txs
3682 .get(&id)
3683 .unwrap()
3684 .state
3685 .contains(TxState::ENOUGH_FEE_CAP_BLOCK),
3686 "fee change was not applied to an unchanged sender"
3687 );
3688 }
3689
3690 #[test]
3691 fn blob_fee_change_records_full_update() {
3692 let mut pool = AllTransactions::<MockTransaction>::default();
3693 pool.pending_fees.blob_fee += 1;
3694
3695 pool.update(&Default::default());
3696
3697 assert_eq!(pool.last_full_update_fees, pool.pending_fees);
3698 }
3699
3700 #[test]
3701 fn gap_fill_rechecks_descendant_fee_eligibility() {
3702 let mut f = MockTransactionFactory::default();
3703 let mut pool = AllTransactions::default();
3704 let sender = Address::with_last_byte(0x0d);
3705 let balance = U256::MAX;
3706
3707 pool.pending_fees.base_fee = 100;
3708 let descendant = MockTransaction::eip1559()
3709 .with_sender(sender)
3710 .with_nonce(1)
3711 .with_gas_limit(21_000)
3712 .with_max_fee(150)
3713 .with_priority_fee(1)
3714 .rng_hash();
3715 let descendant = f.validated(descendant);
3716 let descendant_id = *descendant.id();
3717 pool.insert_tx(descendant, balance, 0).unwrap();
3718 pool.update(&Default::default());
3719
3720 pool.pending_fees.base_fee = 200;
3723 pool.update(&Default::default());
3724 assert!(pool.get(&descendant_id).unwrap().state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3725
3726 let predecessor = MockTransaction::eip1559()
3727 .with_sender(sender)
3728 .with_nonce(0)
3729 .with_gas_limit(21_000)
3730 .with_max_fee(250)
3731 .with_priority_fee(1)
3732 .rng_hash();
3733 let InsertOk { move_to, .. } =
3734 pool.insert_tx(f.validated(predecessor), balance, 0).unwrap();
3735
3736 assert_eq!(move_to, SubPool::Pending);
3737 let descendant = pool.get(&descendant_id).unwrap();
3738 assert_eq!(descendant.subpool, SubPool::BaseFee);
3739 assert!(!descendant.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3740 assert!(descendant.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3741 }
3742
3743 #[test]
3744 fn base_fee_update_unparks_all_descendants() {
3745 let mut f = MockTransactionFactory::default();
3746 let mut pool = AllTransactions::default();
3747 let sender = Address::with_last_byte(0x0e);
3748 let mut ids = Vec::new();
3749
3750 pool.pending_fees.base_fee = 200;
3751 for nonce in 0..3 {
3752 let tx = MockTransaction::eip1559()
3753 .with_sender(sender)
3754 .with_nonce(nonce)
3755 .with_gas_limit(21_000)
3756 .with_max_fee(150)
3757 .with_priority_fee(1)
3758 .rng_hash();
3759 let tx = f.validated(tx);
3760 ids.push(*tx.id());
3761 pool.insert_tx(tx, U256::MAX, 0).unwrap();
3762 }
3763
3764 pool.pending_fees.base_fee = 100;
3765 pool.update(&Default::default());
3766
3767 for id in ids {
3768 assert_eq!(pool.get(&id).unwrap().subpool, SubPool::Pending);
3769 }
3770 }
3771
3772 #[test]
3773 fn fee_update_keeps_descendants_of_underpriced_transaction_parked() {
3774 let mut f = MockTransactionFactory::default();
3775 let mut pool = AllTransactions::default();
3776 let sender = Address::with_last_byte(0x10);
3777 let fee_caps = [150, 50, 150];
3778 let mut ids = Vec::new();
3779
3780 pool.pending_fees.base_fee = 200;
3781 for (nonce, fee_cap) in fee_caps.into_iter().enumerate() {
3782 let tx = MockTransaction::eip1559()
3783 .with_sender(sender)
3784 .with_nonce(nonce as u64)
3785 .with_gas_limit(21_000)
3786 .with_max_fee(fee_cap)
3787 .with_priority_fee(1)
3788 .rng_hash();
3789 let tx = f.validated(tx);
3790 ids.push(*tx.id());
3791 pool.insert_tx(tx, U256::MAX, 0).unwrap();
3792 }
3793
3794 pool.pending_fees.base_fee = 100;
3795 pool.update(&Default::default());
3796
3797 assert_eq!(pool.get(&ids[0]).unwrap().subpool, SubPool::Pending);
3798 assert_eq!(pool.get(&ids[1]).unwrap().subpool, SubPool::BaseFee);
3799 assert_eq!(pool.get(&ids[2]).unwrap().subpool, SubPool::Queued);
3800 }
3801
3802 #[test]
3803 fn blob_fee_update_unparks_all_descendants() {
3804 let mut f = MockTransactionFactory::default();
3805 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3806 let sender = Address::with_last_byte(0x0f);
3807 let mut block_info = pool.block_info();
3808 block_info.pending_blob_fee = Some(200);
3809 pool.set_block_info(block_info);
3810
3811 for nonce in 0..3 {
3812 let tx = MockTransaction::eip4844()
3813 .with_sender(sender)
3814 .with_nonce(nonce)
3815 .with_gas_limit(21_000)
3816 .with_max_fee(1_000)
3817 .with_priority_fee(1)
3818 .with_blob_fee(150)
3819 .rng_hash();
3820 pool.add_transaction(f.validated(tx), U256::MAX, 0, None).unwrap();
3821 }
3822 assert_eq!(pool.blob_pool.len(), 3);
3823
3824 block_info.pending_blob_fee = Some(100);
3825 pool.on_canonical_state_change(
3826 block_info,
3827 Vec::new(),
3828 FxHashMap::default(),
3829 PoolUpdateKind::Commit,
3830 );
3831
3832 let nonces = pool.best_transactions().map(|tx| tx.nonce()).collect::<Vec<_>>();
3833 assert_eq!(nonces, vec![0, 1, 2]);
3834 }
3835
3836 #[test]
3837 fn update_basefee_subpools() {
3838 let mut f = MockTransactionFactory::default();
3839 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3840
3841 let tx = MockTransaction::eip1559().inc_price_by(10);
3842 let validated = f.validated(tx.clone());
3843 let id = *validated.id();
3844 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3845
3846 assert_eq!(pool.pending_pool.len(), 1);
3847
3848 pool.update_basefee((tx.max_fee_per_gas() + 1) as u64, |_| {});
3849
3850 assert!(pool.pending_pool.is_empty());
3851 assert_eq!(pool.basefee_pool.len(), 1);
3852
3853 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3854 }
3855
3856 #[test]
3857 fn update_basefee_subpools_setting_block_info() {
3858 let mut f = MockTransactionFactory::default();
3859 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3860
3861 let tx = MockTransaction::eip1559().inc_price_by(10);
3862 let validated = f.validated(tx.clone());
3863 let id = *validated.id();
3864 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3865
3866 assert_eq!(pool.pending_pool.len(), 1);
3867
3868 let mut block_info = pool.block_info();
3870 block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
3871 pool.set_block_info(block_info);
3872
3873 assert!(pool.pending_pool.is_empty());
3874 assert_eq!(pool.basefee_pool.len(), 1);
3875
3876 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3877 }
3878
3879 #[test]
3880 fn basefee_decrease_promotes_affordable_and_keeps_unaffordable() {
3881 let mut f = MockTransactionFactory::default();
3882 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3883
3884 let sender_a = Address::with_last_byte(0x0a);
3887 let sender_b = Address::with_last_byte(0x0b);
3888 let sender_c = Address::with_last_byte(0x0c);
3889
3890 let tx1 = MockTransaction::eip1559()
3891 .set_sender(sender_a)
3892 .set_nonce(0)
3893 .set_max_fee(500)
3894 .inc_limit();
3895 let tx2 = MockTransaction::eip1559()
3896 .set_sender(sender_b)
3897 .set_nonce(0)
3898 .set_max_fee(600)
3899 .inc_limit();
3900 let tx3 = MockTransaction::eip1559()
3901 .set_sender(sender_c)
3902 .set_nonce(0)
3903 .set_max_fee(400)
3904 .inc_limit();
3905
3906 let mut block_info = pool.block_info();
3908 block_info.pending_basefee = 700;
3909 pool.set_block_info(block_info);
3910
3911 let validated1 = f.validated(tx1);
3912 let validated2 = f.validated(tx2);
3913 let validated3 = f.validated(tx3);
3914 let id1 = *validated1.id();
3915 let id2 = *validated2.id();
3916 let id3 = *validated3.id();
3917
3918 pool.add_transaction(validated1, U256::from(10_000), 0, None).unwrap();
3922 pool.add_transaction(validated2, U256::from(10_000), 0, None).unwrap();
3923 pool.add_transaction(validated3, U256::from(10_000), 0, None).unwrap();
3924
3925 println!("Basefee pool len: {}", pool.basefee_pool.len());
3927 println!("Pending pool len: {}", pool.pending_pool.len());
3928 println!("tx1 subpool: {:?}", pool.all_transactions.txs.get(&id1).unwrap().subpool);
3929 println!("tx2 subpool: {:?}", pool.all_transactions.txs.get(&id2).unwrap().subpool);
3930 println!("tx3 subpool: {:?}", pool.all_transactions.txs.get(&id3).unwrap().subpool);
3931
3932 assert_eq!(pool.basefee_pool.len(), 3);
3934 assert_eq!(pool.pending_pool.len(), 0);
3935 assert_eq!(pool.all_transactions.txs.get(&id1).unwrap().subpool, SubPool::BaseFee);
3936 assert_eq!(pool.all_transactions.txs.get(&id2).unwrap().subpool, SubPool::BaseFee);
3937 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3938
3939 let mut block_info = pool.block_info();
3941 block_info.pending_basefee = 450; pool.set_block_info(block_info);
3944
3945 assert_eq!(pool.basefee_pool.len(), 1);
3950 assert_eq!(pool.pending_pool.len(), 2);
3951
3952 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3954
3955 let tx1_meta = pool.all_transactions.txs.get(&id1).unwrap();
3957 let tx2_meta = pool.all_transactions.txs.get(&id2).unwrap();
3958 assert_eq!(tx1_meta.subpool, SubPool::Pending);
3959 assert_eq!(tx2_meta.subpool, SubPool::Pending);
3960 assert!(tx1_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3961 assert!(tx2_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3962
3963 let best: Vec<_> = pool.best_transactions().take(3).collect();
3965 assert_eq!(best.len(), 2); assert!(best.iter().any(|tx| tx.id() == &id1));
3967 assert!(best.iter().any(|tx| tx.id() == &id2));
3968 }
3969
3970 #[test]
3971 fn apply_fee_updates_records_promotions_after_basefee_drop() {
3972 let mut f = MockTransactionFactory::default();
3973 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3974
3975 let tx = MockTransaction::eip1559()
3976 .with_gas_limit(21_000)
3977 .with_max_fee(500)
3978 .with_priority_fee(1);
3979 let validated = f.validated(tx);
3980 let id = *validated.id();
3981 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3982
3983 assert_eq!(pool.pending_pool.len(), 1);
3984
3985 pool.update_basefee(600, |_| {});
3987 assert!(pool.pending_pool.is_empty());
3988 assert_eq!(pool.basefee_pool.len(), 1);
3989
3990 let prev_base_fee = 600;
3991 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3992
3993 pool.all_transactions.pending_fees.base_fee = 400;
3995
3996 let mut outcome = UpdateOutcome::default();
3997 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3998
3999 assert_eq!(pool.pending_pool.len(), 1);
4000 assert!(pool.basefee_pool.is_empty());
4001 assert_eq!(outcome.promoted.len(), 1);
4002 assert_eq!(outcome.promoted[0].id(), &id);
4003 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
4004 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
4005
4006 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
4007 assert_eq!(tx_meta.subpool, SubPool::Pending);
4008 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4009 }
4010
4011 #[test]
4012 fn update_blob_fee_demotes_and_promotes_when_base_fee_falls() {
4013 let mut f = MockTransactionFactory::default();
4014 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4015
4016 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4017 let initial_base_fee = 600;
4018 pool.all_transactions.pending_fees.base_fee = initial_base_fee;
4019
4020 let tx_to_demote = MockTransaction::eip4844()
4021 .with_max_fee(700)
4022 .with_priority_fee(1)
4023 .with_blob_fee(initial_blob_fee + 100);
4024 let validated = f.validated(tx_to_demote);
4025 let demoted_id = *validated.id();
4026 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4027
4028 let tx_to_promote = MockTransaction::eip4844()
4029 .with_max_fee(500)
4030 .with_priority_fee(1)
4031 .with_blob_fee(initial_blob_fee + 300);
4032 let validated = f.validated(tx_to_promote);
4033 let promoted_id = *validated.id();
4034 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4035
4036 assert_eq!(pool.pending_pool.len(), 1);
4037 assert_eq!(pool.blob_pool.len(), 1);
4038
4039 let raised_blob_fee = initial_blob_fee + 200;
4042 pool.all_transactions.pending_fees.base_fee = 400;
4043 let mut promoted = Vec::new();
4044 pool.update_blob_fee(raised_blob_fee, Ordering::Less, |tx| promoted.push(*tx.id()));
4045
4046 assert_eq!(pool.pending_pool.len(), 1);
4047 assert_eq!(pool.blob_pool.len(), 1);
4048 assert_eq!(promoted, vec![promoted_id]);
4049
4050 assert_eq!(pool.all_transactions.txs.get(&demoted_id).unwrap().subpool, SubPool::Blob);
4051 assert_eq!(pool.all_transactions.txs.get(&promoted_id).unwrap().subpool, SubPool::Pending);
4052 }
4053
4054 #[test]
4055 fn apply_fee_updates_records_promotions_after_blob_fee_drop() {
4056 let mut f = MockTransactionFactory::default();
4057 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4058
4059 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4060
4061 let tx = MockTransaction::eip4844().with_blob_fee(initial_blob_fee + 100);
4062 let validated = f.validated(tx.clone());
4063 let id = *validated.id();
4064 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4065
4066 assert_eq!(pool.pending_pool.len(), 1);
4067
4068 let increased_blob_fee = tx.max_fee_per_blob_gas().unwrap() + 200;
4070 pool.update_blob_fee(increased_blob_fee, Ordering::Equal, |_| {});
4071 assert!(pool.pending_pool.is_empty());
4072 assert_eq!(pool.blob_pool.len(), 1);
4073
4074 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
4075 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4076
4077 pool.all_transactions.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
4079
4080 let mut outcome = UpdateOutcome::default();
4081 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
4082
4083 assert_eq!(pool.pending_pool.len(), 1);
4084 assert!(pool.blob_pool.is_empty());
4085 assert_eq!(outcome.promoted.len(), 1);
4086 assert_eq!(outcome.promoted[0].id(), &id);
4087 assert_eq!(pool.all_transactions.pending_fees.base_fee, prev_base_fee);
4088 assert_eq!(pool.all_transactions.pending_fees.blob_fee, tx.max_fee_per_blob_gas().unwrap());
4089
4090 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
4091 assert_eq!(tx_meta.subpool, SubPool::Pending);
4092 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4093 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4094 }
4095
4096 #[test]
4097 fn apply_fee_updates_promotes_blob_after_basefee_drop() {
4098 let mut f = MockTransactionFactory::default();
4099 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4100
4101 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4102
4103 let tx = MockTransaction::eip4844()
4104 .with_max_fee(500)
4105 .with_priority_fee(1)
4106 .with_blob_fee(initial_blob_fee + 100);
4107 let validated = f.validated(tx);
4108 let id = *validated.id();
4109 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4110
4111 assert_eq!(pool.pending_pool.len(), 1);
4112
4113 let high_base_fee = 600;
4115 pool.update_basefee(high_base_fee, |_| {});
4116 assert!(pool.pending_pool.is_empty());
4117 assert_eq!(pool.blob_pool.len(), 1);
4118
4119 let prev_base_fee = high_base_fee;
4120 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4121
4122 pool.all_transactions.pending_fees.base_fee = 400;
4124
4125 let mut outcome = UpdateOutcome::default();
4126 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
4127
4128 assert_eq!(pool.pending_pool.len(), 1);
4129 assert!(pool.blob_pool.is_empty());
4130 assert_eq!(outcome.promoted.len(), 1);
4131 assert_eq!(outcome.promoted[0].id(), &id);
4132 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
4133 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
4134
4135 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
4136 assert_eq!(tx_meta.subpool, SubPool::Pending);
4137 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4138 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4139 }
4140
4141 #[test]
4142 fn queued_transactions_include_blob_pool() {
4143 let mut f = MockTransactionFactory::default();
4144 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4145
4146 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4147 let tx = MockTransaction::eip4844()
4148 .with_max_fee(500)
4149 .with_priority_fee(1)
4150 .with_blob_fee(initial_blob_fee + 100);
4151 let validated = f.validated(tx);
4152 let id = *validated.id();
4153 let sender = validated.sender_id();
4154 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4155
4156 pool.update_basefee(600, |_| {});
4158 assert_eq!(pool.blob_pool.len(), 1);
4159
4160 let queued = pool.queued_transactions();
4161 assert_eq!(queued.len(), 1);
4162 assert_eq!(queued[0].id(), &id);
4163
4164 let by_sender = pool.queued_txs_by_sender(sender);
4165 assert_eq!(by_sender.len(), 1);
4166 assert_eq!(by_sender[0].id(), &id);
4167 }
4168
4169 #[test]
4170 fn apply_fee_updates_demotes_after_basefee_rise() {
4171 let mut f = MockTransactionFactory::default();
4172 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4173
4174 let tx = MockTransaction::eip1559()
4175 .with_gas_limit(21_000)
4176 .with_max_fee(400)
4177 .with_priority_fee(1);
4178 let validated = f.validated(tx);
4179 let id = *validated.id();
4180 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4181
4182 assert_eq!(pool.pending_pool.len(), 1);
4183
4184 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
4185 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4186
4187 let new_base_fee = prev_base_fee + 1_000;
4189 pool.all_transactions.pending_fees.base_fee = new_base_fee;
4190
4191 let mut outcome = UpdateOutcome::default();
4192 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
4193
4194 assert!(pool.pending_pool.is_empty());
4195 assert_eq!(pool.basefee_pool.len(), 1);
4196 assert!(outcome.promoted.is_empty());
4197 assert_eq!(pool.all_transactions.pending_fees.base_fee, new_base_fee);
4198 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
4199
4200 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
4201 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
4202 assert!(!tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4203 }
4204
4205 #[test]
4206 fn get_highest_transaction_by_sender_and_nonce() {
4207 let mut f = MockTransactionFactory::default();
4209 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4210
4211 let tx = MockTransaction::eip1559();
4213 pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0, None).unwrap();
4214
4215 let tx1 = tx.inc_price().next();
4217
4218 let tx1_validated = f.validated(tx1.clone());
4220 pool.add_transaction(tx1_validated, U256::from(1_000), 0, None).unwrap();
4221
4222 assert_eq!(
4224 pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
4225 Some(1)
4226 );
4227
4228 let highest_tx = pool
4230 .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
4231 .expect("Failed to retrieve highest transaction");
4232
4233 assert_eq!(highest_tx.as_ref().transaction, tx1);
4235 }
4236
4237 #[test]
4238 fn get_highest_consecutive_transaction_by_sender() {
4239 let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
4241 let mut f = MockTransactionFactory::default();
4242
4243 let sender = Address::random();
4245 let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
4246 for nonce in txs {
4247 let mut mock_tx = MockTransaction::eip1559();
4248 mock_tx.set_sender(sender);
4249 mock_tx.set_nonce(nonce);
4250
4251 let validated_tx = f.validated(mock_tx);
4252 pool.add_transaction(validated_tx, U256::from(1000), 0, None).unwrap();
4253 }
4254
4255 let sender_id = f.ids.sender_id(&sender).unwrap();
4257 let next_tx =
4258 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
4259 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
4260
4261 let next_tx =
4262 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
4263 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
4264
4265 let next_tx =
4266 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
4267 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
4268
4269 let mut info = SenderInfo::default();
4271 info.update(8, U256::ZERO);
4272 pool.all_transactions.sender_info.insert(sender_id, info);
4273 let next_tx =
4274 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
4275 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
4276 }
4277
4278 #[test]
4279 fn discard_nonce_too_low() {
4280 let mut f = MockTransactionFactory::default();
4281 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4282
4283 let tx = MockTransaction::eip1559().inc_price_by(10);
4284 let validated = f.validated(tx.clone());
4285 let id = *validated.id();
4286 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4287
4288 let next = tx.next();
4289 let validated = f.validated(next.clone());
4290 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4291
4292 assert_eq!(pool.pending_pool.len(), 2);
4293
4294 let mut changed_senders = HashMap::default();
4295 changed_senders.insert(
4296 id.sender,
4297 SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
4298 );
4299 let outcome = pool.update_accounts(changed_senders);
4300 assert_eq!(outcome.discarded.len(), 1);
4301 assert_eq!(pool.pending_pool.len(), 1);
4302 }
4303
4304 fn stale_validation_pool() -> TxPool<MockOrdering> {
4306 TxPool::new(
4307 MockOrdering::default(),
4308 PoolConfig { enforce_tracked_nonce: true, ..Default::default() },
4309 )
4310 }
4311
4312 #[test]
4313 fn stale_validation_accepted_by_default() {
4314 let mut f = MockTransactionFactory::default();
4315 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4316
4317 let template = MockTransaction::eip1559();
4318 let first = f.validated(template.clone().with_nonce(0).rng_hash());
4319 let sender = first.sender_id();
4320 pool.add_transaction(first, U256::from(1_000), 0, None).unwrap();
4321
4322 let changed = FxHashMap::from_iter([(
4323 sender,
4324 SenderInfo { state_nonce: 1, balance: U256::from(1_000) },
4325 )]);
4326 let outcome = pool.update_accounts(changed);
4327 assert_eq!(outcome.discarded.len(), 1);
4328 assert!(!pool.all_transactions.sender_info.contains_key(&sender));
4330
4331 let stale = f.validated(template.with_nonce(0).rng_hash());
4333 pool.add_transaction(stale, U256::from(1_000), 0, None).unwrap();
4334 assert_eq!(pool.pending_pool.len(), 1);
4335 assert_eq!(pool.all_transactions.sender_info[&sender].state_nonce, 0);
4336 }
4337
4338 #[test]
4339 fn stale_validation_does_not_regress_sender_state() {
4340 let mut f = MockTransactionFactory::default();
4341 let mut pool = stale_validation_pool();
4342
4343 let template = MockTransaction::eip1559();
4344 let first = f.validated(template.clone().with_nonce(0).rng_hash());
4345 let sender = first.sender_id();
4346 pool.add_transaction(first, U256::from(1_000), 0, None).unwrap();
4347
4348 let mut changed_senders = HashMap::default();
4349 changed_senders.insert(sender, SenderInfo { state_nonce: 1, balance: U256::from(1_000) });
4350 let outcome = pool.update_accounts(changed_senders);
4351 assert_eq!(outcome.discarded.len(), 1);
4352 assert_eq!(pool.all_transactions.sender_info[&sender].state_nonce, 1);
4353
4354 let current = f.validated(template.clone().with_nonce(1).rng_hash());
4356 pool.add_transaction(current, U256::from(1_000), 0, None).unwrap();
4357 assert_eq!(pool.pending_pool.len(), 1);
4358 assert_eq!(pool.queued_pool.len(), 0);
4359 assert_eq!(pool.all_transactions.sender_info[&sender].state_nonce, 1);
4360
4361 let stale = f.validated(template.with_nonce(0).rng_hash());
4362 let stale_hash = *stale.hash();
4363 let err = pool.add_transaction(stale, U256::from(1_000), 0, None).unwrap_err();
4364 assert!(matches!(
4365 err.kind,
4366 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Consensus(
4367 InvalidTransactionError::NonceNotConsistent { tx: 0, state: 1 }
4368 ))
4369 ));
4370 assert!(!pool.contains(&stale_hash));
4371 assert_eq!(pool.all_transactions.sender_info[&sender].state_nonce, 1);
4372 }
4373
4374 #[test]
4375 fn stale_validation_resubmission_after_commit() {
4376 let mut f = MockTransactionFactory::default();
4377 let mut pool = stale_validation_pool();
4378 let block_info = pool.block_info();
4379
4380 let template = MockTransaction::eip1559();
4381 let tx0 = template.clone().with_nonce(0).rng_hash();
4382 let tx1 = template.clone().with_nonce(1).rng_hash();
4383 let first = f.validated(tx0.clone());
4384 let sender = first.sender_id();
4385 pool.add_transaction(first, U256::from(1_000), 0, None).unwrap();
4386 pool.add_transaction(f.validated(tx1.clone()), U256::from(1_000), 0, None).unwrap();
4387 assert_eq!(pool.pending_pool.len(), 2);
4388
4389 let changed = FxHashMap::from_iter([(
4391 sender,
4392 SenderInfo { state_nonce: 1, balance: U256::from(1_000) },
4393 )]);
4394 pool.on_canonical_state_change(
4395 block_info,
4396 vec![*tx0.get_hash()],
4397 changed,
4398 PoolUpdateKind::Commit,
4399 );
4400 assert_eq!(pool.pending_pool.len(), 1);
4401
4402 let retry = f.validated(template.with_nonce(0).rng_hash());
4404 let err = pool.add_transaction(retry, U256::from(1_000), 0, None).unwrap_err();
4405 assert!(matches!(
4406 err.kind,
4407 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Consensus(
4408 InvalidTransactionError::NonceNotConsistent { tx: 0, state: 1 }
4409 ))
4410 ));
4411
4412 let replacement = f.validated(tx1.inc_price_by(10).rng_hash());
4414 let added = pool.add_transaction(replacement, U256::from(1_000), 0, None).unwrap();
4415 assert!(added.as_pending().is_some());
4416 assert!(added.replaced().is_some());
4417 assert_eq!(pool.pending_pool.len(), 1);
4418 assert_eq!(pool.all_transactions.sender_info[&sender].state_nonce, 1);
4419 let next =
4420 pool.get_highest_consecutive_transaction_by_sender(sender.into_transaction_id(1));
4421 assert_eq!(next.map(|tx| tx.nonce()), Some(1));
4422 }
4423
4424 #[test]
4425 fn stale_validation_uses_tracked_balance() {
4426 let mut f = MockTransactionFactory::default();
4427 let mut pool = stale_validation_pool();
4428
4429 let template = MockTransaction::eip1559().with_gas_price(50).with_gas_limit(30_000);
4431 let first = f.validated(template.clone().with_nonce(0).rng_hash());
4432 let sender = first.sender_id();
4433 pool.add_transaction(first, U256::from(10_000_000), 0, None).unwrap();
4434 assert_eq!(pool.pending_pool.len(), 1);
4435
4436 let changed = FxHashMap::from_iter([(
4438 sender,
4439 SenderInfo { state_nonce: 1, balance: U256::from(1_000) },
4440 )]);
4441 pool.update_accounts(changed);
4442
4443 let next = f.validated(template.with_nonce(1).rng_hash());
4445 let id = *next.id();
4446 pool.add_transaction(next, U256::from(10_000_000), 0, None).unwrap();
4447 assert_eq!(pool.pending_pool.len(), 0);
4448 assert_eq!(pool.queued_pool.len(), 1);
4449 let state = pool.all_transactions.get(&id).unwrap().state;
4450 assert!(state.contains(TxState::NO_NONCE_GAPS));
4451 assert!(!state.contains(TxState::ENOUGH_BALANCE));
4452 assert_eq!(pool.all_transactions.sender_info[&sender].balance, U256::from(1_000));
4453 }
4454
4455 #[test]
4456 fn discard_with_large_blob_txs() {
4457 reth_tracing::init_test_tracing();
4459
4460 let mut f = MockTransactionFactory::default();
4462 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4463 let default_limits = pool.config.blob_limit;
4464
4465 let a_sender = Address::with_last_byte(0x0a);
4468
4469 let mut block_info = pool.block_info();
4471 block_info.pending_blob_fee = Some(100);
4472 block_info.pending_basefee = 100;
4473
4474 pool.set_block_info(block_info);
4476
4477 let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
4479 .into_iter()
4480 .map(|mut tx| {
4481 tx.set_size(default_limits.max_size / 2 + 1);
4482 tx.set_max_fee((block_info.pending_basefee - 1).into());
4483 tx
4484 })
4485 .collect::<Vec<_>>();
4486
4487 for tx in a_txs {
4489 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
4490 }
4491
4492 let removed = pool.discard_worst();
4494 assert_eq!(removed.len(), 1);
4495 }
4496
4497 #[test]
4498 fn discard_with_parked_large_txs() {
4499 reth_tracing::init_test_tracing();
4501
4502 let mut f = MockTransactionFactory::default();
4504 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4505 let default_limits = pool.config.queued_limit;
4506
4507 let a_sender = Address::with_last_byte(0x0a);
4510
4511 let pool_base_fee = 100;
4513 pool.update_basefee(pool_base_fee, |_| {});
4514
4515 let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
4517 .into_iter()
4518 .map(|mut tx| {
4519 tx.set_size(default_limits.max_size / 2 + 1);
4520 tx.set_max_fee((pool_base_fee - 1).into());
4521 tx
4522 })
4523 .collect::<Vec<_>>();
4524
4525 for tx in a_txs {
4527 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
4528 }
4529
4530 let removed = pool.discard_worst();
4532 assert_eq!(removed.len(), 1);
4533 }
4534
4535 #[test]
4536 fn discard_at_capacity() {
4537 let mut f = MockTransactionFactory::default();
4538 let queued_limit = SubPoolLimit::new(1000, usize::MAX);
4539 let mut pool =
4540 TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
4541
4542 for _ in 0..queued_limit.max_txs {
4544 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
4545 let validated = f.validated(tx);
4546 let _id = *validated.id();
4547 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4548 }
4549
4550 let size = pool.size();
4551 assert_eq!(size.queued, queued_limit.max_txs);
4552
4553 for _ in 0..queued_limit.max_txs {
4554 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
4555 let validated = f.validated(tx);
4556 let _id = *validated.id();
4557 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4558
4559 pool.discard_worst();
4560 pool.assert_invariants();
4561 assert!(pool.size().queued <= queued_limit.max_txs);
4562 }
4563 }
4564
4565 #[test]
4566 fn discard_blobs_at_capacity() {
4567 let mut f = MockTransactionFactory::default();
4568 let blob_limit = SubPoolLimit::new(1000, usize::MAX);
4569 let mut pool =
4570 TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
4571 pool.all_transactions.pending_fees.blob_fee = 10000;
4572 for _ in 0..blob_limit.max_txs {
4574 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
4575 let validated = f.validated(tx);
4576 let _id = *validated.id();
4577 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4578 }
4579
4580 let size = pool.size();
4581 assert_eq!(size.blob, blob_limit.max_txs);
4582
4583 for _ in 0..blob_limit.max_txs {
4584 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
4585 let validated = f.validated(tx);
4586 let _id = *validated.id();
4587 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4588
4589 pool.discard_worst();
4590 pool.assert_invariants();
4591 assert!(pool.size().blob <= blob_limit.max_txs);
4592 }
4593 }
4594
4595 #[test]
4596 fn reuse_update_buffer_across_pool_operations() {
4597 let mut f = MockTransactionFactory::default();
4598 let mut pool = TxPool::new(
4599 MockOrdering::default(),
4600 PoolConfig { max_account_slots: 128, ..Default::default() },
4601 );
4602 let mut tx = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4603 let first = f.validated(tx.clone());
4604 for _ in 0..80 {
4605 pool.add_transaction(f.validated(tx.clone()), U256::MAX, 0, None).unwrap();
4606 tx = tx.next();
4607 }
4608 assert_eq!(pool.all_transactions.update_buffer.capacity(), 0);
4609
4610 let changed = |balance, state_nonce| {
4611 FxHashMap::from_iter([(first.sender_id(), SenderInfo { balance, state_nonce })])
4612 };
4613 let outcome = pool.update_accounts(changed(U256::ZERO, 0));
4614 assert!(outcome.promoted.is_empty());
4615 assert!(outcome.discarded.is_empty());
4616 assert_eq!(pool.queued_pool.len(), 80);
4617 let capacity = pool.all_transactions.update_buffer.capacity();
4618 assert!(capacity >= 80);
4619 let buffer = pool.all_transactions.update_buffer.as_ptr();
4620
4621 for _ in 0..2 {
4622 let outcome = pool.update_accounts(changed(U256::MAX, 0));
4623 assert_eq!(outcome.promoted.len(), 80);
4624 assert!(outcome.discarded.is_empty());
4625 assert_eq!(pool.pending_pool.len(), 80);
4626
4627 let outcome = pool.update_accounts(FxHashMap::default());
4628 assert!(outcome.promoted.is_empty());
4629 assert!(outcome.discarded.is_empty());
4630
4631 pool.remove_transaction_by_hash(first.hash()).unwrap();
4632 assert_eq!(pool.queued_pool.len(), 79);
4633
4634 let replacement = f.validated(first.transaction.next());
4636 assert!(pool.add_transaction(replacement, U256::MAX, 0, None).is_err());
4637 assert_eq!(pool.all_transactions.update_buffer.as_ptr(), buffer);
4638
4639 let added = pool.add_transaction(first.clone(), U256::MAX, 0, None).unwrap();
4640 let AddedTransaction::Pending(added) = added else { panic!("expected pending") };
4641 assert_eq!(added.promoted.len(), 79);
4642 assert!(added.discarded.is_empty());
4643 assert_eq!(pool.pending_pool.len(), 80);
4644
4645 pool.update_accounts(changed(U256::ZERO, 0));
4646 assert_eq!(pool.queued_pool.len(), 80);
4647 assert!(pool.all_transactions.update_buffer.is_empty());
4648 assert_eq!(pool.all_transactions.update_buffer.capacity(), capacity);
4649 assert_eq!(pool.all_transactions.update_buffer.as_ptr(), buffer);
4650 pool.assert_invariants();
4651 }
4652
4653 let outcome = pool.update_accounts(changed(U256::MAX, 80));
4654 assert_eq!(outcome.discarded.len(), 80);
4655 assert!(outcome.promoted.is_empty());
4656 assert!(pool.is_empty());
4657 assert!(pool.all_transactions.update_buffer.is_empty());
4658 assert_eq!(pool.all_transactions.update_buffer.as_ptr(), buffer);
4659 }
4660
4661 #[test]
4662 fn account_updates_sender_balance() {
4663 let mut on_chain_balance = U256::from(100);
4664 let on_chain_nonce = 0;
4665 let mut f = MockTransactionFactory::default();
4666 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4667
4668 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4669 let tx_1 = tx_0.next();
4670 let tx_2 = tx_1.next();
4671
4672 let v0 = f.validated(tx_0);
4674 let v1 = f.validated(tx_1);
4675 let v2 = f.validated(tx_2);
4676
4677 let _res =
4678 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4679 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4680 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4681
4682 assert_eq!(1, pool.pending_transactions().len());
4684 assert_eq!(2, pool.queued_transactions().len());
4685
4686 let mut updated_accounts = HashMap::default();
4688 on_chain_balance = U256::from(300);
4689 updated_accounts.insert(
4690 v0.sender_id(),
4691 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4692 );
4693 pool.update_accounts(updated_accounts.clone());
4694
4695 assert_eq!(3, pool.pending_transactions().len());
4696 assert!(pool.queued_transactions().is_empty());
4697
4698 updated_accounts.entry(v0.sender_id()).and_modify(|v| v.balance = U256::ONE);
4700 pool.update_accounts(updated_accounts);
4701
4702 assert!(pool.pending_transactions().is_empty());
4703 assert_eq!(3, pool.queued_transactions().len());
4704 }
4705
4706 #[test]
4707 fn account_updates_nonce_gap() {
4708 let on_chain_balance = U256::from(10_000);
4709 let mut on_chain_nonce = 0;
4710 let mut f = MockTransactionFactory::default();
4711 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4712
4713 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4714 let tx_1 = tx_0.next();
4715 let tx_2 = tx_1.next();
4716
4717 let v0 = f.validated(tx_0);
4719 let v1 = f.validated(tx_1);
4720 let v2 = f.validated(tx_2);
4721
4722 let _res =
4724 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4725 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4726
4727 assert!(pool.queued_transactions().is_empty());
4728 assert_eq!(2, pool.pending_transactions().len());
4729
4730 pool.remove_transaction_by_hash(v0.hash());
4732
4733 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4735
4736 assert_eq!(2, pool.queued_transactions().len());
4738 assert!(pool.pending_transactions().is_empty());
4739
4740 let mut updated_accounts = HashMap::default();
4742 on_chain_nonce += 1;
4743 updated_accounts.insert(
4744 v0.sender_id(),
4745 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4746 );
4747 pool.update_accounts(updated_accounts);
4748
4749 assert!(pool.queued_transactions().is_empty());
4751 assert_eq!(2, pool.pending_transactions().len());
4752 }
4753 #[test]
4754 fn test_transaction_removal() {
4755 let on_chain_balance = U256::from(10_000);
4756 let on_chain_nonce = 0;
4757 let mut f = MockTransactionFactory::default();
4758 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4759
4760 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4761 let tx_1 = tx_0.next();
4762
4763 let v0 = f.validated(tx_0);
4765 let v1 = f.validated(tx_1);
4766
4767 let _res =
4769 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4770 let _res =
4771 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4772
4773 assert_eq!(0, pool.queued_transactions().len());
4774 assert_eq!(2, pool.pending_transactions().len());
4775
4776 pool.remove_transaction(v0.id());
4778 let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
4780 assert_eq!(vec![v1.nonce()], pool_txs);
4781 }
4782 #[test]
4783 fn test_remove_transactions() {
4784 let on_chain_balance = U256::from(10_000);
4785 let on_chain_nonce = 0;
4786 let mut f = MockTransactionFactory::default();
4787 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4788
4789 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4790 let tx_1 = tx_0.next();
4791 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4792 let tx_3 = tx_2.next();
4793
4794 let v0 = f.validated(tx_0);
4796 let v1 = f.validated(tx_1);
4797 let v2 = f.validated(tx_2);
4798 let v3 = f.validated(tx_3);
4799
4800 let _res =
4802 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4803 let _res =
4804 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4805 let _res =
4806 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4807 let _res =
4808 pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4809
4810 assert_eq!(0, pool.queued_transactions().len());
4811 assert_eq!(4, pool.pending_transactions().len());
4812
4813 pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
4814
4815 assert_eq!(2, pool.queued_transactions().len());
4816 assert!(pool.pending_transactions().is_empty());
4817 assert!(pool.contains(v1.hash()));
4818 assert!(pool.contains(v3.hash()));
4819 }
4820
4821 #[test]
4822 fn test_remove_transactions_middle_pending_hash() {
4823 let on_chain_balance = U256::from(10_000);
4824 let on_chain_nonce = 0;
4825 let mut f = MockTransactionFactory::default();
4826 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4827
4828 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4829 let tx_1 = tx_0.next();
4830 let tx_2 = tx_1.next();
4831 let tx_3 = tx_2.next();
4832
4833 let v0 = f.validated(tx_0);
4835 let v1 = f.validated(tx_1);
4836 let v2 = f.validated(tx_2);
4837 let v3 = f.validated(tx_3);
4838
4839 let _res = pool.add_transaction(v0, on_chain_balance, on_chain_nonce, None).unwrap();
4841 let _res =
4842 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4843 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4844 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4845
4846 assert_eq!(0, pool.queued_transactions().len());
4847 assert_eq!(4, pool.pending_transactions().len());
4848
4849 let mut removed_txs = pool.remove_transactions(vec![*v1.hash()]);
4850 assert_eq!(1, removed_txs.len());
4851
4852 assert_eq!(2, pool.queued_transactions().len());
4853 assert_eq!(1, pool.pending_transactions().len());
4854
4855 let removed_tx = removed_txs.pop().unwrap();
4857 let v1 = f.validated(removed_tx.transaction.clone());
4858 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4859 assert_eq!(0, pool.queued_transactions().len());
4860 assert_eq!(4, pool.pending_transactions().len());
4861 }
4862
4863 #[test]
4864 fn test_remove_transactions_and_descendants() {
4865 let on_chain_balance = U256::from(10_000);
4866 let on_chain_nonce = 0;
4867 let mut f = MockTransactionFactory::default();
4868 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4869
4870 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4871 let tx_1 = tx_0.next();
4872 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4873 let tx_3 = tx_2.next();
4874 let tx_4 = tx_3.next();
4875
4876 let v0 = f.validated(tx_0);
4878 let v1 = f.validated(tx_1);
4879 let v2 = f.validated(tx_2);
4880 let v3 = f.validated(tx_3);
4881 let v4 = f.validated(tx_4);
4882
4883 let _res =
4885 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4886 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4887 let _res =
4888 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4889 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4890 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4891
4892 assert_eq!(0, pool.queued_transactions().len());
4893 assert_eq!(5, pool.pending_transactions().len());
4894
4895 pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
4896
4897 assert_eq!(0, pool.queued_transactions().len());
4898 assert_eq!(0, pool.pending_transactions().len());
4899 }
4900 #[test]
4901 fn test_remove_descendants() {
4902 let on_chain_balance = U256::from(10_000);
4903 let on_chain_nonce = 0;
4904 let mut f = MockTransactionFactory::default();
4905 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4906
4907 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4908 let tx_1 = tx_0.next();
4909 let tx_2 = tx_1.next();
4910 let tx_3 = tx_2.next();
4911
4912 let v0 = f.validated(tx_0);
4914 let v1 = f.validated(tx_1);
4915 let v2 = f.validated(tx_2);
4916 let v3 = f.validated(tx_3);
4917
4918 let _res =
4920 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4921 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4922 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4923 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4924
4925 assert_eq!(0, pool.queued_transactions().len());
4926 assert_eq!(4, pool.pending_transactions().len());
4927
4928 let mut removed = Vec::new();
4929 pool.remove_transaction(v0.id());
4930 pool.remove_descendants(v0.id(), &mut removed);
4931
4932 assert_eq!(0, pool.queued_transactions().len());
4933 assert_eq!(0, pool.pending_transactions().len());
4934 assert_eq!(3, removed.len());
4935 }
4936 #[test]
4937 fn test_remove_transactions_by_sender() {
4938 let on_chain_balance = U256::from(10_000);
4939 let on_chain_nonce = 0;
4940 let mut f = MockTransactionFactory::default();
4941 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4942
4943 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4944 let tx_1 = tx_0.next();
4945 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4946 let tx_3 = tx_2.next();
4947 let tx_4 = tx_3.next();
4948
4949 let v0 = f.validated(tx_0);
4951 let v1 = f.validated(tx_1);
4952 let v2 = f.validated(tx_2);
4953 let v3 = f.validated(tx_3);
4954 let v4 = f.validated(tx_4);
4955
4956 let _res =
4958 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4959 let _res =
4960 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4961 let _res =
4962 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4963 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4964 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4965
4966 assert_eq!(0, pool.queued_transactions().len());
4967 assert_eq!(5, pool.pending_transactions().len());
4968
4969 pool.remove_transactions_by_sender(v2.sender_id());
4970
4971 assert_eq!(0, pool.queued_transactions().len());
4972 assert_eq!(2, pool.pending_transactions().len());
4973 assert!(pool.contains(v0.hash()));
4974 assert!(pool.contains(v1.hash()));
4975 }
4976 #[test]
4977 fn wrong_best_order_of_transactions() {
4978 let on_chain_balance = U256::from(10_000);
4979 let mut on_chain_nonce = 0;
4980 let mut f = MockTransactionFactory::default();
4981 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4982
4983 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4984 let tx_1 = tx_0.next();
4985 let tx_2 = tx_1.next();
4986 let tx_3 = tx_2.next();
4987
4988 let v0 = f.validated(tx_0);
4990 let v1 = f.validated(tx_1);
4991 let v2 = f.validated(tx_2);
4992 let v3 = f.validated(tx_3);
4993
4994 let _res =
4996 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4997 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4998
4999 assert_eq!(0, pool.queued_transactions().len());
5000 assert_eq!(2, pool.pending_transactions().len());
5001
5002 pool.remove_transaction(v0.id());
5004
5005 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
5007
5008 assert_eq!(1, pool.queued_transactions().len());
5010 assert_eq!(1, pool.pending_transactions().len());
5011
5012 let mut updated_accounts = HashMap::default();
5014 on_chain_nonce += 1;
5015 updated_accounts.insert(
5016 v0.sender_id(),
5017 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
5018 );
5019 pool.update_accounts(updated_accounts);
5020
5021 assert_eq!(0, pool.queued_transactions().len());
5024 assert_eq!(2, pool.pending_transactions().len());
5025
5026 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
5028 assert_eq!(0, pool.queued_transactions().len());
5029 assert_eq!(3, pool.pending_transactions().len());
5030
5031 assert_eq!(
5034 pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
5035 vec![1, 2, 3]
5036 );
5037 }
5038
5039 #[test]
5040 fn test_best_with_attributes() {
5041 let on_chain_balance = U256::MAX;
5042 let on_chain_nonce = 0;
5043 let mut f = MockTransactionFactory::default();
5044 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5045
5046 let base_fee: u128 = 100;
5047 let blob_fee: u128 = 100;
5048
5049 let mut block_info = pool.block_info();
5051 block_info.pending_basefee = base_fee as u64;
5052 block_info.pending_blob_fee = Some(blob_fee);
5053 pool.set_block_info(block_info);
5054
5055 let tx1 = MockTransaction::eip4844()
5057 .with_sender(Address::with_last_byte(1))
5058 .with_max_fee(base_fee + 10)
5059 .with_blob_fee(blob_fee + 10);
5060 let tx2 = MockTransaction::eip4844()
5061 .with_sender(Address::with_last_byte(2))
5062 .with_max_fee(base_fee + 10)
5063 .with_blob_fee(blob_fee);
5064 let tx3 = MockTransaction::eip4844()
5065 .with_sender(Address::with_last_byte(3))
5066 .with_max_fee(base_fee)
5067 .with_blob_fee(blob_fee + 10);
5068 let tx4 = MockTransaction::eip4844()
5069 .with_sender(Address::with_last_byte(4))
5070 .with_max_fee(base_fee)
5071 .with_blob_fee(blob_fee);
5072 let tx5 = MockTransaction::eip4844()
5073 .with_sender(Address::with_last_byte(5))
5074 .with_max_fee(base_fee)
5075 .with_blob_fee(blob_fee - 10);
5076 let tx6 = MockTransaction::eip4844()
5077 .with_sender(Address::with_last_byte(6))
5078 .with_max_fee(base_fee - 10)
5079 .with_blob_fee(blob_fee);
5080 let tx7 = MockTransaction::eip4844()
5081 .with_sender(Address::with_last_byte(7))
5082 .with_max_fee(base_fee - 10)
5083 .with_blob_fee(blob_fee - 10);
5084
5085 for tx in vec![
5086 tx1.clone(),
5087 tx2.clone(),
5088 tx3.clone(),
5089 tx4.clone(),
5090 tx5.clone(),
5091 tx6.clone(),
5092 tx7.clone(),
5093 ] {
5094 pool.add_transaction(f.validated(tx), on_chain_balance, on_chain_nonce, None).unwrap();
5095 }
5096
5097 let base_fee = base_fee as u64;
5098 let blob_fee = blob_fee as u64;
5099
5100 let cases = vec![
5101 (BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee + 5)), vec![tx1.clone()]),
5103 (
5105 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee)),
5106 vec![tx1.clone(), tx2.clone()],
5107 ),
5108 (
5110 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee - 5)),
5111 vec![tx1.clone(), tx2.clone()],
5112 ),
5113 (
5115 BestTransactionsAttributes::new(base_fee, Some(blob_fee + 5)),
5116 vec![tx1.clone(), tx3.clone()],
5117 ),
5118 (
5120 BestTransactionsAttributes::new(base_fee, Some(blob_fee)),
5121 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
5122 ),
5123 (
5125 BestTransactionsAttributes::new(base_fee, Some(blob_fee - 10)),
5126 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx5.clone()],
5127 ),
5128 (
5130 BestTransactionsAttributes::new(base_fee - 5, Some(blob_fee + 5)),
5131 vec![tx1.clone(), tx3.clone()],
5132 ),
5133 (
5135 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee)),
5136 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx6.clone()],
5137 ),
5138 (
5140 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee - 10)),
5141 vec![tx1, tx2, tx5, tx3, tx4, tx6, tx7],
5142 ),
5143 ];
5144
5145 for (idx, (attribute, expected)) in cases.into_iter().enumerate() {
5146 let mut best = pool.best_transactions_with_attributes(attribute);
5147
5148 for (tx_idx, expected_tx) in expected.into_iter().enumerate() {
5149 let tx = best.next().expect("Transaction should be returned");
5150 assert_eq!(
5151 tx.transaction,
5152 expected_tx,
5153 "Failed tx {} in case {}",
5154 tx_idx + 1,
5155 idx + 1
5156 );
5157 }
5158
5159 assert!(best.next().is_none());
5161 }
5162 }
5163
5164 #[test]
5165 fn test_pending_ordering() {
5166 let mut f = MockTransactionFactory::default();
5167 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5168
5169 let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
5170 let tx_1 = tx_0.next();
5171
5172 let v0 = f.validated(tx_0);
5173 let v1 = f.validated(tx_1);
5174
5175 pool.add_transaction(v0.clone(), U256::MAX, 0, None).unwrap();
5177 assert_eq!(1, pool.queued_transactions().len());
5178
5179 pool.add_transaction(v1, U256::MAX, 1, None).unwrap();
5181
5182 assert_eq!(2, pool.pending_transactions().len());
5183 assert_eq!(0, pool.queued_transactions().len());
5184
5185 assert_eq!(
5186 pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
5187 v0.nonce()
5188 );
5189 }
5190
5191 #[test]
5193 fn one_sender_one_independent_transaction() {
5194 let mut on_chain_balance = U256::from(4_999); let mut on_chain_nonce = 40;
5196 let mut f = MockTransactionFactory::default();
5197 let mut pool = TxPool::mock();
5198 let mut submitted_txs = Vec::new();
5199
5200 let template =
5202 MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
5203
5204 for tx_nonce in 40..48 {
5207 let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
5208 submitted_txs.push(*tx.id());
5209 pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap();
5210 }
5211
5212 on_chain_balance = U256::from(999_999);
5215 on_chain_nonce = 42;
5216 pool.remove_transaction(&submitted_txs[0]);
5217 pool.remove_transaction(&submitted_txs[1]);
5218
5219 for tx_nonce in 48..52 {
5221 pool.add_transaction(
5222 f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
5223 on_chain_balance,
5224 on_chain_nonce,
5225 None,
5226 )
5227 .unwrap();
5228 }
5229
5230 let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
5231 assert_eq!(best_txs.len(), 10); assert_eq!(pool.pending_pool.independent().len(), 1);
5234 }
5235
5236 #[test]
5237 fn test_insertion_disorder() {
5238 let mut f = MockTransactionFactory::default();
5239 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5240
5241 let sender = address!("0x1234567890123456789012345678901234567890");
5242 let tx0 = f.validated_arc(
5243 MockTransaction::legacy().with_sender(sender).with_nonce(0).with_gas_price(10),
5244 );
5245 let tx1 = f.validated_arc(
5246 MockTransaction::eip1559()
5247 .with_sender(sender)
5248 .with_nonce(1)
5249 .with_gas_limit(1000)
5250 .with_gas_price(10),
5251 );
5252 let tx2 = f.validated_arc(
5253 MockTransaction::legacy().with_sender(sender).with_nonce(2).with_gas_price(10),
5254 );
5255 let tx3 = f.validated_arc(
5256 MockTransaction::legacy().with_sender(sender).with_nonce(3).with_gas_price(10),
5257 );
5258
5259 pool.add_transaction((*tx0).clone(), U256::from(1000), 0, None).unwrap();
5261 let mut best = pool.best_transactions();
5262 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5263 assert_eq!(t0.id(), tx0.id());
5264 pool.add_transaction((*tx1).clone(), U256::from(1000), 0, None).unwrap();
5266 let mut best = pool.best_transactions();
5267 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5268 assert_eq!(t0.id(), tx0.id());
5269 assert!(best.next().is_none());
5270
5271 pool.add_transaction((*tx2).clone(), U256::MAX, 0, None).unwrap();
5273
5274 let mut best = pool.best_transactions();
5275
5276 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5277 let t1 = best.next().expect("tx1 should be put in the pending subpool");
5278 let t2 = best.next().expect("tx2 should be put in the pending subpool");
5279 assert_eq!(t0.id(), tx0.id());
5280 assert_eq!(t1.id(), tx1.id());
5281 assert_eq!(t2.id(), tx2.id());
5282
5283 pool.add_transaction((*tx3).clone(), U256::MAX, 0, None).unwrap();
5285 let mut best = pool.best_transactions();
5286 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5287 let t1 = best.next().expect("tx1 should be put in the pending subpool");
5288 let t2 = best.next().expect("tx2 should be put in the pending subpool");
5289 let t3 = best.next().expect("tx3 should be put in the pending subpool");
5290 assert_eq!(t0.id(), tx0.id());
5291 assert_eq!(t1.id(), tx1.id());
5292 assert_eq!(t2.id(), tx2.id());
5293 assert_eq!(t3.id(), tx3.id());
5294 }
5295
5296 #[test]
5297 fn test_non_4844_blob_fee_bit_invariant() {
5298 let mut f = MockTransactionFactory::default();
5299 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5300
5301 let non_4844_tx = MockTransaction::eip1559().set_max_fee(200).inc_limit();
5302 let validated = f.validated(non_4844_tx.clone());
5303
5304 assert!(!non_4844_tx.is_eip4844());
5305 pool.add_transaction(validated.clone(), U256::from(10_000), 0, None).unwrap();
5306
5307 let tx_meta = pool.all_transactions.txs.get(validated.id()).unwrap();
5309 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
5310 assert_eq!(tx_meta.subpool, SubPool::Pending);
5311 }
5312
5313 #[test]
5314 fn test_blob_fee_enforcement_only_applies_to_eip4844() {
5315 let mut f = MockTransactionFactory::default();
5316 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5317
5318 let mut block_info = pool.block_info();
5320 block_info.pending_blob_fee = Some(160);
5321 block_info.pending_basefee = 100;
5322 pool.set_block_info(block_info);
5323
5324 let eip4844_tx = MockTransaction::eip4844()
5325 .with_sender(Address::with_last_byte(0x0a))
5326 .with_max_fee(200)
5327 .with_blob_fee(150) .inc_limit();
5329
5330 let non_4844_tx = MockTransaction::eip1559()
5331 .with_sender(Address::with_last_byte(0x0b))
5332 .set_max_fee(200)
5333 .inc_limit();
5334
5335 let validated_4844 = f.validated(eip4844_tx);
5336 let validated_non_4844 = f.validated(non_4844_tx);
5337
5338 pool.add_transaction(validated_4844.clone(), U256::from(10_000), 0, None).unwrap();
5339 pool.add_transaction(validated_non_4844.clone(), U256::from(10_000), 0, None).unwrap();
5340
5341 let tx_4844_meta = pool.all_transactions.txs.get(validated_4844.id()).unwrap();
5342 let tx_non_4844_meta = pool.all_transactions.txs.get(validated_non_4844.id()).unwrap();
5343
5344 assert!(!tx_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
5346 assert_eq!(tx_4844_meta.subpool, SubPool::Blob);
5347
5348 assert!(tx_non_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
5350 assert_eq!(tx_non_4844_meta.subpool, SubPool::Pending);
5351 }
5352
5353 #[test]
5354 fn test_basefee_decrease_preserves_non_4844_blob_fee_bit() {
5355 let mut f = MockTransactionFactory::default();
5356 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5357
5358 let non_4844_tx = MockTransaction::eip1559()
5360 .with_sender(Address::with_last_byte(0x0a))
5361 .set_max_fee(500) .inc_limit();
5363
5364 pool.update_basefee(600, |_| {});
5366
5367 let validated = f.validated(non_4844_tx);
5368 let tx_id = *validated.id();
5369 pool.add_transaction(validated, U256::from(10_000), 0, None).unwrap();
5370
5371 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
5373 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
5374 assert!(
5375 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
5376 "Non-4844 tx in BaseFee pool must retain ENOUGH_BLOB_FEE_CAP_BLOCK bit"
5377 );
5378
5379 pool.update_basefee(400, |_| {});
5382
5383 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
5385 assert_eq!(
5386 tx_meta.subpool,
5387 SubPool::Pending,
5388 "Non-4844 tx should be promoted from BaseFee to Pending after basefee decrease"
5389 );
5390 assert!(
5391 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
5392 "Non-4844 tx must NEVER lose ENOUGH_BLOB_FEE_CAP_BLOCK bit during basefee promotion"
5393 );
5394 assert!(
5395 tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK),
5396 "Non-4844 tx should gain ENOUGH_FEE_CAP_BLOCK bit after basefee decrease"
5397 );
5398 }
5399
5400 #[test]
5406 fn best_transactions_nonce_order_on_balance_unlock() {
5407 let mut f = MockTransactionFactory::default();
5408 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5409
5410 let sender = Address::random();
5411 let on_chain_balance = U256::from(10_000);
5412
5413 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
5415 let tx1 = tx0.next().inc_limit().with_value(on_chain_balance);
5417 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
5419
5420 let v0 = f.validated(tx0);
5421 let v1 = f.validated(tx1);
5422 let v2 = f.validated(tx2);
5423
5424 pool.add_transaction(v0, on_chain_balance, 0, None).unwrap();
5426
5427 let mut best = pool.best_transactions();
5429
5430 let first = best.next().expect("should yield tx0");
5432 assert_eq!(first.id().nonce, 0);
5433
5434 pool.add_transaction(v1, on_chain_balance, 0, None).unwrap();
5437
5438 assert!(best.next().is_none(), "tx1 should be queued, not pending");
5440
5441 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
5445
5446 let t1 = best.next().expect("should yield a transaction");
5447 let t2 = best.next().expect("should yield a transaction");
5448
5449 assert_eq!(
5451 t1.id().nonce,
5452 1,
5453 "first yielded tx should be nonce 1, got nonce {}",
5454 t1.id().nonce
5455 );
5456 assert_eq!(
5457 t2.id().nonce,
5458 2,
5459 "second yielded tx should be nonce 2, got nonce {}",
5460 t2.id().nonce
5461 );
5462 }
5463
5464 #[test]
5468 fn best_transactions_nonce_order_on_gap_fill() {
5469 let mut f = MockTransactionFactory::default();
5470 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5471
5472 let sender = Address::random();
5473 let balance = U256::MAX;
5474
5475 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
5477 let tx1 = tx0.next().inc_limit();
5479
5480 let v0 = f.validated(tx0);
5481 let v1 = f.validated(tx1);
5482
5483 pool.add_transaction(v1, balance, 0, None).unwrap();
5485
5486 let mut best = pool.best_transactions();
5488 assert!(best.next().is_none(), "pool should have no pending txs yet");
5489
5490 pool.add_transaction(v0, balance, 0, None).unwrap();
5492
5493 let t0 = best.next().expect("should yield a transaction");
5494 let t1 = best.next().expect("should yield a transaction");
5495
5496 assert_eq!(t0.id().nonce, 0, "first yielded tx should be nonce 0, got {}", t0.id().nonce);
5497 assert_eq!(t1.id().nonce, 1, "second yielded tx should be nonce 1, got {}", t1.id().nonce);
5498 }
5499
5500 #[test]
5504 fn best_transactions_nonce_order_mixed_promotions() {
5505 let mut f = MockTransactionFactory::default();
5506 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5507
5508 let sender = Address::random();
5509 let low_balance = U256::from(10_000);
5510
5511 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
5513 let tx1 = tx0.next().inc_limit().with_value(low_balance);
5515 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
5517 let tx3 = tx2.next().inc_limit().with_value(U256::ZERO);
5519
5520 let v0 = f.validated(tx0);
5521 let v1 = f.validated(tx1);
5522 let v2 = f.validated(tx2);
5523 let v3 = f.validated(tx3);
5524
5525 pool.add_transaction(v0, low_balance, 0, None).unwrap();
5527
5528 pool.add_transaction(v1, low_balance, 0, None).unwrap();
5530
5531 pool.add_transaction(v3, low_balance, 0, None).unwrap();
5533
5534 let mut best = pool.best_transactions();
5535
5536 let first = best.next().expect("should yield tx0");
5538 assert_eq!(first.id().nonce, 0);
5539 assert!(best.next().is_none(), "only tx0 should be pending");
5540
5541 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
5546
5547 let t1 = best.next().expect("should yield nonce 1");
5548 let t2 = best.next().expect("should yield nonce 2");
5549 let t3 = best.next().expect("should yield nonce 3");
5550
5551 assert_eq!(t1.id().nonce, 1, "expected nonce 1, got {}", t1.id().nonce);
5552 assert_eq!(t2.id().nonce, 2, "expected nonce 2, got {}", t2.id().nonce);
5553 assert_eq!(t3.id().nonce, 3, "expected nonce 3, got {}", t3.id().nonce);
5554 }
5555}