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 &&
2002 transaction.nonce() > on_chain_nonce &&
2003 !self.txs.contains_key(&transaction.transaction_id)
2004 {
2005 return Err(InsertErr::ExceededSenderTransactionsCapacity {
2006 transaction: Arc::new(transaction),
2007 })
2008 }
2009 }
2010 if transaction.gas_limit() > self.block_gas_limit {
2011 return Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas {
2012 block_gas_limit: self.block_gas_limit,
2013 tx_gas_limit: transaction.gas_limit(),
2014 transaction: Arc::new(transaction),
2015 })
2016 }
2017
2018 if self.contains_conflicting_transaction(&transaction) {
2019 return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) })
2021 }
2022
2023 Ok(transaction)
2024 }
2025
2026 fn ensure_valid_blob_transaction(
2032 &self,
2033 new_blob_tx: ValidPoolTransaction<T>,
2034 on_chain_balance: U256,
2035 ancestor: Option<TransactionId>,
2036 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
2037 let mut cumulative_cost = if let Some(ancestor) = ancestor {
2039 let Some(ancestor_tx) = self.txs.get(&ancestor) else {
2040 self.metrics.blob_transactions_nonce_gaps.increment(1);
2042 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
2043 };
2044 if ancestor_tx.state.has_nonce_gap() {
2045 self.metrics.blob_transactions_nonce_gaps.increment(1);
2048 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
2049 }
2050
2051 ancestor_tx.next_cumulative_cost()
2052 } else {
2053 U256::ZERO
2054 };
2055
2056 cumulative_cost += new_blob_tx.cost();
2058
2059 if cumulative_cost > on_chain_balance {
2061 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
2063 }
2064
2065 let id = new_blob_tx.transaction_id;
2068 let mut descendants = self.descendant_txs_inclusive(&id).peekable();
2069 if let Some((maybe_replacement, _)) = descendants.peek() &&
2070 **maybe_replacement == new_blob_tx.transaction_id
2071 {
2072 descendants.next();
2074
2075 for (_, tx) in descendants {
2077 cumulative_cost += tx.transaction.cost();
2078 if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance {
2079 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
2081 }
2082 }
2083 }
2084
2085 Ok(new_blob_tx)
2086 }
2087
2088 pub(crate) fn insert_tx(
2120 &mut self,
2121 transaction: ValidPoolTransaction<T>,
2122 on_chain_balance: U256,
2123 on_chain_nonce: u64,
2124 ) -> InsertResult<T> {
2125 assert!(on_chain_nonce <= transaction.nonce(), "Invalid transaction");
2126
2127 let mut transaction = self.ensure_valid(transaction, on_chain_nonce)?;
2128
2129 let inserted_tx_id = *transaction.id();
2130 let mut state = TxState::default();
2131 let mut cumulative_cost = U256::ZERO;
2132
2133 state.insert(TxState::NOT_TOO_MUCH_GAS);
2135
2136 let ancestor = TransactionId::ancestor(
2139 transaction.transaction.nonce(),
2140 on_chain_nonce,
2141 inserted_tx_id.sender,
2142 );
2143
2144 if transaction.is_eip4844() {
2147 state.insert(TxState::BLOB_TRANSACTION);
2148
2149 transaction =
2150 self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
2151 let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();
2152 if blob_fee_cap >= self.pending_fees.blob_fee {
2153 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
2154 }
2155 } else {
2156 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
2158 }
2159
2160 let transaction = Arc::new(transaction);
2161
2162 if ancestor.is_none() {
2164 state.insert(TxState::NO_NONCE_GAPS);
2165 state.insert(TxState::NO_PARKED_ANCESTORS);
2166 }
2167
2168 let fee_cap = transaction.max_fee_per_gas();
2170
2171 if fee_cap < self.minimal_protocol_basefee as u128 {
2172 return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap })
2173 }
2174 if fee_cap >= self.pending_fees.base_fee as u128 {
2175 state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
2176 }
2177
2178 let mut replaced_tx = None;
2180
2181 let pool_tx = PoolInternalTransaction {
2182 transaction: Arc::clone(&transaction),
2183 subpool: state.into(),
2184 state,
2185 cumulative_cost,
2186 };
2187
2188 match self.txs.entry(*transaction.id()) {
2190 Entry::Vacant(entry) => {
2191 self.by_hash.insert(*pool_tx.transaction.hash(), pool_tx.transaction.clone());
2193 self.tx_type_counts.inc(pool_tx.transaction.transaction.ty());
2194 entry.insert(pool_tx);
2195 }
2196 Entry::Occupied(mut entry) => {
2197 let existing_transaction = entry.get().transaction.as_ref();
2199 let maybe_replacement = transaction.as_ref();
2200
2201 if existing_transaction
2203 .is_replacement_underpriced(maybe_replacement, &self.price_bumps)
2204 {
2205 return Err(InsertErr::Underpriced {
2206 transaction: pool_tx.transaction,
2207 existing: *entry.get().transaction.hash(),
2208 })
2209 }
2210 let new_hash = *pool_tx.transaction.hash();
2211 let new_transaction = pool_tx.transaction.clone();
2212 self.tx_type_counts.inc(pool_tx.transaction.transaction.ty());
2213 let replaced = entry.insert(pool_tx);
2214 self.tx_type_counts.dec(replaced.transaction.transaction.ty());
2215 self.by_hash.remove(replaced.transaction.hash());
2216 self.by_hash.insert(new_hash, new_transaction);
2217
2218 self.remove_auths(&replaced);
2219
2220 replaced_tx = Some((replaced.transaction, replaced.subpool));
2222 }
2223 }
2224
2225 if let Some(auths) = &transaction.authority_ids {
2226 let tx_hash = transaction.hash();
2227 for auth in auths {
2228 self.auths.entry(*auth).or_default().insert(*tx_hash);
2229 }
2230 }
2231
2232 let mut updates = std::mem::take(&mut self.update_buffer);
2234
2235 let on_chain_id = TransactionId::new(transaction.sender_id(), on_chain_nonce);
2237 let pending_fees = self.pending_fees;
2238 {
2239 let mut next_nonce = on_chain_id.nonce;
2241
2242 let mut has_parked_ancestor = false;
2246
2247 for (id, tx) in self.descendant_txs_mut(&on_chain_id) {
2250 let current_pool = tx.subpool;
2251
2252 if next_nonce != id.nonce {
2254 break
2255 }
2256
2257 tx.state.insert(TxState::NO_NONCE_GAPS);
2259
2260 tx.cumulative_cost = cumulative_cost;
2262
2263 cumulative_cost = tx.next_cumulative_cost();
2265
2266 if cumulative_cost > on_chain_balance {
2267 tx.state.remove(TxState::ENOUGH_BALANCE);
2269 } else {
2270 tx.state.insert(TxState::ENOUGH_BALANCE);
2271 }
2272
2273 if has_parked_ancestor {
2275 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
2276 } else {
2277 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
2278 }
2279
2280 Self::update_tx_fees(pending_fees, tx);
2281
2282 tx.subpool = tx.state.into();
2284 has_parked_ancestor = !tx.state.is_pending();
2285
2286 if inserted_tx_id.eq(id) {
2287 state = tx.state;
2289 } else {
2290 if current_pool != tx.subpool {
2292 updates.push(PoolUpdate {
2293 id: *id,
2294 current: current_pool,
2295 destination: tx.subpool.into(),
2296 })
2297 }
2298 }
2299
2300 next_nonce = id.next_nonce();
2302 }
2303 }
2304
2305 if replaced_tx.is_none() {
2307 self.tx_inc(inserted_tx_id.sender);
2308 }
2309
2310 Ok(InsertOk { transaction, move_to: state.into(), state, replaced_tx, updates })
2311 }
2312
2313 pub(crate) fn len(&self) -> usize {
2315 self.txs.len()
2316 }
2317
2318 pub(crate) fn is_empty(&self) -> bool {
2320 self.txs.is_empty()
2321 }
2322
2323 #[cfg(any(test, feature = "test-utils"))]
2325 pub(crate) fn assert_invariants(&self) {
2326 assert_eq!(self.by_hash.len(), self.txs.len(), "by_hash.len() != txs.len()");
2327 assert!(self.auths.len() <= self.txs.len(), "auths.len() > txs.len()");
2328 }
2329}
2330
2331#[cfg(test)]
2332impl<T: PoolTransaction> AllTransactions<T> {
2333 pub(crate) fn tx_count(&self, sender: SenderId) -> usize {
2337 self.tx_counter.get(&sender).copied().unwrap_or_default()
2338 }
2339}
2340
2341impl<T: PoolTransaction> Default for AllTransactions<T> {
2342 fn default() -> Self {
2343 Self {
2344 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
2345 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
2346 block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
2347 by_hash: Default::default(),
2348 txs: Default::default(),
2349 sender_info: Default::default(),
2350 tx_counter: Default::default(),
2351 last_seen_block_number: Default::default(),
2352 last_seen_block_hash: Default::default(),
2353 pending_fees: Default::default(),
2354 last_full_update_fees: Default::default(),
2356 price_bumps: Default::default(),
2357 local_transactions_config: Default::default(),
2358 auths: Default::default(),
2359 tx_type_counts: Default::default(),
2360 update_buffer: Default::default(),
2361 metrics: Default::default(),
2362 }
2363 }
2364}
2365
2366#[derive(Debug, Clone, Copy, Default)]
2371pub(crate) struct TxTypeCounts {
2372 legacy: u64,
2373 eip2930: u64,
2374 eip1559: u64,
2375 eip4844: u64,
2376 eip7702: u64,
2377 other: u64,
2378}
2379
2380impl TxTypeCounts {
2381 const fn counter_mut(&mut self, tx_type: u8) -> &mut u64 {
2383 match tx_type {
2384 LEGACY_TX_TYPE_ID => &mut self.legacy,
2385 EIP2930_TX_TYPE_ID => &mut self.eip2930,
2386 EIP1559_TX_TYPE_ID => &mut self.eip1559,
2387 EIP4844_TX_TYPE_ID => &mut self.eip4844,
2388 EIP7702_TX_TYPE_ID => &mut self.eip7702,
2389 _ => &mut self.other,
2390 }
2391 }
2392
2393 const fn inc(&mut self, tx_type: u8) {
2395 *self.counter_mut(tx_type) += 1;
2396 }
2397
2398 const fn dec(&mut self, tx_type: u8) {
2400 *self.counter_mut(tx_type) -= 1;
2401 }
2402}
2403
2404#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2406pub(crate) struct PendingFees {
2407 pub(crate) base_fee: u64,
2409 pub(crate) blob_fee: u128,
2411}
2412
2413impl Default for PendingFees {
2414 fn default() -> Self {
2415 Self { base_fee: Default::default(), blob_fee: BLOB_TX_MIN_BLOB_GASPRICE }
2416 }
2417}
2418
2419pub(crate) type InsertResult<T> = Result<InsertOk<T>, InsertErr<T>>;
2421
2422#[derive(Debug)]
2424pub(crate) enum InsertErr<T: PoolTransaction> {
2425 Underpriced {
2427 transaction: Arc<ValidPoolTransaction<T>>,
2428 #[expect(dead_code)]
2429 existing: TxHash,
2430 },
2431 BlobTxHasNonceGap { transaction: Arc<ValidPoolTransaction<T>> },
2433 Overdraft { transaction: Arc<ValidPoolTransaction<T>> },
2436 FeeCapBelowMinimumProtocolFeeCap { transaction: Arc<ValidPoolTransaction<T>>, fee_cap: u128 },
2440 ExceededSenderTransactionsCapacity { transaction: Arc<ValidPoolTransaction<T>> },
2444 TxGasLimitMoreThanAvailableBlockGas {
2446 transaction: Arc<ValidPoolTransaction<T>>,
2447 block_gas_limit: u64,
2448 tx_gas_limit: u64,
2449 },
2450 TxTypeConflict { transaction: Arc<ValidPoolTransaction<T>> },
2452}
2453
2454#[derive(Debug)]
2456pub(crate) struct InsertOk<T: PoolTransaction> {
2457 transaction: Arc<ValidPoolTransaction<T>>,
2459 move_to: SubPool,
2461 state: TxState,
2463 replaced_tx: Option<(Arc<ValidPoolTransaction<T>>, SubPool)>,
2465 updates: Vec<PoolUpdate>,
2467}
2468
2469#[derive(Debug)]
2472pub(crate) struct PoolInternalTransaction<T: PoolTransaction> {
2473 pub(crate) transaction: Arc<ValidPoolTransaction<T>>,
2475 pub(crate) subpool: SubPool,
2477 pub(crate) state: TxState,
2480 pub(crate) cumulative_cost: U256,
2485}
2486
2487impl<T: PoolTransaction> PoolInternalTransaction<T> {
2490 fn next_cumulative_cost(&self) -> U256 {
2491 self.cumulative_cost + self.transaction.cost()
2492 }
2493}
2494
2495#[derive(Debug, Clone, Default)]
2497pub(crate) struct SenderInfo {
2498 pub(crate) state_nonce: u64,
2500 pub(crate) balance: U256,
2502}
2503
2504impl SenderInfo {
2507 const fn update(&mut self, state_nonce: u64, balance: U256) {
2509 *self = Self { state_nonce, balance };
2510 }
2511}
2512
2513#[cfg(test)]
2514mod tests {
2515 use super::*;
2516 use crate::{
2517 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
2518 traits::TransactionOrigin,
2519 SubPoolLimit,
2520 };
2521 use alloy_consensus::{Transaction, TxType};
2522 use alloy_primitives::address;
2523
2524 #[test]
2525 fn test_insert_blob() {
2526 let on_chain_balance = U256::MAX;
2527 let on_chain_nonce = 0;
2528 let mut f = MockTransactionFactory::default();
2529 let mut pool = AllTransactions::default();
2530 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2531 let valid_tx = f.validated(tx);
2532 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2533 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2534 assert!(updates.is_empty());
2535 assert!(replaced_tx.is_none());
2536 assert!(state.contains(TxState::NO_NONCE_GAPS));
2537 assert!(state.contains(TxState::ENOUGH_BALANCE));
2538 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2539 assert_eq!(move_to, SubPool::Pending);
2540
2541 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2542 assert_eq!(inserted.subpool, SubPool::Pending);
2543 }
2544
2545 #[test]
2546 fn test_blob_head_replacement_overdraft() {
2547 let on_chain_nonce = 0;
2548 let mut f = MockTransactionFactory::default();
2549 let mut pool = AllTransactions::default();
2550
2551 let head = MockTransaction::eip4844()
2553 .with_max_fee(100)
2554 .with_priority_fee(100)
2555 .with_nonce(on_chain_nonce)
2556 .with_gas_limit(10);
2557 let child = head.next();
2558 let head_id = f.validated(head.clone()).transaction_id;
2559 let child_id = f.validated(child.clone()).transaction_id;
2560 assert_eq!(*head.cost(), U256::from(1_000));
2561
2562 let on_chain_balance = U256::from(4_500);
2564 pool.insert_tx(f.validated(head.clone()), on_chain_balance, on_chain_nonce).unwrap();
2565 pool.insert_tx(f.validated(child), on_chain_balance, on_chain_nonce).unwrap();
2566 assert_eq!(pool.txs.get(&child_id).unwrap().subpool, SubPool::Pending);
2567
2568 let blob_fee = 3 * head.max_fee_per_blob_gas().unwrap();
2570 let replacement = head
2571 .rng_hash()
2572 .with_max_fee(400)
2573 .with_priority_fee(400)
2574 .with_blob_fee(blob_fee)
2575 .with_gas_limit(10);
2576 assert_eq!(*replacement.cost(), U256::from(4_000));
2577 let err =
2578 pool.insert_tx(f.validated(replacement), on_chain_balance, on_chain_nonce).unwrap_err();
2579 assert!(matches!(err, InsertErr::Overdraft { .. }), "{err:?}");
2580
2581 assert_eq!(pool.txs.get(&head_id).unwrap().transaction.cost(), &U256::from(1_000));
2583 assert_eq!(pool.txs.get(&child_id).unwrap().subpool, SubPool::Pending);
2584 assert!(pool.txs.get(&child_id).unwrap().state.contains(TxState::ENOUGH_BALANCE));
2585 }
2586
2587 #[test]
2588 fn test_insert_blob_not_enough_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 = AllTransactions {
2593 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2594 ..Default::default()
2595 };
2596 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2597 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2598 let valid_tx = f.validated(tx);
2599 let InsertOk { state, .. } =
2600 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2601 assert!(state.contains(TxState::NO_NONCE_GAPS));
2602 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2603
2604 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2605 }
2606
2607 #[test]
2608 fn test_valid_tx_with_decreasing_blob_fee() {
2609 let on_chain_balance = U256::MAX;
2610 let on_chain_nonce = 0;
2611 let mut f = MockTransactionFactory::default();
2612 let mut pool = AllTransactions {
2613 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2614 ..Default::default()
2615 };
2616 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2617
2618 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2619 let valid_tx = f.validated(tx.clone());
2620 let InsertOk { state, .. } =
2621 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2622 assert!(state.contains(TxState::NO_NONCE_GAPS));
2623 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2624
2625 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2626 pool.remove_transaction(&valid_tx.transaction_id);
2627
2628 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
2629 let InsertOk { state, .. } =
2630 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2631 assert!(state.contains(TxState::NO_NONCE_GAPS));
2632 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2633 }
2634
2635 #[test]
2636 fn test_demote_valid_tx_with_increasing_blob_fee() {
2637 let on_chain_balance = U256::MAX;
2638 let on_chain_nonce = 0;
2639 let mut f = MockTransactionFactory::default();
2640 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2641 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2642
2643 let mut block_info = pool.block_info();
2645 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2646 pool.set_block_info(block_info);
2647
2648 let validated = f.validated(tx.clone());
2649 let id = *validated.id();
2650 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2651
2652 assert!(pool.blob_pool.is_empty());
2654 assert_eq!(pool.pending_pool.len(), 1);
2655
2656 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2658 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2659 assert_eq!(internal_tx.subpool, SubPool::Pending);
2660
2661 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2663 pool.set_block_info(block_info);
2664
2665 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2667 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2668 assert_eq!(internal_tx.subpool, SubPool::Blob);
2669
2670 assert_eq!(pool.blob_pool.len(), 1);
2672 assert!(pool.pending_pool.is_empty());
2673 }
2674
2675 #[test]
2676 fn test_promote_valid_tx_with_decreasing_blob_fee() {
2677 let on_chain_balance = U256::MAX;
2678 let on_chain_nonce = 0;
2679 let mut f = MockTransactionFactory::default();
2680 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2681 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2682
2683 let mut block_info = pool.block_info();
2685 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2686 pool.set_block_info(block_info);
2687
2688 let validated = f.validated(tx.clone());
2689 let id = *validated.id();
2690 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2691
2692 assert!(pool.pending_pool.is_empty());
2694 assert_eq!(pool.blob_pool.len(), 1);
2695
2696 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2698 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2699 assert_eq!(internal_tx.subpool, SubPool::Blob);
2700
2701 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2703 pool.set_block_info(block_info);
2704
2705 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2707 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2708 assert_eq!(internal_tx.subpool, SubPool::Pending);
2709
2710 assert_eq!(pool.pending_pool.len(), 1);
2712 assert!(pool.blob_pool.is_empty());
2713 }
2714
2715 #[test]
2716 fn test_queued_count_includes_blob_pool() {
2717 let on_chain_balance = U256::MAX;
2718 let on_chain_nonce = 0;
2719 let mut f = MockTransactionFactory::default();
2720 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2721 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2722
2723 let mut block_info = pool.block_info();
2725 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2726 pool.set_block_info(block_info);
2727
2728 let validated = f.validated(tx);
2729 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2730
2731 assert_eq!(pool.blob_pool.len(), 1);
2732 assert!(pool.pending_pool.is_empty());
2733
2734 assert_eq!(pool.queued_transactions_count(), 1);
2736 }
2737
2738 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
2740 struct PromotionTest {
2741 basefee: u64,
2743 blobfee: u128,
2745 subpool: SubPool,
2747 basefee_update: u64,
2749 blobfee_update: u128,
2751 new_subpool: SubPool,
2753 }
2754
2755 impl PromotionTest {
2756 const fn opposite(&self) -> Self {
2758 Self {
2759 basefee: self.basefee_update,
2760 blobfee: self.blobfee_update,
2761 subpool: self.new_subpool,
2762 blobfee_update: self.blobfee,
2763 basefee_update: self.basefee,
2764 new_subpool: self.subpool,
2765 }
2766 }
2767
2768 fn assert_subpool_lengths<T: TransactionOrdering>(
2769 &self,
2770 pool: &TxPool<T>,
2771 failure_message: String,
2772 check_subpool: SubPool,
2773 ) {
2774 match check_subpool {
2775 SubPool::Blob => {
2776 assert_eq!(pool.blob_pool.len(), 1, "{failure_message}");
2777 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2778 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2779 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2780 }
2781 SubPool::Pending => {
2782 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2783 assert_eq!(pool.pending_pool.len(), 1, "{failure_message}");
2784 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2785 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2786 }
2787 SubPool::BaseFee => {
2788 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2789 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2790 assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}");
2791 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2792 }
2793 SubPool::Queued => {
2794 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2795 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2796 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2797 assert_eq!(pool.queued_pool.len(), 1, "{failure_message}");
2798 }
2799 }
2800 }
2801
2802 fn assert_single_tx_starting_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2806 self.assert_subpool_lengths(
2807 pool,
2808 format!("pool length check failed at start of test: {self:?}"),
2809 self.subpool,
2810 );
2811 }
2812
2813 fn assert_single_tx_ending_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2817 self.assert_subpool_lengths(
2818 pool,
2819 format!("pool length check failed at end of test: {self:?}"),
2820 self.new_subpool,
2821 );
2822 }
2823 }
2824
2825 #[test]
2826 fn test_promote_blob_tx_with_both_pending_fee_updates() {
2827 let on_chain_balance = U256::MAX;
2830 let on_chain_nonce = 0;
2831 let mut f = MockTransactionFactory::default();
2832 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2833
2834 let max_fee_per_blob_gas = tx.max_fee_per_blob_gas().unwrap();
2835 let max_fee_per_gas = tx.max_fee_per_gas() as u64;
2836
2837 let mut expected_promotions = vec![
2839 PromotionTest {
2840 blobfee: max_fee_per_blob_gas + 1,
2841 basefee: max_fee_per_gas + 1,
2842 subpool: SubPool::Blob,
2843 blobfee_update: max_fee_per_blob_gas + 1,
2844 basefee_update: max_fee_per_gas + 1,
2845 new_subpool: SubPool::Blob,
2846 },
2847 PromotionTest {
2848 blobfee: max_fee_per_blob_gas + 1,
2849 basefee: max_fee_per_gas + 1,
2850 subpool: SubPool::Blob,
2851 blobfee_update: max_fee_per_blob_gas,
2852 basefee_update: max_fee_per_gas + 1,
2853 new_subpool: SubPool::Blob,
2854 },
2855 PromotionTest {
2856 blobfee: max_fee_per_blob_gas + 1,
2857 basefee: max_fee_per_gas + 1,
2858 subpool: SubPool::Blob,
2859 blobfee_update: max_fee_per_blob_gas + 1,
2860 basefee_update: max_fee_per_gas,
2861 new_subpool: SubPool::Blob,
2862 },
2863 PromotionTest {
2864 blobfee: max_fee_per_blob_gas + 1,
2865 basefee: max_fee_per_gas + 1,
2866 subpool: SubPool::Blob,
2867 blobfee_update: max_fee_per_blob_gas,
2868 basefee_update: max_fee_per_gas,
2869 new_subpool: SubPool::Pending,
2870 },
2871 PromotionTest {
2872 blobfee: max_fee_per_blob_gas,
2873 basefee: max_fee_per_gas + 1,
2874 subpool: SubPool::Blob,
2875 blobfee_update: max_fee_per_blob_gas,
2876 basefee_update: max_fee_per_gas,
2877 new_subpool: SubPool::Pending,
2878 },
2879 PromotionTest {
2880 blobfee: max_fee_per_blob_gas + 1,
2881 basefee: max_fee_per_gas,
2882 subpool: SubPool::Blob,
2883 blobfee_update: max_fee_per_blob_gas,
2884 basefee_update: max_fee_per_gas,
2885 new_subpool: SubPool::Pending,
2886 },
2887 PromotionTest {
2888 blobfee: max_fee_per_blob_gas,
2889 basefee: max_fee_per_gas,
2890 subpool: SubPool::Pending,
2891 blobfee_update: max_fee_per_blob_gas,
2892 basefee_update: max_fee_per_gas,
2893 new_subpool: SubPool::Pending,
2894 },
2895 ];
2896
2897 let reversed = expected_promotions.iter().map(|test| test.opposite()).collect::<Vec<_>>();
2899 expected_promotions.extend(reversed);
2900
2901 let expected_promotions = expected_promotions.into_iter().collect::<HashSet<_>>();
2903
2904 for promotion_test in &expected_promotions {
2905 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2906
2907 let mut block_info = pool.block_info();
2909
2910 block_info.pending_blob_fee = Some(promotion_test.blobfee);
2911 block_info.pending_basefee = promotion_test.basefee;
2912 pool.set_block_info(block_info);
2913
2914 let validated = f.validated(tx.clone());
2915 let id = *validated.id();
2916 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2917
2918 promotion_test.assert_single_tx_starting_subpool(&pool);
2920
2921 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2923 assert_eq!(
2924 internal_tx.subpool, promotion_test.subpool,
2925 "Subpools do not match at start of test: {promotion_test:?}"
2926 );
2927
2928 block_info.pending_basefee = promotion_test.basefee_update;
2930 block_info.pending_blob_fee = Some(promotion_test.blobfee_update);
2931 pool.set_block_info(block_info);
2932
2933 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2935 assert_eq!(
2936 internal_tx.subpool, promotion_test.new_subpool,
2937 "Subpools do not match at end of test: {promotion_test:?}"
2938 );
2939
2940 promotion_test.assert_single_tx_ending_subpool(&pool);
2942 }
2943 }
2944
2945 #[test]
2946 fn test_insert_pending() {
2947 let on_chain_balance = U256::MAX;
2948 let on_chain_nonce = 0;
2949 let mut f = MockTransactionFactory::default();
2950 let mut pool = AllTransactions::default();
2951 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2952 let valid_tx = f.validated(tx);
2953 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2954 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2955 assert!(updates.is_empty());
2956 assert!(replaced_tx.is_none());
2957 assert!(state.contains(TxState::NO_NONCE_GAPS));
2958 assert!(state.contains(TxState::ENOUGH_BALANCE));
2959 assert_eq!(move_to, SubPool::Pending);
2960
2961 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2962 assert_eq!(inserted.subpool, SubPool::Pending);
2963 }
2964
2965 #[test]
2966 fn test_simple_insert() {
2967 let on_chain_balance = U256::ZERO;
2968 let on_chain_nonce = 0;
2969 let mut f = MockTransactionFactory::default();
2970 let mut pool = AllTransactions::default();
2971 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2972 tx.set_priority_fee(100);
2973 tx.set_max_fee(100);
2974 let valid_tx = f.validated(tx.clone());
2975 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2976 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2977 assert!(updates.is_empty());
2978 assert!(replaced_tx.is_none());
2979 assert!(state.contains(TxState::NO_NONCE_GAPS));
2980 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2981 assert_eq!(move_to, SubPool::Queued);
2982
2983 assert_eq!(pool.len(), 1);
2984 assert!(pool.contains(valid_tx.hash()));
2985 let expected_state = TxState::ENOUGH_FEE_CAP_BLOCK | TxState::NO_NONCE_GAPS;
2986 let inserted = pool.get(valid_tx.id()).unwrap();
2987 assert!(inserted.state.intersects(expected_state));
2988
2989 let res = pool.insert_tx(valid_tx, on_chain_balance, on_chain_nonce);
2991 res.unwrap_err();
2992 assert_eq!(pool.len(), 1);
2993
2994 let valid_tx = f.validated(tx.next());
2995 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2996 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2997
2998 assert!(updates.is_empty());
2999 assert!(replaced_tx.is_none());
3000 assert!(state.contains(TxState::NO_NONCE_GAPS));
3001 assert!(!state.contains(TxState::ENOUGH_BALANCE));
3002 assert_eq!(move_to, SubPool::Queued);
3003
3004 assert!(pool.contains(valid_tx.hash()));
3005 assert_eq!(pool.len(), 2);
3006 let inserted = pool.get(valid_tx.id()).unwrap();
3007 assert!(inserted.state.intersects(expected_state));
3008 }
3009
3010 #[test]
3011 fn test_on_canonical_state_change_no_double_processing() {
3014 let mut tx_factory = MockTransactionFactory::default();
3015 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3016
3017 let tx = MockTransaction::eip1559().with_gas_price(50).with_gas_limit(30_000);
3019 let sender = tx.sender();
3020
3021 let mut block_info = pool.block_info();
3023 block_info.pending_basefee = 100;
3024 pool.set_block_info(block_info);
3025
3026 let validated = tx_factory.validated(tx);
3027 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
3028
3029 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
3031
3032 assert_eq!(pool.basefee_pool.len(), 1);
3033 assert_eq!(pool.pending_pool.len(), 0);
3034
3035 block_info.pending_basefee = 40;
3039
3040 let mut changed_senders = FxHashMap::default();
3041 changed_senders.insert(
3042 sender_id,
3043 SenderInfo {
3044 state_nonce: 0,
3045 balance: U256::from(20_000_000), },
3047 );
3048
3049 let outcome = pool.on_canonical_state_change(
3050 block_info,
3051 vec![], changed_senders,
3053 PoolUpdateKind::Commit,
3054 );
3055
3056 assert_eq!(pool.pending_pool.len(), 1, "Transaction should be in pending pool");
3058 assert_eq!(pool.basefee_pool.len(), 0, "Transaction should not be in basefee pool");
3059 assert_eq!(outcome.promoted.len(), 1, "Should report exactly one promotion");
3060 }
3061
3062 #[test]
3063 fn test_canonical_state_change_with_basefee_update_regression() {
3066 let mut tx_factory = MockTransactionFactory::default();
3067 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3068
3069 let sender_balance = U256::from(100_000_000);
3071
3072 let tx1 =
3074 MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000).with_nonce(0);
3075 let sender1 = tx1.sender();
3076
3077 let tx2 =
3079 MockTransaction::eip1559().with_gas_price(55).with_gas_limit(21_000).with_nonce(0);
3080 let sender2 = tx2.sender();
3081
3082 let tx3 =
3084 MockTransaction::eip1559().with_gas_price(45).with_gas_limit(21_000).with_nonce(0);
3085 let sender3 = tx3.sender();
3086
3087 let mut block_info = pool.block_info();
3089 block_info.pending_basefee = 70;
3090 pool.set_block_info(block_info);
3091
3092 let validated1 = tx_factory.validated(tx1);
3094 let validated2 = tx_factory.validated(tx2);
3095 let validated3 = tx_factory.validated(tx3);
3096
3097 pool.add_transaction(validated1, sender_balance, 0, None).unwrap();
3098 pool.add_transaction(validated2, sender_balance, 0, None).unwrap();
3099 pool.add_transaction(validated3, sender_balance, 0, None).unwrap();
3100
3101 let sender1_id = tx_factory.ids.sender_id(&sender1).unwrap();
3102 let sender2_id = tx_factory.ids.sender_id(&sender2).unwrap();
3103 let sender3_id = tx_factory.ids.sender_id(&sender3).unwrap();
3104
3105 assert_eq!(pool.basefee_pool.len(), 3, "All txs should be in basefee pool");
3107 assert_eq!(pool.pending_pool.len(), 0, "No txs should be in pending pool");
3108
3109 block_info.pending_basefee = 50;
3112
3113 let mut changed_senders = FxHashMap::default();
3115 changed_senders.insert(
3116 sender1_id,
3117 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
3118 );
3119 changed_senders.insert(
3120 sender2_id,
3121 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
3122 );
3123 changed_senders.insert(
3124 sender3_id,
3125 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
3126 );
3127
3128 let outcome = pool.on_canonical_state_change(
3129 block_info,
3130 vec![],
3131 changed_senders,
3132 PoolUpdateKind::Commit,
3133 );
3134
3135 assert_eq!(pool.pending_pool.len(), 2, "tx1 and tx2 should be promoted");
3137 assert_eq!(pool.basefee_pool.len(), 1, "tx3 should remain in basefee");
3138
3139 assert_eq!(
3142 outcome.promoted.len(),
3143 2,
3144 "Should report exactly 2 promotions, not double-counted"
3145 );
3146
3147 let promoted_prices: Vec<u128> =
3149 outcome.promoted.iter().map(|tx| tx.max_fee_per_gas()).collect();
3150 assert!(promoted_prices.contains(&60));
3151 assert!(promoted_prices.contains(&55));
3152 }
3153
3154 #[test]
3155 fn test_basefee_decrease_with_empty_senders() {
3156 let mut tx_factory = MockTransactionFactory::default();
3159 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3160
3161 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
3163
3164 let mut block_info = pool.block_info();
3166 block_info.pending_basefee = 100;
3167 pool.set_block_info(block_info);
3168
3169 let validated = tx_factory.validated(tx);
3171 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
3172
3173 assert_eq!(pool.basefee_pool.len(), 1);
3174 assert_eq!(pool.pending_pool.len(), 0);
3175
3176 block_info.pending_basefee = 50;
3178 let outcome = pool.on_canonical_state_change(
3179 block_info,
3180 vec![],
3181 FxHashMap::default(), PoolUpdateKind::Commit,
3183 );
3184
3185 assert_eq!(pool.pending_pool.len(), 1, "Fee decrease should promote tx");
3187 assert_eq!(pool.basefee_pool.len(), 0);
3188 assert_eq!(outcome.promoted.len(), 1, "Should report promotion from fee update");
3189 }
3190
3191 #[test]
3192 fn test_basefee_decrease_account_makes_unfundable() {
3193 let mut tx_factory = MockTransactionFactory::default();
3196 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3197
3198 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
3199 let sender = tx.sender();
3200
3201 let mut block_info = pool.block_info();
3203 block_info.pending_basefee = 100;
3204 pool.set_block_info(block_info);
3205
3206 let validated = tx_factory.validated(tx);
3207 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
3208 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
3209
3210 assert_eq!(pool.basefee_pool.len(), 1);
3211
3212 block_info.pending_basefee = 50;
3214 let mut changed_senders = FxHashMap::default();
3215 changed_senders.insert(
3216 sender_id,
3217 SenderInfo {
3218 state_nonce: 0,
3219 balance: U256::from(100), },
3221 );
3222
3223 let outcome = pool.on_canonical_state_change(
3224 block_info,
3225 vec![],
3226 changed_senders,
3227 PoolUpdateKind::Commit,
3228 );
3229
3230 assert_eq!(pool.pending_pool.len(), 0, "Unfunded tx should not be in pending");
3232 assert_eq!(pool.basefee_pool.len(), 0, "Tx no longer in basefee pool");
3233 assert_eq!(pool.queued_pool.len(), 1, "Unfunded tx should be in queued pool");
3234
3235 let tx_count = pool.all_transactions.txs.len();
3237 assert_eq!(tx_count, 1, "Transaction should still be in pool (in queued)");
3238
3239 assert_eq!(outcome.promoted.len(), 0, "Should not report promotion");
3240 assert_eq!(outcome.discarded.len(), 0, "Queued tx is not reported as discarded");
3241 }
3242
3243 #[test]
3244 fn insert_already_imported() {
3245 let on_chain_balance = U256::ZERO;
3246 let on_chain_nonce = 0;
3247 let mut f = MockTransactionFactory::default();
3248 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3249 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3250 let tx = f.validated(tx);
3251 pool.add_transaction(tx.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3252 match pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap_err().kind {
3253 PoolErrorKind::AlreadyImported => {}
3254 _ => unreachable!(),
3255 }
3256 }
3257
3258 #[test]
3259 fn insert_replace() {
3260 let on_chain_balance = U256::ZERO;
3261 let on_chain_nonce = 0;
3262 let mut f = MockTransactionFactory::default();
3263 let mut pool = AllTransactions::default();
3264 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3265 let first = f.validated(tx.clone());
3266 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3267 let replacement = f.validated(tx.rng_hash().inc_price());
3268 let InsertOk { updates, replaced_tx, .. } =
3269 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
3270 assert!(updates.is_empty());
3271 let replaced = replaced_tx.unwrap();
3272 assert_eq!(replaced.0.hash(), first.hash());
3273
3274 assert!(!pool.contains(first.hash()));
3276 assert!(pool.contains(replacement.hash()));
3277 assert_eq!(pool.len(), 1);
3278 }
3279
3280 #[test]
3281 fn insert_replace_txpool() {
3282 let on_chain_balance = U256::ZERO;
3283 let on_chain_nonce = 0;
3284 let mut f = MockTransactionFactory::default();
3285 let mut pool = TxPool::mock();
3286
3287 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3288 let first = f.validated(tx.clone());
3289 let first_added =
3290 pool.add_transaction(first, on_chain_balance, on_chain_nonce, None).unwrap();
3291 let replacement = f.validated(tx.rng_hash().inc_price());
3292 let replacement_added = pool
3293 .add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce, None)
3294 .unwrap();
3295
3296 assert!(!pool.contains(first_added.hash()));
3298 assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));
3300
3301 assert!(pool.contains(replacement.hash()));
3302 let size = pool.size();
3303 assert_eq!(size.total, 1);
3304 size.assert_invariants();
3305 }
3306
3307 #[test]
3308 fn insert_replace_underpriced() {
3309 let on_chain_balance = U256::ZERO;
3310 let on_chain_nonce = 0;
3311 let mut f = MockTransactionFactory::default();
3312 let mut pool = AllTransactions::default();
3313 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3314 let first = f.validated(tx.clone());
3315 let _res = pool.insert_tx(first, on_chain_balance, on_chain_nonce);
3316 let mut replacement = f.validated(tx.rng_hash());
3317 replacement.transaction = replacement.transaction.decr_price();
3318 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3319 assert!(matches!(err, InsertErr::Underpriced { .. }));
3320 }
3321
3322 #[test]
3323 fn insert_replace_underpriced_not_enough_bump() {
3324 let on_chain_balance = U256::ZERO;
3325 let on_chain_nonce = 0;
3326 let mut f = MockTransactionFactory::default();
3327 let mut pool = AllTransactions::default();
3328 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3329 tx.set_priority_fee(100);
3330 tx.set_max_fee(100);
3331 let first = f.validated(tx.clone());
3332 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3333 let mut replacement = f.validated(tx.rng_hash().inc_price());
3334
3335 replacement.transaction.set_priority_fee(109);
3337 replacement.transaction.set_max_fee(109);
3338 let err =
3339 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3340 assert!(matches!(err, InsertErr::Underpriced { .. }));
3341 assert!(pool.contains(first.hash()));
3343 assert_eq!(pool.len(), 1);
3344
3345 replacement.transaction.set_priority_fee(110);
3347 replacement.transaction.set_max_fee(109);
3348 let err =
3349 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3350 assert!(matches!(err, InsertErr::Underpriced { .. }));
3351 assert!(pool.contains(first.hash()));
3352 assert_eq!(pool.len(), 1);
3353
3354 replacement.transaction.set_priority_fee(109);
3356 replacement.transaction.set_max_fee(110);
3357 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3358 assert!(matches!(err, InsertErr::Underpriced { .. }));
3359 assert!(pool.contains(first.hash()));
3360 assert_eq!(pool.len(), 1);
3361 }
3362
3363 #[test]
3364 fn insert_replace_underpriced_rounds_up_minimum_bump() {
3365 let on_chain_balance = U256::ZERO;
3366 let on_chain_nonce = 0;
3367 let mut f = MockTransactionFactory::default();
3368 let mut pool = AllTransactions { minimal_protocol_basefee: 0, ..Default::default() };
3369 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3370 tx.set_priority_fee(1);
3371 tx.set_max_fee(1);
3372
3373 let first = f.validated(tx.clone());
3374 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3375
3376 let mut replacement = f.validated(tx.rng_hash().inc_price());
3377 replacement.transaction.set_priority_fee(1);
3378 replacement.transaction.set_max_fee(2);
3379 let err =
3380 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3381 assert!(matches!(err, InsertErr::Underpriced { .. }));
3382 assert!(pool.contains(first.hash()));
3383 assert_eq!(pool.len(), 1);
3384
3385 replacement.transaction.set_priority_fee(2);
3386 replacement.transaction.set_max_fee(2);
3387 let replaced = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap();
3388 assert!(replaced.replaced_tx.is_some());
3389 assert_eq!(pool.len(), 1);
3390 }
3391
3392 #[test]
3393 fn insert_conflicting_type_normal_to_blob() {
3394 let on_chain_balance = U256::from(10_000);
3395 let on_chain_nonce = 0;
3396 let mut f = MockTransactionFactory::default();
3397 let mut pool = AllTransactions::default();
3398 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3399 let first = f.validated(tx.clone());
3400 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3401 let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3402 let blob = f.validated(tx);
3403 let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
3404 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3405 }
3406
3407 #[test]
3408 fn insert_conflicting_type_blob_to_normal() {
3409 let on_chain_balance = U256::from(10_000);
3410 let on_chain_nonce = 0;
3411 let mut f = MockTransactionFactory::default();
3412 let mut pool = AllTransactions::default();
3413 let tx = MockTransaction::eip4844().inc_price().inc_limit();
3414 let first = f.validated(tx.clone());
3415 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3416 let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3417 let tx = f.validated(tx);
3418 let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
3419 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3420 }
3421
3422 #[test]
3424 fn insert_previous() {
3425 let on_chain_balance = U256::ZERO;
3426 let on_chain_nonce = 0;
3427 let mut f = MockTransactionFactory::default();
3428 let mut pool = AllTransactions::default();
3429 let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
3430 let first = f.validated(tx.clone());
3431 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3432
3433 let first_in_pool = pool.get(first.id()).unwrap();
3434
3435 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3437
3438 let prev = f.validated(tx.prev());
3439 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3440 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3441
3442 assert!(updates.is_empty());
3444 assert!(replaced_tx.is_none());
3445 assert!(state.contains(TxState::NO_NONCE_GAPS));
3446 assert_eq!(move_to, SubPool::Queued);
3447
3448 let first_in_pool = pool.get(first.id()).unwrap();
3449 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3451 }
3452
3453 #[test]
3455 fn insert_with_updates() {
3456 let on_chain_balance = U256::from(10_000);
3457 let on_chain_nonce = 0;
3458 let mut f = MockTransactionFactory::default();
3459 let mut pool = AllTransactions::default();
3460 let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
3461 let first = f.validated(tx.clone());
3462 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3463
3464 let first_in_pool = pool.get(first.id()).unwrap();
3465 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3467 assert_eq!(SubPool::Queued, first_in_pool.subpool);
3468
3469 let prev = f.validated(tx.prev());
3470 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3471 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3472
3473 assert_eq!(updates.len(), 1);
3475 assert!(replaced_tx.is_none());
3476 assert!(state.contains(TxState::NO_NONCE_GAPS));
3477 assert_eq!(move_to, SubPool::Pending);
3478
3479 let first_in_pool = pool.get(first.id()).unwrap();
3480 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3482 assert_eq!(SubPool::Pending, first_in_pool.subpool);
3483 }
3484
3485 #[test]
3486 fn insert_previous_blocking() {
3487 let on_chain_balance = U256::from(1_000);
3488 let on_chain_nonce = 0;
3489 let mut f = MockTransactionFactory::default();
3490 let mut pool = AllTransactions::default();
3491 pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
3492 let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
3493 let first = f.validated(tx.clone());
3494
3495 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3496
3497 let first_in_pool = pool.get(first.id()).unwrap();
3498
3499 assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
3500 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3502
3503 let prev = f.validated(tx.prev());
3504 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3505 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3506
3507 assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3508 assert!(updates.is_empty());
3510 assert!(replaced_tx.is_none());
3511 assert!(state.contains(TxState::NO_NONCE_GAPS));
3512 assert_eq!(move_to, SubPool::BaseFee);
3513
3514 let first_in_pool = pool.get(first.id()).unwrap();
3515 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3517 }
3518
3519 #[test]
3520 fn rejects_spammer() {
3521 let on_chain_balance = U256::from(1_000);
3522 let on_chain_nonce = 0;
3523 let mut f = MockTransactionFactory::default();
3524 let mut pool = AllTransactions::default();
3525
3526 let mut tx = MockTransaction::eip1559();
3527 let unblocked_tx = tx.clone();
3528 for _ in 0..pool.max_account_slots {
3529 tx = tx.next();
3530 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
3531 }
3532
3533 assert_eq!(
3534 pool.max_account_slots,
3535 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3536 );
3537
3538 let err =
3539 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
3540 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
3541
3542 assert!(pool
3543 .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
3544 .is_ok());
3545 }
3546
3547 #[test]
3548 fn replace_at_sender_capacity() {
3549 let on_chain_balance = U256::from(1_000);
3550 let on_chain_nonce = 0;
3551 let mut f = MockTransactionFactory::default();
3552 let mut pool = AllTransactions::default();
3553
3554 let mut tx = MockTransaction::eip1559().with_nonce(on_chain_nonce);
3555 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
3556 for _ in 1..pool.max_account_slots {
3557 tx = tx.next();
3558 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
3559 }
3560 let sender = f.ids.sender_id(tx.get_sender()).unwrap();
3561 assert_eq!(pool.tx_count(sender), pool.max_account_slots);
3562
3563 let replacement = f.validated(tx.clone().rng_hash().inc_price_by(tx.get_gas_price()));
3565 let InsertOk { replaced_tx, .. } =
3566 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
3567 assert_eq!(replaced_tx.unwrap().0.hash(), tx.hash());
3568 assert!(!pool.contains(tx.hash()));
3569 assert!(pool.contains(replacement.hash()));
3570 assert_eq!(pool.tx_count(sender), pool.max_account_slots);
3571
3572 let underpriced = f.validated(tx.clone().rng_hash());
3574 let err = pool.insert_tx(underpriced, on_chain_balance, on_chain_nonce).unwrap_err();
3575 assert!(matches!(err, InsertErr::Underpriced { .. }));
3576
3577 let err =
3579 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
3580 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
3581 }
3582
3583 #[test]
3584 fn allow_local_spamming() {
3585 let on_chain_balance = U256::from(1_000);
3586 let on_chain_nonce = 0;
3587 let mut f = MockTransactionFactory::default();
3588 let mut pool = AllTransactions::default();
3589
3590 let mut tx = MockTransaction::eip1559();
3591 for _ in 0..pool.max_account_slots {
3592 tx = tx.next();
3593 pool.insert_tx(
3594 f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
3595 on_chain_balance,
3596 on_chain_nonce,
3597 )
3598 .unwrap();
3599 }
3600
3601 assert_eq!(
3602 pool.max_account_slots,
3603 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3604 );
3605
3606 pool.insert_tx(
3607 f.validated_with_origin(TransactionOrigin::Local, tx.next()),
3608 on_chain_balance,
3609 on_chain_nonce,
3610 )
3611 .unwrap();
3612 }
3613
3614 #[test]
3615 fn reject_tx_over_gas_limit() {
3616 let on_chain_balance = U256::from(1_000);
3617 let on_chain_nonce = 0;
3618 let mut f = MockTransactionFactory::default();
3619 let mut pool = AllTransactions::default();
3620
3621 let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
3622
3623 assert!(matches!(
3624 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
3625 Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
3626 ));
3627 }
3628
3629 #[test]
3630 fn test_tx_equal_gas_limit() {
3631 let on_chain_balance = U256::from(1_000);
3632 let on_chain_nonce = 0;
3633 let mut f = MockTransactionFactory::default();
3634 let mut pool = AllTransactions::default();
3635
3636 let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
3637
3638 let InsertOk { state, .. } =
3639 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
3640 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
3641 }
3642
3643 #[test]
3644 fn full_update_sender_heuristic() {
3645 let mut pool = AllTransactions::<MockTransaction>::default();
3646 for sender in 0..(FULL_UPDATE_MIN_SENDERS - 1) as u64 {
3647 pool.tx_counter.insert(sender.into(), 1);
3648 }
3649
3650 assert!(!pool.should_update_all_senders(250));
3652 assert!(pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS - 1));
3653
3654 pool.tx_counter.insert((FULL_UPDATE_MIN_SENDERS as u64 - 1).into(), 1);
3655
3656 assert!(
3658 !pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS / FULL_UPDATE_SENDER_RATIO - 1)
3659 );
3660 assert!(pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS / FULL_UPDATE_SENDER_RATIO));
3661 assert!(pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS + 1));
3662 }
3663
3664 #[test]
3665 fn changed_sender_update_matches_full_update() {
3666 let senders = [
3667 Address::with_last_byte(0x0a),
3668 Address::with_last_byte(0x0b),
3669 Address::with_last_byte(0x0c),
3670 ];
3671 let starting_nonces = [5, 11, 17];
3672
3673 let build_pool = || {
3674 let mut f = MockTransactionFactory::default();
3675 let mut pool = AllTransactions::default();
3676 pool.pending_fees.base_fee = 1;
3677
3678 for (sender, starting_nonce) in senders.into_iter().zip(starting_nonces) {
3679 for nonce in starting_nonce..starting_nonce + 3 {
3680 let tx = MockTransaction::eip1559()
3681 .with_sender(sender)
3682 .with_nonce(nonce)
3683 .inc_price_by(10)
3684 .rng_hash();
3685 pool.insert_tx(f.validated(tx), U256::from(1_000_000), starting_nonce).unwrap();
3686 }
3687 }
3688
3689 pool.update(&Default::default());
3691 let sender_ids = senders.map(|sender| f.ids.sender_id(&sender).unwrap());
3692 (pool, sender_ids)
3693 };
3694
3695 let (mut changed_senders_only, sender_ids) = build_pool();
3696 let (mut full_update, full_update_sender_ids) = build_pool();
3697 assert_eq!(sender_ids, full_update_sender_ids);
3698
3699 let mut changed = FxHashMap::default();
3700 changed.insert(
3701 sender_ids[0],
3702 SenderInfo { state_nonce: starting_nonces[0] + 1, balance: U256::from(1_000_000) },
3703 );
3704 changed.insert(
3705 sender_ids[1],
3706 SenderInfo { state_nonce: starting_nonces[1], balance: U256::ZERO },
3707 );
3708
3709 let mut changed_sender_updates = changed_senders_only.update(&changed);
3710 full_update.last_full_update_fees.base_fee =
3712 full_update.last_full_update_fees.base_fee.saturating_add(1);
3713 let mut full_updates = full_update.update(&changed);
3714
3715 let update_key = |update: &PoolUpdate| {
3716 let destination = match &update.destination {
3717 Destination::Discard => None,
3718 Destination::Pool(pool) => Some(*pool),
3719 };
3720 (update.id, update.current, destination)
3721 };
3722 changed_sender_updates.sort_unstable_by_key(|update| update.id);
3723 full_updates.sort_unstable_by_key(|update| update.id);
3724 assert_eq!(
3725 changed_sender_updates.iter().map(update_key).collect::<Vec<_>>(),
3726 full_updates.iter().map(update_key).collect::<Vec<_>>()
3727 );
3728
3729 let metadata = |pool: &AllTransactions<MockTransaction>| {
3730 pool.txs
3731 .iter()
3732 .map(|(id, tx)| (*id, tx.state, tx.subpool, tx.cumulative_cost))
3733 .collect::<Vec<_>>()
3734 };
3735 assert_eq!(metadata(&changed_senders_only), metadata(&full_update));
3736 }
3737
3738 #[test]
3739 fn update_visits_every_sender_when_the_base_fee_moved() {
3740 let mut f = MockTransactionFactory::default();
3741 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3742
3743 let tx = MockTransaction::eip1559().inc_price_by(10);
3744 let validated = f.validated(tx.clone());
3745 let id = *validated.id();
3746 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3747
3748 pool.all_transactions.update(&Default::default());
3749 assert!(pool
3750 .all_transactions
3751 .txs
3752 .get(&id)
3753 .unwrap()
3754 .state
3755 .contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3756
3757 pool.all_transactions.pending_fees.base_fee = (tx.max_fee_per_gas() + 1) as u64;
3760 pool.all_transactions.update(&Default::default());
3761
3762 assert!(
3763 !pool
3764 .all_transactions
3765 .txs
3766 .get(&id)
3767 .unwrap()
3768 .state
3769 .contains(TxState::ENOUGH_FEE_CAP_BLOCK),
3770 "fee change was not applied to an unchanged sender"
3771 );
3772 }
3773
3774 #[test]
3775 fn blob_fee_change_records_full_update() {
3776 let mut pool = AllTransactions::<MockTransaction>::default();
3777 pool.pending_fees.blob_fee += 1;
3778
3779 pool.update(&Default::default());
3780
3781 assert_eq!(pool.last_full_update_fees, pool.pending_fees);
3782 }
3783
3784 #[test]
3785 fn gap_fill_rechecks_descendant_fee_eligibility() {
3786 let mut f = MockTransactionFactory::default();
3787 let mut pool = AllTransactions::default();
3788 let sender = Address::with_last_byte(0x0d);
3789 let balance = U256::MAX;
3790
3791 pool.pending_fees.base_fee = 100;
3792 let descendant = MockTransaction::eip1559()
3793 .with_sender(sender)
3794 .with_nonce(1)
3795 .with_gas_limit(21_000)
3796 .with_max_fee(150)
3797 .with_priority_fee(1)
3798 .rng_hash();
3799 let descendant = f.validated(descendant);
3800 let descendant_id = *descendant.id();
3801 pool.insert_tx(descendant, balance, 0).unwrap();
3802 pool.update(&Default::default());
3803
3804 pool.pending_fees.base_fee = 200;
3807 pool.update(&Default::default());
3808 assert!(pool.get(&descendant_id).unwrap().state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3809
3810 let predecessor = MockTransaction::eip1559()
3811 .with_sender(sender)
3812 .with_nonce(0)
3813 .with_gas_limit(21_000)
3814 .with_max_fee(250)
3815 .with_priority_fee(1)
3816 .rng_hash();
3817 let InsertOk { move_to, .. } =
3818 pool.insert_tx(f.validated(predecessor), balance, 0).unwrap();
3819
3820 assert_eq!(move_to, SubPool::Pending);
3821 let descendant = pool.get(&descendant_id).unwrap();
3822 assert_eq!(descendant.subpool, SubPool::BaseFee);
3823 assert!(!descendant.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3824 assert!(descendant.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3825 }
3826
3827 #[test]
3828 fn base_fee_update_unparks_all_descendants() {
3829 let mut f = MockTransactionFactory::default();
3830 let mut pool = AllTransactions::default();
3831 let sender = Address::with_last_byte(0x0e);
3832 let mut ids = Vec::new();
3833
3834 pool.pending_fees.base_fee = 200;
3835 for nonce in 0..3 {
3836 let tx = MockTransaction::eip1559()
3837 .with_sender(sender)
3838 .with_nonce(nonce)
3839 .with_gas_limit(21_000)
3840 .with_max_fee(150)
3841 .with_priority_fee(1)
3842 .rng_hash();
3843 let tx = f.validated(tx);
3844 ids.push(*tx.id());
3845 pool.insert_tx(tx, U256::MAX, 0).unwrap();
3846 }
3847
3848 pool.pending_fees.base_fee = 100;
3849 pool.update(&Default::default());
3850
3851 for id in ids {
3852 assert_eq!(pool.get(&id).unwrap().subpool, SubPool::Pending);
3853 }
3854 }
3855
3856 #[test]
3857 fn fee_update_keeps_descendants_of_underpriced_transaction_parked() {
3858 let mut f = MockTransactionFactory::default();
3859 let mut pool = AllTransactions::default();
3860 let sender = Address::with_last_byte(0x10);
3861 let fee_caps = [150, 50, 150];
3862 let mut ids = Vec::new();
3863
3864 pool.pending_fees.base_fee = 200;
3865 for (nonce, fee_cap) in fee_caps.into_iter().enumerate() {
3866 let tx = MockTransaction::eip1559()
3867 .with_sender(sender)
3868 .with_nonce(nonce as u64)
3869 .with_gas_limit(21_000)
3870 .with_max_fee(fee_cap)
3871 .with_priority_fee(1)
3872 .rng_hash();
3873 let tx = f.validated(tx);
3874 ids.push(*tx.id());
3875 pool.insert_tx(tx, U256::MAX, 0).unwrap();
3876 }
3877
3878 pool.pending_fees.base_fee = 100;
3879 pool.update(&Default::default());
3880
3881 assert_eq!(pool.get(&ids[0]).unwrap().subpool, SubPool::Pending);
3882 assert_eq!(pool.get(&ids[1]).unwrap().subpool, SubPool::BaseFee);
3883 assert_eq!(pool.get(&ids[2]).unwrap().subpool, SubPool::Queued);
3884 }
3885
3886 #[test]
3887 fn blob_fee_update_unparks_all_descendants() {
3888 let mut f = MockTransactionFactory::default();
3889 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3890 let sender = Address::with_last_byte(0x0f);
3891 let mut block_info = pool.block_info();
3892 block_info.pending_blob_fee = Some(200);
3893 pool.set_block_info(block_info);
3894
3895 for nonce in 0..3 {
3896 let tx = MockTransaction::eip4844()
3897 .with_sender(sender)
3898 .with_nonce(nonce)
3899 .with_gas_limit(21_000)
3900 .with_max_fee(1_000)
3901 .with_priority_fee(1)
3902 .with_blob_fee(150)
3903 .rng_hash();
3904 pool.add_transaction(f.validated(tx), U256::MAX, 0, None).unwrap();
3905 }
3906 assert_eq!(pool.blob_pool.len(), 3);
3907
3908 block_info.pending_blob_fee = Some(100);
3909 pool.on_canonical_state_change(
3910 block_info,
3911 Vec::new(),
3912 FxHashMap::default(),
3913 PoolUpdateKind::Commit,
3914 );
3915
3916 let nonces = pool.best_transactions().map(|tx| tx.nonce()).collect::<Vec<_>>();
3917 assert_eq!(nonces, vec![0, 1, 2]);
3918 }
3919
3920 #[test]
3921 fn update_basefee_subpools() {
3922 let mut f = MockTransactionFactory::default();
3923 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3924
3925 let tx = MockTransaction::eip1559().inc_price_by(10);
3926 let validated = f.validated(tx.clone());
3927 let id = *validated.id();
3928 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3929
3930 assert_eq!(pool.pending_pool.len(), 1);
3931
3932 pool.update_basefee((tx.max_fee_per_gas() + 1) as u64, |_| {});
3933
3934 assert!(pool.pending_pool.is_empty());
3935 assert_eq!(pool.basefee_pool.len(), 1);
3936
3937 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3938 }
3939
3940 #[test]
3941 fn update_basefee_subpools_setting_block_info() {
3942 let mut f = MockTransactionFactory::default();
3943 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3944
3945 let tx = MockTransaction::eip1559().inc_price_by(10);
3946 let validated = f.validated(tx.clone());
3947 let id = *validated.id();
3948 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3949
3950 assert_eq!(pool.pending_pool.len(), 1);
3951
3952 let mut block_info = pool.block_info();
3954 block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
3955 pool.set_block_info(block_info);
3956
3957 assert!(pool.pending_pool.is_empty());
3958 assert_eq!(pool.basefee_pool.len(), 1);
3959
3960 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3961 }
3962
3963 #[test]
3964 fn basefee_decrease_promotes_affordable_and_keeps_unaffordable() {
3965 let mut f = MockTransactionFactory::default();
3966 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3967
3968 let sender_a = Address::with_last_byte(0x0a);
3971 let sender_b = Address::with_last_byte(0x0b);
3972 let sender_c = Address::with_last_byte(0x0c);
3973
3974 let tx1 = MockTransaction::eip1559()
3975 .set_sender(sender_a)
3976 .set_nonce(0)
3977 .set_max_fee(500)
3978 .inc_limit();
3979 let tx2 = MockTransaction::eip1559()
3980 .set_sender(sender_b)
3981 .set_nonce(0)
3982 .set_max_fee(600)
3983 .inc_limit();
3984 let tx3 = MockTransaction::eip1559()
3985 .set_sender(sender_c)
3986 .set_nonce(0)
3987 .set_max_fee(400)
3988 .inc_limit();
3989
3990 let mut block_info = pool.block_info();
3992 block_info.pending_basefee = 700;
3993 pool.set_block_info(block_info);
3994
3995 let validated1 = f.validated(tx1);
3996 let validated2 = f.validated(tx2);
3997 let validated3 = f.validated(tx3);
3998 let id1 = *validated1.id();
3999 let id2 = *validated2.id();
4000 let id3 = *validated3.id();
4001
4002 pool.add_transaction(validated1, U256::from(10_000), 0, None).unwrap();
4006 pool.add_transaction(validated2, U256::from(10_000), 0, None).unwrap();
4007 pool.add_transaction(validated3, U256::from(10_000), 0, None).unwrap();
4008
4009 println!("Basefee pool len: {}", pool.basefee_pool.len());
4011 println!("Pending pool len: {}", pool.pending_pool.len());
4012 println!("tx1 subpool: {:?}", pool.all_transactions.txs.get(&id1).unwrap().subpool);
4013 println!("tx2 subpool: {:?}", pool.all_transactions.txs.get(&id2).unwrap().subpool);
4014 println!("tx3 subpool: {:?}", pool.all_transactions.txs.get(&id3).unwrap().subpool);
4015
4016 assert_eq!(pool.basefee_pool.len(), 3);
4018 assert_eq!(pool.pending_pool.len(), 0);
4019 assert_eq!(pool.all_transactions.txs.get(&id1).unwrap().subpool, SubPool::BaseFee);
4020 assert_eq!(pool.all_transactions.txs.get(&id2).unwrap().subpool, SubPool::BaseFee);
4021 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
4022
4023 let mut block_info = pool.block_info();
4025 block_info.pending_basefee = 450; pool.set_block_info(block_info);
4028
4029 assert_eq!(pool.basefee_pool.len(), 1);
4034 assert_eq!(pool.pending_pool.len(), 2);
4035
4036 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
4038
4039 let tx1_meta = pool.all_transactions.txs.get(&id1).unwrap();
4041 let tx2_meta = pool.all_transactions.txs.get(&id2).unwrap();
4042 assert_eq!(tx1_meta.subpool, SubPool::Pending);
4043 assert_eq!(tx2_meta.subpool, SubPool::Pending);
4044 assert!(tx1_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4045 assert!(tx2_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4046
4047 let best: Vec<_> = pool.best_transactions().take(3).collect();
4049 assert_eq!(best.len(), 2); assert!(best.iter().any(|tx| tx.id() == &id1));
4051 assert!(best.iter().any(|tx| tx.id() == &id2));
4052 }
4053
4054 #[test]
4055 fn apply_fee_updates_records_promotions_after_basefee_drop() {
4056 let mut f = MockTransactionFactory::default();
4057 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4058
4059 let tx = MockTransaction::eip1559()
4060 .with_gas_limit(21_000)
4061 .with_max_fee(500)
4062 .with_priority_fee(1);
4063 let validated = f.validated(tx);
4064 let id = *validated.id();
4065 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4066
4067 assert_eq!(pool.pending_pool.len(), 1);
4068
4069 pool.update_basefee(600, |_| {});
4071 assert!(pool.pending_pool.is_empty());
4072 assert_eq!(pool.basefee_pool.len(), 1);
4073
4074 let prev_base_fee = 600;
4075 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4076
4077 pool.all_transactions.pending_fees.base_fee = 400;
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.basefee_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, 400);
4088 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
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_FEE_CAP_BLOCK));
4093 }
4094
4095 #[test]
4096 fn update_blob_fee_demotes_and_promotes_when_base_fee_falls() {
4097 let mut f = MockTransactionFactory::default();
4098 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4099
4100 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4101 let initial_base_fee = 600;
4102 pool.all_transactions.pending_fees.base_fee = initial_base_fee;
4103
4104 let tx_to_demote = MockTransaction::eip4844()
4105 .with_max_fee(700)
4106 .with_priority_fee(1)
4107 .with_blob_fee(initial_blob_fee + 100);
4108 let validated = f.validated(tx_to_demote);
4109 let demoted_id = *validated.id();
4110 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4111
4112 let tx_to_promote = MockTransaction::eip4844()
4113 .with_max_fee(500)
4114 .with_priority_fee(1)
4115 .with_blob_fee(initial_blob_fee + 300);
4116 let validated = f.validated(tx_to_promote);
4117 let promoted_id = *validated.id();
4118 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4119
4120 assert_eq!(pool.pending_pool.len(), 1);
4121 assert_eq!(pool.blob_pool.len(), 1);
4122
4123 let raised_blob_fee = initial_blob_fee + 200;
4126 pool.all_transactions.pending_fees.base_fee = 400;
4127 let mut promoted = Vec::new();
4128 pool.update_blob_fee(raised_blob_fee, Ordering::Less, |tx| promoted.push(*tx.id()));
4129
4130 assert_eq!(pool.pending_pool.len(), 1);
4131 assert_eq!(pool.blob_pool.len(), 1);
4132 assert_eq!(promoted, vec![promoted_id]);
4133
4134 assert_eq!(pool.all_transactions.txs.get(&demoted_id).unwrap().subpool, SubPool::Blob);
4135 assert_eq!(pool.all_transactions.txs.get(&promoted_id).unwrap().subpool, SubPool::Pending);
4136 }
4137
4138 #[test]
4139 fn apply_fee_updates_records_promotions_after_blob_fee_drop() {
4140 let mut f = MockTransactionFactory::default();
4141 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4142
4143 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4144
4145 let tx = MockTransaction::eip4844().with_blob_fee(initial_blob_fee + 100);
4146 let validated = f.validated(tx.clone());
4147 let id = *validated.id();
4148 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4149
4150 assert_eq!(pool.pending_pool.len(), 1);
4151
4152 let increased_blob_fee = tx.max_fee_per_blob_gas().unwrap() + 200;
4154 pool.update_blob_fee(increased_blob_fee, Ordering::Equal, |_| {});
4155 assert!(pool.pending_pool.is_empty());
4156 assert_eq!(pool.blob_pool.len(), 1);
4157
4158 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
4159 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4160
4161 pool.all_transactions.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
4163
4164 let mut outcome = UpdateOutcome::default();
4165 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
4166
4167 assert_eq!(pool.pending_pool.len(), 1);
4168 assert!(pool.blob_pool.is_empty());
4169 assert_eq!(outcome.promoted.len(), 1);
4170 assert_eq!(outcome.promoted[0].id(), &id);
4171 assert_eq!(pool.all_transactions.pending_fees.base_fee, prev_base_fee);
4172 assert_eq!(pool.all_transactions.pending_fees.blob_fee, tx.max_fee_per_blob_gas().unwrap());
4173
4174 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
4175 assert_eq!(tx_meta.subpool, SubPool::Pending);
4176 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4177 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4178 }
4179
4180 #[test]
4181 fn apply_fee_updates_promotes_blob_after_basefee_drop() {
4182 let mut f = MockTransactionFactory::default();
4183 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4184
4185 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4186
4187 let tx = MockTransaction::eip4844()
4188 .with_max_fee(500)
4189 .with_priority_fee(1)
4190 .with_blob_fee(initial_blob_fee + 100);
4191 let validated = f.validated(tx);
4192 let id = *validated.id();
4193 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4194
4195 assert_eq!(pool.pending_pool.len(), 1);
4196
4197 let high_base_fee = 600;
4199 pool.update_basefee(high_base_fee, |_| {});
4200 assert!(pool.pending_pool.is_empty());
4201 assert_eq!(pool.blob_pool.len(), 1);
4202
4203 let prev_base_fee = high_base_fee;
4204 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4205
4206 pool.all_transactions.pending_fees.base_fee = 400;
4208
4209 let mut outcome = UpdateOutcome::default();
4210 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
4211
4212 assert_eq!(pool.pending_pool.len(), 1);
4213 assert!(pool.blob_pool.is_empty());
4214 assert_eq!(outcome.promoted.len(), 1);
4215 assert_eq!(outcome.promoted[0].id(), &id);
4216 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
4217 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
4218
4219 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
4220 assert_eq!(tx_meta.subpool, SubPool::Pending);
4221 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4222 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4223 }
4224
4225 #[test]
4226 fn queued_transactions_include_blob_pool() {
4227 let mut f = MockTransactionFactory::default();
4228 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4229
4230 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4231 let tx = MockTransaction::eip4844()
4232 .with_max_fee(500)
4233 .with_priority_fee(1)
4234 .with_blob_fee(initial_blob_fee + 100);
4235 let validated = f.validated(tx);
4236 let id = *validated.id();
4237 let sender = validated.sender_id();
4238 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4239
4240 pool.update_basefee(600, |_| {});
4242 assert_eq!(pool.blob_pool.len(), 1);
4243
4244 let queued = pool.queued_transactions();
4245 assert_eq!(queued.len(), 1);
4246 assert_eq!(queued[0].id(), &id);
4247
4248 let by_sender = pool.queued_txs_by_sender(sender);
4249 assert_eq!(by_sender.len(), 1);
4250 assert_eq!(by_sender[0].id(), &id);
4251 }
4252
4253 #[test]
4254 fn apply_fee_updates_demotes_after_basefee_rise() {
4255 let mut f = MockTransactionFactory::default();
4256 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4257
4258 let tx = MockTransaction::eip1559()
4259 .with_gas_limit(21_000)
4260 .with_max_fee(400)
4261 .with_priority_fee(1);
4262 let validated = f.validated(tx);
4263 let id = *validated.id();
4264 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4265
4266 assert_eq!(pool.pending_pool.len(), 1);
4267
4268 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
4269 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4270
4271 let new_base_fee = prev_base_fee + 1_000;
4273 pool.all_transactions.pending_fees.base_fee = new_base_fee;
4274
4275 let mut outcome = UpdateOutcome::default();
4276 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
4277
4278 assert!(pool.pending_pool.is_empty());
4279 assert_eq!(pool.basefee_pool.len(), 1);
4280 assert!(outcome.promoted.is_empty());
4281 assert_eq!(pool.all_transactions.pending_fees.base_fee, new_base_fee);
4282 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
4283
4284 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
4285 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
4286 assert!(!tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4287 }
4288
4289 #[test]
4290 fn get_highest_transaction_by_sender_and_nonce() {
4291 let mut f = MockTransactionFactory::default();
4293 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4294
4295 let tx = MockTransaction::eip1559();
4297 pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0, None).unwrap();
4298
4299 let tx1 = tx.inc_price().next();
4301
4302 let tx1_validated = f.validated(tx1.clone());
4304 pool.add_transaction(tx1_validated, U256::from(1_000), 0, None).unwrap();
4305
4306 assert_eq!(
4308 pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
4309 Some(1)
4310 );
4311
4312 let highest_tx = pool
4314 .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
4315 .expect("Failed to retrieve highest transaction");
4316
4317 assert_eq!(highest_tx.as_ref().transaction, tx1);
4319 }
4320
4321 #[test]
4322 fn get_highest_consecutive_transaction_by_sender() {
4323 let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
4325 let mut f = MockTransactionFactory::default();
4326
4327 let sender = Address::random();
4329 let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
4330 for nonce in txs {
4331 let mut mock_tx = MockTransaction::eip1559();
4332 mock_tx.set_sender(sender);
4333 mock_tx.set_nonce(nonce);
4334
4335 let validated_tx = f.validated(mock_tx);
4336 pool.add_transaction(validated_tx, U256::from(1000), 0, None).unwrap();
4337 }
4338
4339 let sender_id = f.ids.sender_id(&sender).unwrap();
4341 let next_tx =
4342 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
4343 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
4344
4345 let next_tx =
4346 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
4347 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
4348
4349 let next_tx =
4350 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
4351 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
4352
4353 let mut info = SenderInfo::default();
4355 info.update(8, U256::ZERO);
4356 pool.all_transactions.sender_info.insert(sender_id, info);
4357 let next_tx =
4358 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
4359 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
4360 }
4361
4362 #[test]
4363 fn discard_nonce_too_low() {
4364 let mut f = MockTransactionFactory::default();
4365 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4366
4367 let tx = MockTransaction::eip1559().inc_price_by(10);
4368 let validated = f.validated(tx.clone());
4369 let id = *validated.id();
4370 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4371
4372 let next = tx.next();
4373 let validated = f.validated(next.clone());
4374 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4375
4376 assert_eq!(pool.pending_pool.len(), 2);
4377
4378 let mut changed_senders = HashMap::default();
4379 changed_senders.insert(
4380 id.sender,
4381 SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
4382 );
4383 let outcome = pool.update_accounts(changed_senders);
4384 assert_eq!(outcome.discarded.len(), 1);
4385 assert_eq!(pool.pending_pool.len(), 1);
4386 }
4387
4388 fn stale_validation_pool() -> TxPool<MockOrdering> {
4390 TxPool::new(
4391 MockOrdering::default(),
4392 PoolConfig { enforce_tracked_nonce: true, ..Default::default() },
4393 )
4394 }
4395
4396 #[test]
4397 fn stale_validation_accepted_by_default() {
4398 let mut f = MockTransactionFactory::default();
4399 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4400
4401 let template = MockTransaction::eip1559();
4402 let first = f.validated(template.clone().with_nonce(0).rng_hash());
4403 let sender = first.sender_id();
4404 pool.add_transaction(first, U256::from(1_000), 0, None).unwrap();
4405
4406 let changed = FxHashMap::from_iter([(
4407 sender,
4408 SenderInfo { state_nonce: 1, balance: U256::from(1_000) },
4409 )]);
4410 let outcome = pool.update_accounts(changed);
4411 assert_eq!(outcome.discarded.len(), 1);
4412 assert!(!pool.all_transactions.sender_info.contains_key(&sender));
4414
4415 let stale = f.validated(template.with_nonce(0).rng_hash());
4417 pool.add_transaction(stale, U256::from(1_000), 0, None).unwrap();
4418 assert_eq!(pool.pending_pool.len(), 1);
4419 assert_eq!(pool.all_transactions.sender_info[&sender].state_nonce, 0);
4420 }
4421
4422 #[test]
4423 fn stale_validation_does_not_regress_sender_state() {
4424 let mut f = MockTransactionFactory::default();
4425 let mut pool = stale_validation_pool();
4426
4427 let template = MockTransaction::eip1559();
4428 let first = f.validated(template.clone().with_nonce(0).rng_hash());
4429 let sender = first.sender_id();
4430 pool.add_transaction(first, U256::from(1_000), 0, None).unwrap();
4431
4432 let mut changed_senders = HashMap::default();
4433 changed_senders.insert(sender, SenderInfo { state_nonce: 1, balance: U256::from(1_000) });
4434 let outcome = pool.update_accounts(changed_senders);
4435 assert_eq!(outcome.discarded.len(), 1);
4436 assert_eq!(pool.all_transactions.sender_info[&sender].state_nonce, 1);
4437
4438 let current = f.validated(template.clone().with_nonce(1).rng_hash());
4440 pool.add_transaction(current, U256::from(1_000), 0, None).unwrap();
4441 assert_eq!(pool.pending_pool.len(), 1);
4442 assert_eq!(pool.queued_pool.len(), 0);
4443 assert_eq!(pool.all_transactions.sender_info[&sender].state_nonce, 1);
4444
4445 let stale = f.validated(template.with_nonce(0).rng_hash());
4446 let stale_hash = *stale.hash();
4447 let err = pool.add_transaction(stale, U256::from(1_000), 0, None).unwrap_err();
4448 assert!(matches!(
4449 err.kind,
4450 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Consensus(
4451 InvalidTransactionError::NonceNotConsistent { tx: 0, state: 1 }
4452 ))
4453 ));
4454 assert!(!pool.contains(&stale_hash));
4455 assert_eq!(pool.all_transactions.sender_info[&sender].state_nonce, 1);
4456 }
4457
4458 #[test]
4459 fn stale_validation_resubmission_after_commit() {
4460 let mut f = MockTransactionFactory::default();
4461 let mut pool = stale_validation_pool();
4462 let block_info = pool.block_info();
4463
4464 let template = MockTransaction::eip1559();
4465 let tx0 = template.clone().with_nonce(0).rng_hash();
4466 let tx1 = template.clone().with_nonce(1).rng_hash();
4467 let first = f.validated(tx0.clone());
4468 let sender = first.sender_id();
4469 pool.add_transaction(first, U256::from(1_000), 0, None).unwrap();
4470 pool.add_transaction(f.validated(tx1.clone()), U256::from(1_000), 0, None).unwrap();
4471 assert_eq!(pool.pending_pool.len(), 2);
4472
4473 let changed = FxHashMap::from_iter([(
4475 sender,
4476 SenderInfo { state_nonce: 1, balance: U256::from(1_000) },
4477 )]);
4478 pool.on_canonical_state_change(
4479 block_info,
4480 vec![*tx0.get_hash()],
4481 changed,
4482 PoolUpdateKind::Commit,
4483 );
4484 assert_eq!(pool.pending_pool.len(), 1);
4485
4486 let retry = f.validated(template.with_nonce(0).rng_hash());
4488 let err = pool.add_transaction(retry, U256::from(1_000), 0, None).unwrap_err();
4489 assert!(matches!(
4490 err.kind,
4491 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Consensus(
4492 InvalidTransactionError::NonceNotConsistent { tx: 0, state: 1 }
4493 ))
4494 ));
4495
4496 let replacement = f.validated(tx1.inc_price_by(10).rng_hash());
4498 let added = pool.add_transaction(replacement, U256::from(1_000), 0, None).unwrap();
4499 assert!(added.as_pending().is_some());
4500 assert!(added.replaced().is_some());
4501 assert_eq!(pool.pending_pool.len(), 1);
4502 assert_eq!(pool.all_transactions.sender_info[&sender].state_nonce, 1);
4503 let next =
4504 pool.get_highest_consecutive_transaction_by_sender(sender.into_transaction_id(1));
4505 assert_eq!(next.map(|tx| tx.nonce()), Some(1));
4506 }
4507
4508 #[test]
4509 fn stale_validation_uses_tracked_balance() {
4510 let mut f = MockTransactionFactory::default();
4511 let mut pool = stale_validation_pool();
4512
4513 let template = MockTransaction::eip1559().with_gas_price(50).with_gas_limit(30_000);
4515 let first = f.validated(template.clone().with_nonce(0).rng_hash());
4516 let sender = first.sender_id();
4517 pool.add_transaction(first, U256::from(10_000_000), 0, None).unwrap();
4518 assert_eq!(pool.pending_pool.len(), 1);
4519
4520 let changed = FxHashMap::from_iter([(
4522 sender,
4523 SenderInfo { state_nonce: 1, balance: U256::from(1_000) },
4524 )]);
4525 pool.update_accounts(changed);
4526
4527 let next = f.validated(template.with_nonce(1).rng_hash());
4529 let id = *next.id();
4530 pool.add_transaction(next, U256::from(10_000_000), 0, None).unwrap();
4531 assert_eq!(pool.pending_pool.len(), 0);
4532 assert_eq!(pool.queued_pool.len(), 1);
4533 let state = pool.all_transactions.get(&id).unwrap().state;
4534 assert!(state.contains(TxState::NO_NONCE_GAPS));
4535 assert!(!state.contains(TxState::ENOUGH_BALANCE));
4536 assert_eq!(pool.all_transactions.sender_info[&sender].balance, U256::from(1_000));
4537 }
4538
4539 #[test]
4540 fn discard_with_large_blob_txs() {
4541 reth_tracing::init_test_tracing();
4543
4544 let mut f = MockTransactionFactory::default();
4546 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4547 let default_limits = pool.config.blob_limit;
4548
4549 let a_sender = Address::with_last_byte(0x0a);
4552
4553 let mut block_info = pool.block_info();
4555 block_info.pending_blob_fee = Some(100);
4556 block_info.pending_basefee = 100;
4557
4558 pool.set_block_info(block_info);
4560
4561 let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
4563 .into_iter()
4564 .map(|mut tx| {
4565 tx.set_size(default_limits.max_size / 2 + 1);
4566 tx.set_max_fee((block_info.pending_basefee - 1).into());
4567 tx
4568 })
4569 .collect::<Vec<_>>();
4570
4571 for tx in a_txs {
4573 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
4574 }
4575
4576 let removed = pool.discard_worst();
4578 assert_eq!(removed.len(), 1);
4579 }
4580
4581 #[test]
4582 fn discard_with_parked_large_txs() {
4583 reth_tracing::init_test_tracing();
4585
4586 let mut f = MockTransactionFactory::default();
4588 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4589 let default_limits = pool.config.queued_limit;
4590
4591 let a_sender = Address::with_last_byte(0x0a);
4594
4595 let pool_base_fee = 100;
4597 pool.update_basefee(pool_base_fee, |_| {});
4598
4599 let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
4601 .into_iter()
4602 .map(|mut tx| {
4603 tx.set_size(default_limits.max_size / 2 + 1);
4604 tx.set_max_fee((pool_base_fee - 1).into());
4605 tx
4606 })
4607 .collect::<Vec<_>>();
4608
4609 for tx in a_txs {
4611 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
4612 }
4613
4614 let removed = pool.discard_worst();
4616 assert_eq!(removed.len(), 1);
4617 }
4618
4619 #[test]
4620 fn discard_at_capacity() {
4621 let mut f = MockTransactionFactory::default();
4622 let queued_limit = SubPoolLimit::new(1000, usize::MAX);
4623 let mut pool =
4624 TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
4625
4626 for _ in 0..queued_limit.max_txs {
4628 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
4629 let validated = f.validated(tx);
4630 let _id = *validated.id();
4631 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4632 }
4633
4634 let size = pool.size();
4635 assert_eq!(size.queued, queued_limit.max_txs);
4636
4637 for _ in 0..queued_limit.max_txs {
4638 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
4639 let validated = f.validated(tx);
4640 let _id = *validated.id();
4641 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4642
4643 pool.discard_worst();
4644 pool.assert_invariants();
4645 assert!(pool.size().queued <= queued_limit.max_txs);
4646 }
4647 }
4648
4649 #[test]
4650 fn discard_blobs_at_capacity() {
4651 let mut f = MockTransactionFactory::default();
4652 let blob_limit = SubPoolLimit::new(1000, usize::MAX);
4653 let mut pool =
4654 TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
4655 pool.all_transactions.pending_fees.blob_fee = 10000;
4656 for _ in 0..blob_limit.max_txs {
4658 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
4659 let validated = f.validated(tx);
4660 let _id = *validated.id();
4661 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4662 }
4663
4664 let size = pool.size();
4665 assert_eq!(size.blob, blob_limit.max_txs);
4666
4667 for _ in 0..blob_limit.max_txs {
4668 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
4669 let validated = f.validated(tx);
4670 let _id = *validated.id();
4671 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4672
4673 pool.discard_worst();
4674 pool.assert_invariants();
4675 assert!(pool.size().blob <= blob_limit.max_txs);
4676 }
4677 }
4678
4679 #[test]
4680 fn reuse_update_buffer_across_pool_operations() {
4681 let mut f = MockTransactionFactory::default();
4682 let mut pool = TxPool::new(
4683 MockOrdering::default(),
4684 PoolConfig { max_account_slots: 128, ..Default::default() },
4685 );
4686 let mut tx = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4687 let first = f.validated(tx.clone());
4688 for _ in 0..80 {
4689 pool.add_transaction(f.validated(tx.clone()), U256::MAX, 0, None).unwrap();
4690 tx = tx.next();
4691 }
4692 assert_eq!(pool.all_transactions.update_buffer.capacity(), 0);
4693
4694 let changed = |balance, state_nonce| {
4695 FxHashMap::from_iter([(first.sender_id(), SenderInfo { balance, state_nonce })])
4696 };
4697 let outcome = pool.update_accounts(changed(U256::ZERO, 0));
4698 assert!(outcome.promoted.is_empty());
4699 assert!(outcome.discarded.is_empty());
4700 assert_eq!(pool.queued_pool.len(), 80);
4701 let capacity = pool.all_transactions.update_buffer.capacity();
4702 assert!(capacity >= 80);
4703 let buffer = pool.all_transactions.update_buffer.as_ptr();
4704
4705 for _ in 0..2 {
4706 let outcome = pool.update_accounts(changed(U256::MAX, 0));
4707 assert_eq!(outcome.promoted.len(), 80);
4708 assert!(outcome.discarded.is_empty());
4709 assert_eq!(pool.pending_pool.len(), 80);
4710
4711 let outcome = pool.update_accounts(FxHashMap::default());
4712 assert!(outcome.promoted.is_empty());
4713 assert!(outcome.discarded.is_empty());
4714
4715 pool.remove_transaction_by_hash(first.hash()).unwrap();
4716 assert_eq!(pool.queued_pool.len(), 79);
4717
4718 let replacement = f.validated(first.transaction.next());
4720 assert!(pool.add_transaction(replacement, U256::MAX, 0, None).is_err());
4721 assert_eq!(pool.all_transactions.update_buffer.as_ptr(), buffer);
4722
4723 let added = pool.add_transaction(first.clone(), U256::MAX, 0, None).unwrap();
4724 let AddedTransaction::Pending(added) = added else { panic!("expected pending") };
4725 assert_eq!(added.promoted.len(), 79);
4726 assert!(added.discarded.is_empty());
4727 assert_eq!(pool.pending_pool.len(), 80);
4728
4729 pool.update_accounts(changed(U256::ZERO, 0));
4730 assert_eq!(pool.queued_pool.len(), 80);
4731 assert!(pool.all_transactions.update_buffer.is_empty());
4732 assert_eq!(pool.all_transactions.update_buffer.capacity(), capacity);
4733 assert_eq!(pool.all_transactions.update_buffer.as_ptr(), buffer);
4734 pool.assert_invariants();
4735 }
4736
4737 let outcome = pool.update_accounts(changed(U256::MAX, 80));
4738 assert_eq!(outcome.discarded.len(), 80);
4739 assert!(outcome.promoted.is_empty());
4740 assert!(pool.is_empty());
4741 assert!(pool.all_transactions.update_buffer.is_empty());
4742 assert_eq!(pool.all_transactions.update_buffer.as_ptr(), buffer);
4743 }
4744
4745 #[test]
4746 fn account_updates_sender_balance() {
4747 let mut on_chain_balance = U256::from(100);
4748 let on_chain_nonce = 0;
4749 let mut f = MockTransactionFactory::default();
4750 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4751
4752 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4753 let tx_1 = tx_0.next();
4754 let tx_2 = tx_1.next();
4755
4756 let v0 = f.validated(tx_0);
4758 let v1 = f.validated(tx_1);
4759 let v2 = f.validated(tx_2);
4760
4761 let _res =
4762 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4763 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4764 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4765
4766 assert_eq!(1, pool.pending_transactions().len());
4768 assert_eq!(2, pool.queued_transactions().len());
4769
4770 let mut updated_accounts = HashMap::default();
4772 on_chain_balance = U256::from(300);
4773 updated_accounts.insert(
4774 v0.sender_id(),
4775 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4776 );
4777 pool.update_accounts(updated_accounts.clone());
4778
4779 assert_eq!(3, pool.pending_transactions().len());
4780 assert!(pool.queued_transactions().is_empty());
4781
4782 updated_accounts.entry(v0.sender_id()).and_modify(|v| v.balance = U256::ONE);
4784 pool.update_accounts(updated_accounts);
4785
4786 assert!(pool.pending_transactions().is_empty());
4787 assert_eq!(3, pool.queued_transactions().len());
4788 }
4789
4790 #[test]
4791 fn account_updates_nonce_gap() {
4792 let on_chain_balance = U256::from(10_000);
4793 let mut on_chain_nonce = 0;
4794 let mut f = MockTransactionFactory::default();
4795 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4796
4797 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4798 let tx_1 = tx_0.next();
4799 let tx_2 = tx_1.next();
4800
4801 let v0 = f.validated(tx_0);
4803 let v1 = f.validated(tx_1);
4804 let v2 = f.validated(tx_2);
4805
4806 let _res =
4808 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4809 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4810
4811 assert!(pool.queued_transactions().is_empty());
4812 assert_eq!(2, pool.pending_transactions().len());
4813
4814 pool.remove_transaction_by_hash(v0.hash());
4816
4817 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4819
4820 assert_eq!(2, pool.queued_transactions().len());
4822 assert!(pool.pending_transactions().is_empty());
4823
4824 let mut updated_accounts = HashMap::default();
4826 on_chain_nonce += 1;
4827 updated_accounts.insert(
4828 v0.sender_id(),
4829 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4830 );
4831 pool.update_accounts(updated_accounts);
4832
4833 assert!(pool.queued_transactions().is_empty());
4835 assert_eq!(2, pool.pending_transactions().len());
4836 }
4837 #[test]
4838 fn test_transaction_removal() {
4839 let on_chain_balance = U256::from(10_000);
4840 let on_chain_nonce = 0;
4841 let mut f = MockTransactionFactory::default();
4842 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4843
4844 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4845 let tx_1 = tx_0.next();
4846
4847 let v0 = f.validated(tx_0);
4849 let v1 = f.validated(tx_1);
4850
4851 let _res =
4853 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4854 let _res =
4855 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4856
4857 assert_eq!(0, pool.queued_transactions().len());
4858 assert_eq!(2, pool.pending_transactions().len());
4859
4860 pool.remove_transaction(v0.id());
4862 let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
4864 assert_eq!(vec![v1.nonce()], pool_txs);
4865 }
4866 #[test]
4867 fn test_remove_transactions() {
4868 let on_chain_balance = U256::from(10_000);
4869 let on_chain_nonce = 0;
4870 let mut f = MockTransactionFactory::default();
4871 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4872
4873 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4874 let tx_1 = tx_0.next();
4875 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4876 let tx_3 = tx_2.next();
4877
4878 let v0 = f.validated(tx_0);
4880 let v1 = f.validated(tx_1);
4881 let v2 = f.validated(tx_2);
4882 let v3 = f.validated(tx_3);
4883
4884 let _res =
4886 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4887 let _res =
4888 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4889 let _res =
4890 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4891 let _res =
4892 pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4893
4894 assert_eq!(0, pool.queued_transactions().len());
4895 assert_eq!(4, pool.pending_transactions().len());
4896
4897 pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
4898
4899 assert_eq!(2, pool.queued_transactions().len());
4900 assert!(pool.pending_transactions().is_empty());
4901 assert!(pool.contains(v1.hash()));
4902 assert!(pool.contains(v3.hash()));
4903 }
4904
4905 #[test]
4906 fn test_remove_transactions_middle_pending_hash() {
4907 let on_chain_balance = U256::from(10_000);
4908 let on_chain_nonce = 0;
4909 let mut f = MockTransactionFactory::default();
4910 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4911
4912 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4913 let tx_1 = tx_0.next();
4914 let tx_2 = tx_1.next();
4915 let tx_3 = tx_2.next();
4916
4917 let v0 = f.validated(tx_0);
4919 let v1 = f.validated(tx_1);
4920 let v2 = f.validated(tx_2);
4921 let v3 = f.validated(tx_3);
4922
4923 let _res = pool.add_transaction(v0, on_chain_balance, on_chain_nonce, None).unwrap();
4925 let _res =
4926 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4927 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4928 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4929
4930 assert_eq!(0, pool.queued_transactions().len());
4931 assert_eq!(4, pool.pending_transactions().len());
4932
4933 let mut removed_txs = pool.remove_transactions(vec![*v1.hash()]);
4934 assert_eq!(1, removed_txs.len());
4935
4936 assert_eq!(2, pool.queued_transactions().len());
4937 assert_eq!(1, pool.pending_transactions().len());
4938
4939 let removed_tx = removed_txs.pop().unwrap();
4941 let v1 = f.validated(removed_tx.transaction.clone());
4942 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4943 assert_eq!(0, pool.queued_transactions().len());
4944 assert_eq!(4, pool.pending_transactions().len());
4945 }
4946
4947 #[test]
4948 fn test_remove_transactions_and_descendants() {
4949 let on_chain_balance = U256::from(10_000);
4950 let on_chain_nonce = 0;
4951 let mut f = MockTransactionFactory::default();
4952 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4953
4954 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4955 let tx_1 = tx_0.next();
4956 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4957 let tx_3 = tx_2.next();
4958 let tx_4 = tx_3.next();
4959
4960 let v0 = f.validated(tx_0);
4962 let v1 = f.validated(tx_1);
4963 let v2 = f.validated(tx_2);
4964 let v3 = f.validated(tx_3);
4965 let v4 = f.validated(tx_4);
4966
4967 let _res =
4969 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4970 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4971 let _res =
4972 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4973 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4974 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4975
4976 assert_eq!(0, pool.queued_transactions().len());
4977 assert_eq!(5, pool.pending_transactions().len());
4978
4979 pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
4980
4981 assert_eq!(0, pool.queued_transactions().len());
4982 assert_eq!(0, pool.pending_transactions().len());
4983 }
4984 #[test]
4985 fn test_remove_descendants() {
4986 let on_chain_balance = U256::from(10_000);
4987 let on_chain_nonce = 0;
4988 let mut f = MockTransactionFactory::default();
4989 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4990
4991 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4992 let tx_1 = tx_0.next();
4993 let tx_2 = tx_1.next();
4994 let tx_3 = tx_2.next();
4995
4996 let v0 = f.validated(tx_0);
4998 let v1 = f.validated(tx_1);
4999 let v2 = f.validated(tx_2);
5000 let v3 = f.validated(tx_3);
5001
5002 let _res =
5004 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
5005 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
5006 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
5007 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
5008
5009 assert_eq!(0, pool.queued_transactions().len());
5010 assert_eq!(4, pool.pending_transactions().len());
5011
5012 let mut removed = Vec::new();
5013 pool.remove_transaction(v0.id());
5014 pool.remove_descendants(v0.id(), &mut removed);
5015
5016 assert_eq!(0, pool.queued_transactions().len());
5017 assert_eq!(0, pool.pending_transactions().len());
5018 assert_eq!(3, removed.len());
5019 }
5020 #[test]
5021 fn test_remove_transactions_by_sender() {
5022 let on_chain_balance = U256::from(10_000);
5023 let on_chain_nonce = 0;
5024 let mut f = MockTransactionFactory::default();
5025 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5026
5027 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
5028 let tx_1 = tx_0.next();
5029 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
5030 let tx_3 = tx_2.next();
5031 let tx_4 = tx_3.next();
5032
5033 let v0 = f.validated(tx_0);
5035 let v1 = f.validated(tx_1);
5036 let v2 = f.validated(tx_2);
5037 let v3 = f.validated(tx_3);
5038 let v4 = f.validated(tx_4);
5039
5040 let _res =
5042 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
5043 let _res =
5044 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
5045 let _res =
5046 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
5047 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
5048 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
5049
5050 assert_eq!(0, pool.queued_transactions().len());
5051 assert_eq!(5, pool.pending_transactions().len());
5052
5053 pool.remove_transactions_by_sender(v2.sender_id());
5054
5055 assert_eq!(0, pool.queued_transactions().len());
5056 assert_eq!(2, pool.pending_transactions().len());
5057 assert!(pool.contains(v0.hash()));
5058 assert!(pool.contains(v1.hash()));
5059 }
5060 #[test]
5061 fn wrong_best_order_of_transactions() {
5062 let on_chain_balance = U256::from(10_000);
5063 let mut on_chain_nonce = 0;
5064 let mut f = MockTransactionFactory::default();
5065 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5066
5067 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
5068 let tx_1 = tx_0.next();
5069 let tx_2 = tx_1.next();
5070 let tx_3 = tx_2.next();
5071
5072 let v0 = f.validated(tx_0);
5074 let v1 = f.validated(tx_1);
5075 let v2 = f.validated(tx_2);
5076 let v3 = f.validated(tx_3);
5077
5078 let _res =
5080 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
5081 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
5082
5083 assert_eq!(0, pool.queued_transactions().len());
5084 assert_eq!(2, pool.pending_transactions().len());
5085
5086 pool.remove_transaction(v0.id());
5088
5089 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
5091
5092 assert_eq!(1, pool.queued_transactions().len());
5094 assert_eq!(1, pool.pending_transactions().len());
5095
5096 let mut updated_accounts = HashMap::default();
5098 on_chain_nonce += 1;
5099 updated_accounts.insert(
5100 v0.sender_id(),
5101 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
5102 );
5103 pool.update_accounts(updated_accounts);
5104
5105 assert_eq!(0, pool.queued_transactions().len());
5108 assert_eq!(2, pool.pending_transactions().len());
5109
5110 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
5112 assert_eq!(0, pool.queued_transactions().len());
5113 assert_eq!(3, pool.pending_transactions().len());
5114
5115 assert_eq!(
5118 pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
5119 vec![1, 2, 3]
5120 );
5121 }
5122
5123 #[test]
5124 fn test_best_with_attributes() {
5125 let on_chain_balance = U256::MAX;
5126 let on_chain_nonce = 0;
5127 let mut f = MockTransactionFactory::default();
5128 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5129
5130 let base_fee: u128 = 100;
5131 let blob_fee: u128 = 100;
5132
5133 let mut block_info = pool.block_info();
5135 block_info.pending_basefee = base_fee as u64;
5136 block_info.pending_blob_fee = Some(blob_fee);
5137 pool.set_block_info(block_info);
5138
5139 let tx1 = MockTransaction::eip4844()
5141 .with_sender(Address::with_last_byte(1))
5142 .with_max_fee(base_fee + 10)
5143 .with_blob_fee(blob_fee + 10);
5144 let tx2 = MockTransaction::eip4844()
5145 .with_sender(Address::with_last_byte(2))
5146 .with_max_fee(base_fee + 10)
5147 .with_blob_fee(blob_fee);
5148 let tx3 = MockTransaction::eip4844()
5149 .with_sender(Address::with_last_byte(3))
5150 .with_max_fee(base_fee)
5151 .with_blob_fee(blob_fee + 10);
5152 let tx4 = MockTransaction::eip4844()
5153 .with_sender(Address::with_last_byte(4))
5154 .with_max_fee(base_fee)
5155 .with_blob_fee(blob_fee);
5156 let tx5 = MockTransaction::eip4844()
5157 .with_sender(Address::with_last_byte(5))
5158 .with_max_fee(base_fee)
5159 .with_blob_fee(blob_fee - 10);
5160 let tx6 = MockTransaction::eip4844()
5161 .with_sender(Address::with_last_byte(6))
5162 .with_max_fee(base_fee - 10)
5163 .with_blob_fee(blob_fee);
5164 let tx7 = MockTransaction::eip4844()
5165 .with_sender(Address::with_last_byte(7))
5166 .with_max_fee(base_fee - 10)
5167 .with_blob_fee(blob_fee - 10);
5168
5169 for tx in vec![
5170 tx1.clone(),
5171 tx2.clone(),
5172 tx3.clone(),
5173 tx4.clone(),
5174 tx5.clone(),
5175 tx6.clone(),
5176 tx7.clone(),
5177 ] {
5178 pool.add_transaction(f.validated(tx), on_chain_balance, on_chain_nonce, None).unwrap();
5179 }
5180
5181 let base_fee = base_fee as u64;
5182 let blob_fee = blob_fee as u64;
5183
5184 let cases = vec![
5185 (BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee + 5)), vec![tx1.clone()]),
5187 (
5189 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee)),
5190 vec![tx1.clone(), tx2.clone()],
5191 ),
5192 (
5194 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee - 5)),
5195 vec![tx1.clone(), tx2.clone()],
5196 ),
5197 (
5199 BestTransactionsAttributes::new(base_fee, Some(blob_fee + 5)),
5200 vec![tx1.clone(), tx3.clone()],
5201 ),
5202 (
5204 BestTransactionsAttributes::new(base_fee, Some(blob_fee)),
5205 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
5206 ),
5207 (
5209 BestTransactionsAttributes::new(base_fee, Some(blob_fee - 10)),
5210 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx5.clone()],
5211 ),
5212 (
5214 BestTransactionsAttributes::new(base_fee - 5, Some(blob_fee + 5)),
5215 vec![tx1.clone(), tx3.clone()],
5216 ),
5217 (
5219 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee)),
5220 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx6.clone()],
5221 ),
5222 (
5224 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee - 10)),
5225 vec![tx1, tx2, tx5, tx3, tx4, tx6, tx7],
5226 ),
5227 ];
5228
5229 for (idx, (attribute, expected)) in cases.into_iter().enumerate() {
5230 let mut best = pool.best_transactions_with_attributes(attribute);
5231
5232 for (tx_idx, expected_tx) in expected.into_iter().enumerate() {
5233 let tx = best.next().expect("Transaction should be returned");
5234 assert_eq!(
5235 tx.transaction,
5236 expected_tx,
5237 "Failed tx {} in case {}",
5238 tx_idx + 1,
5239 idx + 1
5240 );
5241 }
5242
5243 assert!(best.next().is_none());
5245 }
5246 }
5247
5248 #[test]
5249 fn test_pending_ordering() {
5250 let mut f = MockTransactionFactory::default();
5251 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5252
5253 let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
5254 let tx_1 = tx_0.next();
5255
5256 let v0 = f.validated(tx_0);
5257 let v1 = f.validated(tx_1);
5258
5259 pool.add_transaction(v0.clone(), U256::MAX, 0, None).unwrap();
5261 assert_eq!(1, pool.queued_transactions().len());
5262
5263 pool.add_transaction(v1, U256::MAX, 1, None).unwrap();
5265
5266 assert_eq!(2, pool.pending_transactions().len());
5267 assert_eq!(0, pool.queued_transactions().len());
5268
5269 assert_eq!(
5270 pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
5271 v0.nonce()
5272 );
5273 }
5274
5275 #[test]
5277 fn one_sender_one_independent_transaction() {
5278 let mut on_chain_balance = U256::from(4_999); let mut on_chain_nonce = 40;
5280 let mut f = MockTransactionFactory::default();
5281 let mut pool = TxPool::mock();
5282 let mut submitted_txs = Vec::new();
5283
5284 let template =
5286 MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
5287
5288 for tx_nonce in 40..48 {
5291 let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
5292 submitted_txs.push(*tx.id());
5293 pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap();
5294 }
5295
5296 on_chain_balance = U256::from(999_999);
5299 on_chain_nonce = 42;
5300 pool.remove_transaction(&submitted_txs[0]);
5301 pool.remove_transaction(&submitted_txs[1]);
5302
5303 for tx_nonce in 48..52 {
5305 pool.add_transaction(
5306 f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
5307 on_chain_balance,
5308 on_chain_nonce,
5309 None,
5310 )
5311 .unwrap();
5312 }
5313
5314 let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
5315 assert_eq!(best_txs.len(), 10); assert_eq!(pool.pending_pool.independent().len(), 1);
5318 }
5319
5320 #[test]
5321 fn test_insertion_disorder() {
5322 let mut f = MockTransactionFactory::default();
5323 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5324
5325 let sender = address!("0x1234567890123456789012345678901234567890");
5326 let tx0 = f.validated_arc(
5327 MockTransaction::legacy().with_sender(sender).with_nonce(0).with_gas_price(10),
5328 );
5329 let tx1 = f.validated_arc(
5330 MockTransaction::eip1559()
5331 .with_sender(sender)
5332 .with_nonce(1)
5333 .with_gas_limit(1000)
5334 .with_gas_price(10),
5335 );
5336 let tx2 = f.validated_arc(
5337 MockTransaction::legacy().with_sender(sender).with_nonce(2).with_gas_price(10),
5338 );
5339 let tx3 = f.validated_arc(
5340 MockTransaction::legacy().with_sender(sender).with_nonce(3).with_gas_price(10),
5341 );
5342
5343 pool.add_transaction((*tx0).clone(), U256::from(1000), 0, None).unwrap();
5345 let mut best = pool.best_transactions();
5346 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5347 assert_eq!(t0.id(), tx0.id());
5348 pool.add_transaction((*tx1).clone(), U256::from(1000), 0, None).unwrap();
5350 let mut best = pool.best_transactions();
5351 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5352 assert_eq!(t0.id(), tx0.id());
5353 assert!(best.next().is_none());
5354
5355 pool.add_transaction((*tx2).clone(), U256::MAX, 0, None).unwrap();
5357
5358 let mut best = pool.best_transactions();
5359
5360 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5361 let t1 = best.next().expect("tx1 should be put in the pending subpool");
5362 let t2 = best.next().expect("tx2 should be put in the pending subpool");
5363 assert_eq!(t0.id(), tx0.id());
5364 assert_eq!(t1.id(), tx1.id());
5365 assert_eq!(t2.id(), tx2.id());
5366
5367 pool.add_transaction((*tx3).clone(), U256::MAX, 0, None).unwrap();
5369 let mut best = pool.best_transactions();
5370 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5371 let t1 = best.next().expect("tx1 should be put in the pending subpool");
5372 let t2 = best.next().expect("tx2 should be put in the pending subpool");
5373 let t3 = best.next().expect("tx3 should be put in the pending subpool");
5374 assert_eq!(t0.id(), tx0.id());
5375 assert_eq!(t1.id(), tx1.id());
5376 assert_eq!(t2.id(), tx2.id());
5377 assert_eq!(t3.id(), tx3.id());
5378 }
5379
5380 #[test]
5381 fn test_non_4844_blob_fee_bit_invariant() {
5382 let mut f = MockTransactionFactory::default();
5383 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5384
5385 let non_4844_tx = MockTransaction::eip1559().set_max_fee(200).inc_limit();
5386 let validated = f.validated(non_4844_tx.clone());
5387
5388 assert!(!non_4844_tx.is_eip4844());
5389 pool.add_transaction(validated.clone(), U256::from(10_000), 0, None).unwrap();
5390
5391 let tx_meta = pool.all_transactions.txs.get(validated.id()).unwrap();
5393 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
5394 assert_eq!(tx_meta.subpool, SubPool::Pending);
5395 }
5396
5397 #[test]
5398 fn test_blob_fee_enforcement_only_applies_to_eip4844() {
5399 let mut f = MockTransactionFactory::default();
5400 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5401
5402 let mut block_info = pool.block_info();
5404 block_info.pending_blob_fee = Some(160);
5405 block_info.pending_basefee = 100;
5406 pool.set_block_info(block_info);
5407
5408 let eip4844_tx = MockTransaction::eip4844()
5409 .with_sender(Address::with_last_byte(0x0a))
5410 .with_max_fee(200)
5411 .with_blob_fee(150) .inc_limit();
5413
5414 let non_4844_tx = MockTransaction::eip1559()
5415 .with_sender(Address::with_last_byte(0x0b))
5416 .set_max_fee(200)
5417 .inc_limit();
5418
5419 let validated_4844 = f.validated(eip4844_tx);
5420 let validated_non_4844 = f.validated(non_4844_tx);
5421
5422 pool.add_transaction(validated_4844.clone(), U256::from(10_000), 0, None).unwrap();
5423 pool.add_transaction(validated_non_4844.clone(), U256::from(10_000), 0, None).unwrap();
5424
5425 let tx_4844_meta = pool.all_transactions.txs.get(validated_4844.id()).unwrap();
5426 let tx_non_4844_meta = pool.all_transactions.txs.get(validated_non_4844.id()).unwrap();
5427
5428 assert!(!tx_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
5430 assert_eq!(tx_4844_meta.subpool, SubPool::Blob);
5431
5432 assert!(tx_non_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
5434 assert_eq!(tx_non_4844_meta.subpool, SubPool::Pending);
5435 }
5436
5437 #[test]
5438 fn test_basefee_decrease_preserves_non_4844_blob_fee_bit() {
5439 let mut f = MockTransactionFactory::default();
5440 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5441
5442 let non_4844_tx = MockTransaction::eip1559()
5444 .with_sender(Address::with_last_byte(0x0a))
5445 .set_max_fee(500) .inc_limit();
5447
5448 pool.update_basefee(600, |_| {});
5450
5451 let validated = f.validated(non_4844_tx);
5452 let tx_id = *validated.id();
5453 pool.add_transaction(validated, U256::from(10_000), 0, None).unwrap();
5454
5455 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
5457 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
5458 assert!(
5459 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
5460 "Non-4844 tx in BaseFee pool must retain ENOUGH_BLOB_FEE_CAP_BLOCK bit"
5461 );
5462
5463 pool.update_basefee(400, |_| {});
5466
5467 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
5469 assert_eq!(
5470 tx_meta.subpool,
5471 SubPool::Pending,
5472 "Non-4844 tx should be promoted from BaseFee to Pending after basefee decrease"
5473 );
5474 assert!(
5475 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
5476 "Non-4844 tx must NEVER lose ENOUGH_BLOB_FEE_CAP_BLOCK bit during basefee promotion"
5477 );
5478 assert!(
5479 tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK),
5480 "Non-4844 tx should gain ENOUGH_FEE_CAP_BLOCK bit after basefee decrease"
5481 );
5482 }
5483
5484 #[test]
5490 fn best_transactions_nonce_order_on_balance_unlock() {
5491 let mut f = MockTransactionFactory::default();
5492 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5493
5494 let sender = Address::random();
5495 let on_chain_balance = U256::from(10_000);
5496
5497 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
5499 let tx1 = tx0.next().inc_limit().with_value(on_chain_balance);
5501 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
5503
5504 let v0 = f.validated(tx0);
5505 let v1 = f.validated(tx1);
5506 let v2 = f.validated(tx2);
5507
5508 pool.add_transaction(v0, on_chain_balance, 0, None).unwrap();
5510
5511 let mut best = pool.best_transactions();
5513
5514 let first = best.next().expect("should yield tx0");
5516 assert_eq!(first.id().nonce, 0);
5517
5518 pool.add_transaction(v1, on_chain_balance, 0, None).unwrap();
5521
5522 assert!(best.next().is_none(), "tx1 should be queued, not pending");
5524
5525 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
5529
5530 let t1 = best.next().expect("should yield a transaction");
5531 let t2 = best.next().expect("should yield a transaction");
5532
5533 assert_eq!(
5535 t1.id().nonce,
5536 1,
5537 "first yielded tx should be nonce 1, got nonce {}",
5538 t1.id().nonce
5539 );
5540 assert_eq!(
5541 t2.id().nonce,
5542 2,
5543 "second yielded tx should be nonce 2, got nonce {}",
5544 t2.id().nonce
5545 );
5546 }
5547
5548 #[test]
5552 fn best_transactions_nonce_order_on_gap_fill() {
5553 let mut f = MockTransactionFactory::default();
5554 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5555
5556 let sender = Address::random();
5557 let balance = U256::MAX;
5558
5559 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
5561 let tx1 = tx0.next().inc_limit();
5563
5564 let v0 = f.validated(tx0);
5565 let v1 = f.validated(tx1);
5566
5567 pool.add_transaction(v1, balance, 0, None).unwrap();
5569
5570 let mut best = pool.best_transactions();
5572 assert!(best.next().is_none(), "pool should have no pending txs yet");
5573
5574 pool.add_transaction(v0, balance, 0, None).unwrap();
5576
5577 let t0 = best.next().expect("should yield a transaction");
5578 let t1 = best.next().expect("should yield a transaction");
5579
5580 assert_eq!(t0.id().nonce, 0, "first yielded tx should be nonce 0, got {}", t0.id().nonce);
5581 assert_eq!(t1.id().nonce, 1, "second yielded tx should be nonce 1, got {}", t1.id().nonce);
5582 }
5583
5584 #[test]
5588 fn best_transactions_nonce_order_mixed_promotions() {
5589 let mut f = MockTransactionFactory::default();
5590 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5591
5592 let sender = Address::random();
5593 let low_balance = U256::from(10_000);
5594
5595 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
5597 let tx1 = tx0.next().inc_limit().with_value(low_balance);
5599 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
5601 let tx3 = tx2.next().inc_limit().with_value(U256::ZERO);
5603
5604 let v0 = f.validated(tx0);
5605 let v1 = f.validated(tx1);
5606 let v2 = f.validated(tx2);
5607 let v3 = f.validated(tx3);
5608
5609 pool.add_transaction(v0, low_balance, 0, None).unwrap();
5611
5612 pool.add_transaction(v1, low_balance, 0, None).unwrap();
5614
5615 pool.add_transaction(v3, low_balance, 0, None).unwrap();
5617
5618 let mut best = pool.best_transactions();
5619
5620 let first = best.next().expect("should yield tx0");
5622 assert_eq!(first.id().nonce, 0);
5623 assert!(best.next().is_none(), "only tx0 should be pending");
5624
5625 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
5630
5631 let t1 = best.next().expect("should yield nonce 1");
5632 let t2 = best.next().expect("should yield nonce 2");
5633 let t3 = best.next().expect("should yield nonce 3");
5634
5635 assert_eq!(t1.id().nonce, 1, "expected nonce 1, got {}", t1.id().nonce);
5636 assert_eq!(t2.id().nonce, 2, "expected nonce 2, got {}", t2.id().nonce);
5637 assert_eq!(t3.id().nonce, 3, "expected nonce 3, got {}", t3.id().nonce);
5638 }
5639}