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 == SubPool::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 == SubPool::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 == SubPool::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 == SubPool::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 matches!(move_to, SubPool::Pending) &&
1056 let Some(tx) = moved
1057 {
1058 trace!(target: "txpool", hash=%tx.transaction.hash(), "Promoted transaction to pending");
1059 outcome.promoted.push(tx);
1060 }
1061 }
1062 }
1063 }
1064
1065 if removed > 0 {
1066 self.metrics.removed_transactions.increment(removed);
1067 }
1068 }
1069
1070 fn move_transaction(
1075 &mut self,
1076 from: SubPool,
1077 to: SubPool,
1078 id: &TransactionId,
1079 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1080 let tx = self.remove_from_subpool(from, id)?;
1081 self.add_transaction_to_subpool(to, tx.clone());
1082 Some(tx)
1083 }
1084
1085 pub(crate) fn remove_transactions(
1090 &mut self,
1091 hashes: Vec<TxHash>,
1092 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1093 let txs =
1094 hashes.into_iter().filter_map(|hash| self.remove_transaction_by_hash(&hash)).collect();
1095 self.update_size_metrics();
1096 txs
1097 }
1098
1099 pub(crate) fn remove_transactions_and_descendants(
1101 &mut self,
1102 hashes: Vec<TxHash>,
1103 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1104 let mut removed = Vec::new();
1105 for hash in hashes {
1106 if let Some(tx) = self.remove_transaction_by_hash(&hash) {
1107 removed.push(tx.clone());
1108 self.remove_descendants(tx.id(), &mut removed);
1109 }
1110 }
1111 self.update_size_metrics();
1112 removed
1113 }
1114
1115 pub(crate) fn remove_transactions_by_sender(
1117 &mut self,
1118 sender_id: SenderId,
1119 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1120 let mut removed = Vec::new();
1121 let txs = self.get_transactions_by_sender(sender_id);
1122 for tx in txs {
1123 if let Some(tx) = self.remove_transaction(tx.id()) {
1124 removed.push(tx);
1125 }
1126 }
1127 self.update_size_metrics();
1128 removed
1129 }
1130
1131 pub(crate) fn prune_transactions(
1137 &mut self,
1138 hashes: Vec<TxHash>,
1139 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1140 let txs =
1141 hashes.into_iter().filter_map(|hash| self.prune_transaction_by_hash(&hash)).collect();
1142 self.update_size_metrics();
1143 txs
1144 }
1145
1146 fn remove_transaction(
1150 &mut self,
1151 id: &TransactionId,
1152 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1153 let (tx, pool) = self.all_transactions.remove_transaction(id)?;
1154 self.remove_from_subpool(pool, tx.id())
1155 }
1156
1157 fn remove_transaction_by_hash(
1163 &mut self,
1164 tx_hash: &B256,
1165 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1166 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1167
1168 let mut updates = self.all_transactions.park_descendant_transactions(tx.id());
1170 #[expect(clippy::iter_with_drain, reason = "retain the scratch buffer allocation")]
1171 self.process_updates(updates.drain(..), &mut UpdateOutcome::default());
1172 self.all_transactions.update_buffer = updates;
1173 self.remove_from_subpool(pool, tx.id())
1174 }
1175
1176 fn prune_transaction_by_hash(
1183 &mut self,
1184 tx_hash: &B256,
1185 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1186 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1187 self.remove_from_subpool(pool, tx.id())
1188 }
1189 fn prune_transaction_by_id(
1194 &mut self,
1195 tx_id: &TransactionId,
1196 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1197 let (tx, pool) = self.all_transactions.remove_transaction_by_id(tx_id)?;
1198 self.remove_from_subpool(pool, tx.id())
1199 }
1200
1201 fn remove_from_subpool(
1205 &mut self,
1206 pool: SubPool,
1207 tx: &TransactionId,
1208 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1209 let tx = match pool {
1210 SubPool::Queued => self.queued_pool.remove_transaction(tx),
1211 SubPool::Pending => self.pending_pool.remove_transaction(tx),
1212 SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
1213 SubPool::Blob => self.blob_pool.remove_transaction(tx),
1214 };
1215
1216 if let Some(ref tx) = tx {
1217 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Removed transaction from a subpool");
1221 }
1222
1223 tx
1224 }
1225
1226 fn remove_descendants(
1230 &mut self,
1231 tx: &TransactionId,
1232 removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
1233 ) {
1234 let mut id = *tx;
1235
1236 loop {
1238 let descendant =
1239 self.all_transactions.descendant_txs_exclusive(&id).map(|(id, _)| *id).next();
1240 if let Some(descendant) = descendant {
1241 if let Some(tx) = self.remove_transaction(&descendant) {
1242 removed.push(tx)
1243 }
1244 id = descendant;
1245 } else {
1246 return
1247 }
1248 }
1249 }
1250
1251 fn add_transaction_to_subpool(
1253 &mut self,
1254 pool: SubPool,
1255 tx: Arc<ValidPoolTransaction<T::Transaction>>,
1256 ) {
1257 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Adding transaction to a subpool");
1261 match pool {
1262 SubPool::Queued => self.queued_pool.add_transaction(tx),
1263 SubPool::Pending => {
1264 self.pending_pool.add_transaction(tx, self.all_transactions.pending_fees.base_fee);
1265 }
1266 SubPool::BaseFee => {
1267 self.basefee_pool.add_transaction(tx);
1268 }
1269 SubPool::Blob => {
1270 self.blob_pool.add_transaction(tx);
1271 }
1272 }
1273 }
1274
1275 fn add_new_transaction(
1278 &mut self,
1279 transaction: Arc<ValidPoolTransaction<T::Transaction>>,
1280 replaced: Option<(Arc<ValidPoolTransaction<T::Transaction>>, SubPool)>,
1281 pool: SubPool,
1282 ) {
1283 if let Some((replaced, replaced_pool)) = replaced {
1284 self.remove_from_subpool(replaced_pool, replaced.id());
1286 }
1287
1288 self.add_transaction_to_subpool(pool, transaction)
1289 }
1290
1291 pub(crate) fn discard_worst(&mut self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1298 let mut removed = Vec::new();
1299
1300 macro_rules! discard_worst {
1302 ($this:ident, $removed:ident, [$($limit:ident => ($pool:ident, $metric:ident)),* $(,)*]) => {
1303 $ (
1304 while $this.$pool.exceeds(&$this.config.$limit)
1305 {
1306 trace!(
1307 target: "txpool",
1308 "discarding transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1309 stringify!($pool),
1310 $this.config.$limit,
1311 $this.$pool.size(),
1312 $this.$pool.len(),
1313 );
1314
1315 let removed_from_subpool = $this.$pool.truncate_pool($this.config.$limit.clone());
1317
1318 trace!(
1319 target: "txpool",
1320 "removed {} transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1321 removed_from_subpool.len(),
1322 stringify!($pool),
1323 $this.config.$limit,
1324 $this.$pool.size(),
1325 $this.$pool.len()
1326 );
1327 $this.metrics.$metric.increment(removed_from_subpool.len() as u64);
1328
1329 for tx in removed_from_subpool {
1331 $this.all_transactions.remove_transaction(tx.id());
1332
1333 let id = *tx.id();
1334
1335 removed.push(tx);
1337
1338 $this.remove_descendants(&id, &mut $removed);
1340 }
1341 }
1342
1343 )*
1344 };
1345 }
1346
1347 discard_worst!(
1348 self, removed, [
1349 pending_limit => (pending_pool, pending_transactions_evicted),
1350 basefee_limit => (basefee_pool, basefee_transactions_evicted),
1351 blob_limit => (blob_pool, blob_transactions_evicted),
1352 queued_limit => (queued_pool, queued_transactions_evicted),
1353 ]
1354 );
1355
1356 removed
1357 }
1358
1359 pub(crate) fn len(&self) -> usize {
1361 self.all_transactions.len()
1362 }
1363
1364 pub(crate) fn is_empty(&self) -> bool {
1366 self.all_transactions.is_empty()
1367 }
1368
1369 #[cfg(any(test, feature = "test-utils"))]
1377 pub fn assert_invariants(&self) {
1378 let size = self.size();
1379 let actual = size.basefee + size.pending + size.queued + size.blob;
1380 assert_eq!(
1381 size.total, actual,
1382 "total size must be equal to the sum of all sub-pools, basefee:{}, pending:{}, queued:{}, blob:{}",
1383 size.basefee, size.pending, size.queued, size.blob
1384 );
1385 self.all_transactions.assert_invariants();
1386 self.pending_pool.assert_invariants();
1387 self.basefee_pool.assert_invariants();
1388 self.queued_pool.assert_invariants();
1389 self.blob_pool.assert_invariants();
1390 }
1391}
1392
1393#[cfg(any(test, feature = "test-utils"))]
1394impl TxPool<crate::test_utils::MockOrdering> {
1395 pub fn mock() -> Self {
1397 Self::new(crate::test_utils::MockOrdering::default(), PoolConfig::default())
1398 }
1399}
1400
1401#[cfg(test)]
1402impl<T: TransactionOrdering> Drop for TxPool<T> {
1403 fn drop(&mut self) {
1404 self.assert_invariants();
1405 }
1406}
1407
1408impl<T: TransactionOrdering> TxPool<T> {
1409 pub const fn pending(&self) -> &PendingPool<T> {
1411 &self.pending_pool
1412 }
1413
1414 pub const fn base_fee(&self) -> &ParkedPool<BasefeeOrd<T::Transaction>> {
1416 &self.basefee_pool
1417 }
1418
1419 pub const fn queued(&self) -> &ParkedPool<QueuedOrd<T::Transaction>> {
1421 &self.queued_pool
1422 }
1423}
1424
1425impl<T: TransactionOrdering> fmt::Debug for TxPool<T> {
1426 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1427 f.debug_struct("TxPool").field("config", &self.config).finish_non_exhaustive()
1428 }
1429}
1430
1431const FULL_UPDATE_MIN_SENDERS: usize = 1_000;
1436
1437const FULL_UPDATE_SENDER_RATIO: usize = 4;
1439
1440pub(crate) struct AllTransactions<T: PoolTransaction> {
1445 minimal_protocol_basefee: u64,
1449 block_gas_limit: u64,
1451 max_account_slots: usize,
1453 by_hash: B256Map<Arc<ValidPoolTransaction<T>>>,
1455 txs: BTreeMap<TransactionId, PoolInternalTransaction<T>>,
1457 sender_info: FxHashMap<SenderId, SenderInfo>,
1463 tx_counter: FxHashMap<SenderId, usize>,
1465 last_seen_block_number: u64,
1467 last_seen_block_hash: B256,
1469 pending_fees: PendingFees,
1471 last_full_update_fees: PendingFees,
1479 price_bumps: PriceBumpConfig,
1481 local_transactions_config: LocalTransactionConfig,
1483 auths: FxHashMap<SenderId, B256Set>,
1485 tx_type_counts: TxTypeCounts,
1488 update_buffer: Vec<PoolUpdate>,
1491 metrics: AllTransactionsMetrics,
1493}
1494
1495impl<T: PoolTransaction> AllTransactions<T> {
1496 fn new(config: &PoolConfig) -> Self {
1498 Self {
1499 max_account_slots: config.max_account_slots,
1500 price_bumps: config.price_bumps,
1501 local_transactions_config: config.local_transactions_config.clone(),
1502 minimal_protocol_basefee: config.minimal_protocol_basefee,
1503 block_gas_limit: config.gas_limit,
1504 ..Default::default()
1505 }
1506 }
1507
1508 #[expect(dead_code)]
1510 pub(crate) fn hashes_iter(&self) -> impl Iterator<Item = TxHash> + '_ {
1511 self.by_hash.keys().copied()
1512 }
1513
1514 pub(crate) fn transactions_iter(
1516 &self,
1517 ) -> impl Iterator<Item = &Arc<ValidPoolTransaction<T>>> + '_ {
1518 self.by_hash.values()
1519 }
1520
1521 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
1523 self.by_hash.contains_key(tx_hash)
1524 }
1525
1526 pub(crate) fn get(&self, id: &TransactionId) -> Option<&PoolInternalTransaction<T>> {
1528 self.txs.get(id)
1529 }
1530
1531 pub(crate) fn tx_inc(&mut self, sender: SenderId) {
1533 let count = self.tx_counter.entry(sender).or_default();
1534 *count += 1;
1535 self.metrics.all_transactions_by_all_senders.increment(1.0);
1536 }
1537
1538 pub(crate) fn tx_decr(&mut self, sender: SenderId) {
1540 if let hash_map::Entry::Occupied(mut entry) = self.tx_counter.entry(sender) {
1541 let count = entry.get_mut();
1542 if *count == 1 {
1543 entry.remove();
1544 self.sender_info.remove(&sender);
1545 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1546 return
1547 }
1548 *count -= 1;
1549 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1550 }
1551 }
1552
1553 fn set_block_info(&mut self, block_info: BlockInfo) {
1555 let BlockInfo {
1556 block_gas_limit,
1557 last_seen_block_hash,
1558 last_seen_block_number,
1559 pending_basefee,
1560 pending_blob_fee,
1561 } = block_info;
1562 self.last_seen_block_number = last_seen_block_number;
1563 self.last_seen_block_hash = last_seen_block_hash;
1564
1565 self.pending_fees.base_fee = pending_basefee;
1566 self.metrics.base_fee.set(pending_basefee as f64);
1567
1568 self.block_gas_limit = block_gas_limit;
1569
1570 if let Some(pending_blob_fee) = pending_blob_fee {
1571 self.pending_fees.blob_fee = pending_blob_fee;
1572 self.metrics.blob_base_fee.set(pending_blob_fee as f64);
1573 }
1574 }
1575
1576 pub(crate) fn update_size_metrics(&self) {
1578 self.metrics.all_transactions_by_hash.set(self.by_hash.len() as f64);
1579 self.metrics.all_transactions_by_id.set(self.txs.len() as f64);
1580 }
1581
1582 pub(crate) fn update(
1599 &mut self,
1600 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1601 ) -> Vec<PoolUpdate> {
1602 let mut updates = std::mem::take(&mut self.update_buffer);
1603 let pending_fees = self.pending_fees;
1604
1605 let update_all = self.last_full_update_fees != self.pending_fees ||
1606 self.should_update_all_senders(changed_accounts.len());
1607
1608 if update_all {
1609 Self::update_txs(pending_fees, changed_accounts, &mut updates, self.txs.iter_mut());
1610 self.last_full_update_fees = self.pending_fees;
1611 } else {
1612 for sender in changed_accounts.keys() {
1615 let range = TransactionId::new(*sender, 0)..=TransactionId::new(*sender, u64::MAX);
1616 Self::update_txs(
1617 pending_fees,
1618 changed_accounts,
1619 &mut updates,
1620 self.txs.range_mut(range),
1621 );
1622 }
1623 }
1624
1625 updates
1626 }
1627
1628 fn should_update_all_senders(&self, changed_sender_count: usize) -> bool {
1630 let pool_sender_count = self.tx_counter.len();
1631 changed_sender_count >= pool_sender_count ||
1632 (pool_sender_count >= FULL_UPDATE_MIN_SENDERS &&
1633 changed_sender_count >= pool_sender_count.div_ceil(FULL_UPDATE_SENDER_RATIO))
1634 }
1635
1636 fn update_txs<'a, I>(
1641 pending_fees: PendingFees,
1642 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1643 updates: &mut Vec<PoolUpdate>,
1644 txs: I,
1645 ) where
1646 T: 'a,
1647 I: Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)>,
1648 {
1649 let mut iter = txs.peekable();
1650
1651 'transactions: while let Some((id, tx)) = iter.next() {
1661 macro_rules! next_sender {
1662 ($iter:ident) => {
1663 'this: while let Some((peek, _)) = iter.peek() {
1664 if peek.sender != id.sender {
1665 break 'this
1666 }
1667 iter.next();
1668 }
1669 };
1670 }
1671
1672 let changed_balance = if let Some(info) = changed_accounts.get(&id.sender) {
1675 if id.nonce < info.state_nonce {
1677 updates.push(PoolUpdate {
1678 id: *tx.transaction.id(),
1679 current: tx.subpool,
1680 destination: Destination::Discard,
1681 });
1682 continue 'transactions
1683 }
1684
1685 let ancestor = TransactionId::ancestor(id.nonce, info.state_nonce, id.sender);
1686 if ancestor.is_none() {
1688 tx.state.insert(TxState::NO_NONCE_GAPS);
1689 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1690 tx.cumulative_cost = U256::ZERO;
1691 if tx.transaction.cost() > &info.balance {
1692 tx.state.remove(TxState::ENOUGH_BALANCE);
1694 } else {
1695 tx.state.insert(TxState::ENOUGH_BALANCE);
1696 }
1697 }
1698
1699 Some(&info.balance)
1700 } else {
1701 None
1702 };
1703
1704 if tx.state.has_nonce_gap() {
1706 next_sender!(iter);
1707 continue 'transactions
1708 }
1709
1710 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1712
1713 Self::update_tx_fees(pending_fees, tx);
1715 Self::record_subpool_update(updates, tx);
1717
1718 let mut has_parked_ancestor = !tx.state.is_pending();
1720
1721 let mut cumulative_cost = tx.next_cumulative_cost();
1722
1723 let mut next_nonce_in_line = tx.transaction.nonce().saturating_add(1);
1725
1726 while let Some((peek, tx)) = iter.peek_mut() {
1728 if peek.sender != id.sender {
1729 continue 'transactions
1731 }
1732
1733 if tx.transaction.nonce() == next_nonce_in_line {
1734 tx.state.insert(TxState::NO_NONCE_GAPS);
1736 } else {
1737 next_sender!(iter);
1739 continue 'transactions
1740 }
1741
1742 next_nonce_in_line = next_nonce_in_line.saturating_add(1);
1744
1745 tx.cumulative_cost = cumulative_cost;
1747 cumulative_cost = tx.next_cumulative_cost();
1749
1750 if let Some(changed_balance) = changed_balance {
1752 if &cumulative_cost > changed_balance {
1753 tx.state.remove(TxState::ENOUGH_BALANCE);
1755 } else {
1756 tx.state.insert(TxState::ENOUGH_BALANCE);
1757 }
1758 }
1759
1760 if has_parked_ancestor {
1762 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1763 } else {
1764 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1765 }
1766
1767 Self::update_tx_fees(pending_fees, tx);
1769 Self::record_subpool_update(updates, tx);
1770 has_parked_ancestor = !tx.state.is_pending();
1771
1772 iter.next();
1774 }
1775 }
1776 }
1777
1778 fn record_subpool_update(updates: &mut Vec<PoolUpdate>, tx: &mut PoolInternalTransaction<T>) {
1783 let current_pool = tx.subpool;
1784 tx.subpool = tx.state.into();
1785 if current_pool != tx.subpool {
1786 updates.push(PoolUpdate {
1787 id: *tx.transaction.id(),
1788 current: current_pool,
1789 destination: tx.subpool.into(),
1790 })
1791 }
1792 }
1793
1794 fn update_tx_fees(pending_fees: PendingFees, tx: &mut PoolInternalTransaction<T>) {
1796 match tx.transaction.max_fee_per_gas().cmp(&(pending_fees.base_fee as u128)) {
1797 Ordering::Greater | Ordering::Equal => {
1798 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1799 }
1800 Ordering::Less => {
1801 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
1802 }
1803 }
1804
1805 match tx.transaction.max_fee_per_blob_gas() {
1806 Some(blob_fee_cap) if blob_fee_cap < pending_fees.blob_fee => {
1807 tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1808 }
1809 _ => {
1810 tx.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
1811 }
1812 }
1813 }
1814
1815 pub(crate) fn txs_iter(
1818 &self,
1819 sender: SenderId,
1820 ) -> impl Iterator<Item = (&TransactionId, &PoolInternalTransaction<T>)> + '_ {
1821 self.txs
1822 .range((sender.start_bound(), Unbounded))
1823 .take_while(move |(other, _)| sender == other.sender)
1824 }
1825
1826 #[cfg(test)]
1829 #[expect(dead_code)]
1830 pub(crate) fn txs_iter_mut(
1831 &mut self,
1832 sender: SenderId,
1833 ) -> impl Iterator<Item = (&TransactionId, &mut PoolInternalTransaction<T>)> + '_ {
1834 self.txs
1835 .range_mut((sender.start_bound(), Unbounded))
1836 .take_while(move |(other, _)| sender == other.sender)
1837 }
1838
1839 pub(crate) fn descendant_txs_exclusive<'a, 'b: 'a>(
1843 &'a self,
1844 id: &'b TransactionId,
1845 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1846 self.txs.range((Excluded(id), Unbounded)).take_while(|(other, _)| id.sender == other.sender)
1847 }
1848
1849 pub(crate) fn descendant_txs_inclusive<'a, 'b: 'a>(
1854 &'a self,
1855 id: &'b TransactionId,
1856 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1857 self.txs.range(id..).take_while(|(other, _)| id.sender == other.sender)
1858 }
1859
1860 pub(crate) fn descendant_txs_mut<'a, 'b: 'a>(
1865 &'a mut self,
1866 id: &'b TransactionId,
1867 ) -> impl Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)> + 'a {
1868 self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender)
1869 }
1870
1871 pub(crate) fn remove_transaction_by_hash(
1873 &mut self,
1874 tx_hash: &B256,
1875 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1876 let tx = self.by_hash.remove(tx_hash)?;
1877 let internal = self.txs.remove(&tx.transaction_id)?;
1878 self.remove_auths(&internal);
1879 self.tx_type_counts.dec(internal.transaction.transaction.ty());
1880 self.tx_decr(tx.sender_id());
1882 Some((tx, internal.subpool))
1883 }
1884
1885 pub(crate) fn remove_transaction_by_id(
1889 &mut self,
1890 tx_id: &TransactionId,
1891 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1892 let internal = self.txs.remove(tx_id)?;
1893 let tx = self.by_hash.remove(internal.transaction.hash())?;
1894 self.remove_auths(&internal);
1895 self.tx_type_counts.dec(internal.transaction.transaction.ty());
1896 self.tx_decr(tx.sender_id());
1898 Some((tx, internal.subpool))
1899 }
1900
1901 pub(crate) fn park_descendant_transactions(
1903 &mut self,
1904 tx_id: &TransactionId,
1905 ) -> Vec<PoolUpdate> {
1906 let mut updates = std::mem::take(&mut self.update_buffer);
1907
1908 for (id, tx) in self.descendant_txs_mut(tx_id) {
1909 let current_pool = tx.subpool;
1910
1911 tx.state.remove(TxState::NO_NONCE_GAPS);
1912
1913 tx.subpool = tx.state.into();
1915
1916 if current_pool != tx.subpool {
1918 updates.push(PoolUpdate {
1919 id: *id,
1920 current: current_pool,
1921 destination: tx.subpool.into(),
1922 })
1923 }
1924 }
1925
1926 updates
1927 }
1928
1929 pub(crate) fn remove_transaction(
1935 &mut self,
1936 id: &TransactionId,
1937 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1938 let internal = self.txs.remove(id)?;
1939
1940 self.tx_decr(internal.transaction.sender_id());
1942 self.tx_type_counts.dec(internal.transaction.transaction.ty());
1943
1944 let result =
1945 self.by_hash.remove(internal.transaction.hash()).map(|tx| (tx, internal.subpool));
1946
1947 self.remove_auths(&internal);
1948
1949 result
1950 }
1951
1952 fn remove_auths(&mut self, tx: &PoolInternalTransaction<T>) {
1956 let Some(auths) = &tx.transaction.authority_ids else { return };
1957
1958 let tx_hash = tx.transaction.hash();
1959 for auth in auths {
1960 if let Some(list) = self.auths.get_mut(auth) {
1961 list.remove(tx_hash);
1962 if list.is_empty() {
1963 self.auths.remove(auth);
1964 }
1965 }
1966 }
1967 }
1968
1969 #[inline]
1975 fn contains_conflicting_transaction(&self, tx: &ValidPoolTransaction<T>) -> bool {
1976 self.txs_iter(tx.transaction_id.sender)
1977 .next()
1978 .is_some_and(|(_, existing)| tx.tx_type_conflicts_with(&existing.transaction))
1979 }
1980
1981 fn ensure_valid(
1990 &self,
1991 transaction: ValidPoolTransaction<T>,
1992 on_chain_nonce: u64,
1993 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1994 if !self.local_transactions_config.is_local(transaction.origin, transaction.sender_ref()) {
1995 let current_txs =
1996 self.tx_counter.get(&transaction.sender_id()).copied().unwrap_or_default();
1997
1998 if current_txs >= self.max_account_slots && transaction.nonce() > on_chain_nonce {
2001 return Err(InsertErr::ExceededSenderTransactionsCapacity {
2002 transaction: Arc::new(transaction),
2003 })
2004 }
2005 }
2006 if transaction.gas_limit() > self.block_gas_limit {
2007 return Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas {
2008 block_gas_limit: self.block_gas_limit,
2009 tx_gas_limit: transaction.gas_limit(),
2010 transaction: Arc::new(transaction),
2011 })
2012 }
2013
2014 if self.contains_conflicting_transaction(&transaction) {
2015 return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) })
2017 }
2018
2019 Ok(transaction)
2020 }
2021
2022 fn ensure_valid_blob_transaction(
2028 &self,
2029 new_blob_tx: ValidPoolTransaction<T>,
2030 on_chain_balance: U256,
2031 ancestor: Option<TransactionId>,
2032 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
2033 if let Some(ancestor) = ancestor {
2034 let Some(ancestor_tx) = self.txs.get(&ancestor) else {
2035 self.metrics.blob_transactions_nonce_gaps.increment(1);
2037 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
2038 };
2039 if ancestor_tx.state.has_nonce_gap() {
2040 self.metrics.blob_transactions_nonce_gaps.increment(1);
2043 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
2044 }
2045
2046 let mut cumulative_cost = ancestor_tx.next_cumulative_cost() + new_blob_tx.cost();
2048
2049 if cumulative_cost > on_chain_balance {
2051 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
2053 }
2054
2055 let id = new_blob_tx.transaction_id;
2058 let mut descendants = self.descendant_txs_inclusive(&id).peekable();
2059 if let Some((maybe_replacement, _)) = descendants.peek() &&
2060 **maybe_replacement == new_blob_tx.transaction_id
2061 {
2062 descendants.next();
2064
2065 for (_, tx) in descendants {
2067 cumulative_cost += tx.transaction.cost();
2068 if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance {
2069 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
2071 }
2072 }
2073 }
2074 } else if new_blob_tx.cost() > &on_chain_balance {
2075 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
2077 }
2078
2079 Ok(new_blob_tx)
2080 }
2081
2082 pub(crate) fn insert_tx(
2114 &mut self,
2115 transaction: ValidPoolTransaction<T>,
2116 on_chain_balance: U256,
2117 on_chain_nonce: u64,
2118 ) -> InsertResult<T> {
2119 assert!(on_chain_nonce <= transaction.nonce(), "Invalid transaction");
2120
2121 let mut transaction = self.ensure_valid(transaction, on_chain_nonce)?;
2122
2123 let inserted_tx_id = *transaction.id();
2124 let mut state = TxState::default();
2125 let mut cumulative_cost = U256::ZERO;
2126
2127 state.insert(TxState::NOT_TOO_MUCH_GAS);
2129
2130 let ancestor = TransactionId::ancestor(
2133 transaction.transaction.nonce(),
2134 on_chain_nonce,
2135 inserted_tx_id.sender,
2136 );
2137
2138 if transaction.is_eip4844() {
2141 state.insert(TxState::BLOB_TRANSACTION);
2142
2143 transaction =
2144 self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
2145 let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();
2146 if blob_fee_cap >= self.pending_fees.blob_fee {
2147 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
2148 }
2149 } else {
2150 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
2152 }
2153
2154 let transaction = Arc::new(transaction);
2155
2156 if ancestor.is_none() {
2158 state.insert(TxState::NO_NONCE_GAPS);
2159 state.insert(TxState::NO_PARKED_ANCESTORS);
2160 }
2161
2162 let fee_cap = transaction.max_fee_per_gas();
2164
2165 if fee_cap < self.minimal_protocol_basefee as u128 {
2166 return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap })
2167 }
2168 if fee_cap >= self.pending_fees.base_fee as u128 {
2169 state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
2170 }
2171
2172 let mut replaced_tx = None;
2174
2175 let pool_tx = PoolInternalTransaction {
2176 transaction: Arc::clone(&transaction),
2177 subpool: state.into(),
2178 state,
2179 cumulative_cost,
2180 };
2181
2182 match self.txs.entry(*transaction.id()) {
2184 Entry::Vacant(entry) => {
2185 self.by_hash.insert(*pool_tx.transaction.hash(), pool_tx.transaction.clone());
2187 self.tx_type_counts.inc(pool_tx.transaction.transaction.ty());
2188 entry.insert(pool_tx);
2189 }
2190 Entry::Occupied(mut entry) => {
2191 let existing_transaction = entry.get().transaction.as_ref();
2193 let maybe_replacement = transaction.as_ref();
2194
2195 if existing_transaction
2197 .is_replacement_underpriced(maybe_replacement, &self.price_bumps)
2198 {
2199 return Err(InsertErr::Underpriced {
2200 transaction: pool_tx.transaction,
2201 existing: *entry.get().transaction.hash(),
2202 })
2203 }
2204 let new_hash = *pool_tx.transaction.hash();
2205 let new_transaction = pool_tx.transaction.clone();
2206 self.tx_type_counts.inc(pool_tx.transaction.transaction.ty());
2207 let replaced = entry.insert(pool_tx);
2208 self.tx_type_counts.dec(replaced.transaction.transaction.ty());
2209 self.by_hash.remove(replaced.transaction.hash());
2210 self.by_hash.insert(new_hash, new_transaction);
2211
2212 self.remove_auths(&replaced);
2213
2214 replaced_tx = Some((replaced.transaction, replaced.subpool));
2216 }
2217 }
2218
2219 if let Some(auths) = &transaction.authority_ids {
2220 let tx_hash = transaction.hash();
2221 for auth in auths {
2222 self.auths.entry(*auth).or_default().insert(*tx_hash);
2223 }
2224 }
2225
2226 let mut updates = std::mem::take(&mut self.update_buffer);
2228
2229 let on_chain_id = TransactionId::new(transaction.sender_id(), on_chain_nonce);
2231 let pending_fees = self.pending_fees;
2232 {
2233 let mut next_nonce = on_chain_id.nonce;
2235
2236 let mut has_parked_ancestor = false;
2240
2241 for (id, tx) in self.descendant_txs_mut(&on_chain_id) {
2244 let current_pool = tx.subpool;
2245
2246 if next_nonce != id.nonce {
2248 break
2249 }
2250
2251 tx.state.insert(TxState::NO_NONCE_GAPS);
2253
2254 tx.cumulative_cost = cumulative_cost;
2256
2257 cumulative_cost = tx.next_cumulative_cost();
2259
2260 if cumulative_cost > on_chain_balance {
2261 tx.state.remove(TxState::ENOUGH_BALANCE);
2263 } else {
2264 tx.state.insert(TxState::ENOUGH_BALANCE);
2265 }
2266
2267 if has_parked_ancestor {
2269 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
2270 } else {
2271 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
2272 }
2273
2274 Self::update_tx_fees(pending_fees, tx);
2275
2276 tx.subpool = tx.state.into();
2278 has_parked_ancestor = !tx.state.is_pending();
2279
2280 if inserted_tx_id.eq(id) {
2281 state = tx.state;
2283 } else {
2284 if current_pool != tx.subpool {
2286 updates.push(PoolUpdate {
2287 id: *id,
2288 current: current_pool,
2289 destination: tx.subpool.into(),
2290 })
2291 }
2292 }
2293
2294 next_nonce = id.next_nonce();
2296 }
2297 }
2298
2299 if replaced_tx.is_none() {
2301 self.tx_inc(inserted_tx_id.sender);
2302 }
2303
2304 Ok(InsertOk { transaction, move_to: state.into(), state, replaced_tx, updates })
2305 }
2306
2307 pub(crate) fn len(&self) -> usize {
2309 self.txs.len()
2310 }
2311
2312 pub(crate) fn is_empty(&self) -> bool {
2314 self.txs.is_empty()
2315 }
2316
2317 #[cfg(any(test, feature = "test-utils"))]
2319 pub(crate) fn assert_invariants(&self) {
2320 assert_eq!(self.by_hash.len(), self.txs.len(), "by_hash.len() != txs.len()");
2321 assert!(self.auths.len() <= self.txs.len(), "auths.len() > txs.len()");
2322 }
2323}
2324
2325#[cfg(test)]
2326impl<T: PoolTransaction> AllTransactions<T> {
2327 pub(crate) fn tx_count(&self, sender: SenderId) -> usize {
2331 self.tx_counter.get(&sender).copied().unwrap_or_default()
2332 }
2333}
2334
2335impl<T: PoolTransaction> Default for AllTransactions<T> {
2336 fn default() -> Self {
2337 Self {
2338 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
2339 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
2340 block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
2341 by_hash: Default::default(),
2342 txs: Default::default(),
2343 sender_info: Default::default(),
2344 tx_counter: Default::default(),
2345 last_seen_block_number: Default::default(),
2346 last_seen_block_hash: Default::default(),
2347 pending_fees: Default::default(),
2348 last_full_update_fees: Default::default(),
2350 price_bumps: Default::default(),
2351 local_transactions_config: Default::default(),
2352 auths: Default::default(),
2353 tx_type_counts: Default::default(),
2354 update_buffer: Default::default(),
2355 metrics: Default::default(),
2356 }
2357 }
2358}
2359
2360#[derive(Debug, Clone, Copy, Default)]
2365pub(crate) struct TxTypeCounts {
2366 legacy: u64,
2367 eip2930: u64,
2368 eip1559: u64,
2369 eip4844: u64,
2370 eip7702: u64,
2371 other: u64,
2372}
2373
2374impl TxTypeCounts {
2375 const fn counter_mut(&mut self, tx_type: u8) -> &mut u64 {
2377 match tx_type {
2378 LEGACY_TX_TYPE_ID => &mut self.legacy,
2379 EIP2930_TX_TYPE_ID => &mut self.eip2930,
2380 EIP1559_TX_TYPE_ID => &mut self.eip1559,
2381 EIP4844_TX_TYPE_ID => &mut self.eip4844,
2382 EIP7702_TX_TYPE_ID => &mut self.eip7702,
2383 _ => &mut self.other,
2384 }
2385 }
2386
2387 const fn inc(&mut self, tx_type: u8) {
2389 *self.counter_mut(tx_type) += 1;
2390 }
2391
2392 const fn dec(&mut self, tx_type: u8) {
2394 *self.counter_mut(tx_type) -= 1;
2395 }
2396}
2397
2398#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2400pub(crate) struct PendingFees {
2401 pub(crate) base_fee: u64,
2403 pub(crate) blob_fee: u128,
2405}
2406
2407impl Default for PendingFees {
2408 fn default() -> Self {
2409 Self { base_fee: Default::default(), blob_fee: BLOB_TX_MIN_BLOB_GASPRICE }
2410 }
2411}
2412
2413pub(crate) type InsertResult<T> = Result<InsertOk<T>, InsertErr<T>>;
2415
2416#[derive(Debug)]
2418pub(crate) enum InsertErr<T: PoolTransaction> {
2419 Underpriced {
2421 transaction: Arc<ValidPoolTransaction<T>>,
2422 #[expect(dead_code)]
2423 existing: TxHash,
2424 },
2425 BlobTxHasNonceGap { transaction: Arc<ValidPoolTransaction<T>> },
2427 Overdraft { transaction: Arc<ValidPoolTransaction<T>> },
2430 FeeCapBelowMinimumProtocolFeeCap { transaction: Arc<ValidPoolTransaction<T>>, fee_cap: u128 },
2434 ExceededSenderTransactionsCapacity { transaction: Arc<ValidPoolTransaction<T>> },
2438 TxGasLimitMoreThanAvailableBlockGas {
2440 transaction: Arc<ValidPoolTransaction<T>>,
2441 block_gas_limit: u64,
2442 tx_gas_limit: u64,
2443 },
2444 TxTypeConflict { transaction: Arc<ValidPoolTransaction<T>> },
2446}
2447
2448#[derive(Debug)]
2450pub(crate) struct InsertOk<T: PoolTransaction> {
2451 transaction: Arc<ValidPoolTransaction<T>>,
2453 move_to: SubPool,
2455 state: TxState,
2457 replaced_tx: Option<(Arc<ValidPoolTransaction<T>>, SubPool)>,
2459 updates: Vec<PoolUpdate>,
2461}
2462
2463#[derive(Debug)]
2466pub(crate) struct PoolInternalTransaction<T: PoolTransaction> {
2467 pub(crate) transaction: Arc<ValidPoolTransaction<T>>,
2469 pub(crate) subpool: SubPool,
2471 pub(crate) state: TxState,
2474 pub(crate) cumulative_cost: U256,
2479}
2480
2481impl<T: PoolTransaction> PoolInternalTransaction<T> {
2484 fn next_cumulative_cost(&self) -> U256 {
2485 self.cumulative_cost + self.transaction.cost()
2486 }
2487}
2488
2489#[derive(Debug, Clone, Default)]
2491pub(crate) struct SenderInfo {
2492 pub(crate) state_nonce: u64,
2494 pub(crate) balance: U256,
2496}
2497
2498impl SenderInfo {
2501 const fn update(&mut self, state_nonce: u64, balance: U256) {
2503 *self = Self { state_nonce, balance };
2504 }
2505}
2506
2507#[cfg(test)]
2508mod tests {
2509 use super::*;
2510 use crate::{
2511 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
2512 traits::TransactionOrigin,
2513 SubPoolLimit,
2514 };
2515 use alloy_consensus::{Transaction, TxType};
2516 use alloy_primitives::address;
2517
2518 #[test]
2519 fn test_insert_blob() {
2520 let on_chain_balance = U256::MAX;
2521 let on_chain_nonce = 0;
2522 let mut f = MockTransactionFactory::default();
2523 let mut pool = AllTransactions::default();
2524 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2525 let valid_tx = f.validated(tx);
2526 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2527 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2528 assert!(updates.is_empty());
2529 assert!(replaced_tx.is_none());
2530 assert!(state.contains(TxState::NO_NONCE_GAPS));
2531 assert!(state.contains(TxState::ENOUGH_BALANCE));
2532 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2533 assert_eq!(move_to, SubPool::Pending);
2534
2535 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2536 assert_eq!(inserted.subpool, SubPool::Pending);
2537 }
2538
2539 #[test]
2540 fn test_insert_blob_not_enough_blob_fee() {
2541 let on_chain_balance = U256::MAX;
2542 let on_chain_nonce = 0;
2543 let mut f = MockTransactionFactory::default();
2544 let mut pool = AllTransactions {
2545 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2546 ..Default::default()
2547 };
2548 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2549 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2550 let valid_tx = f.validated(tx);
2551 let InsertOk { state, .. } =
2552 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2553 assert!(state.contains(TxState::NO_NONCE_GAPS));
2554 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2555
2556 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2557 }
2558
2559 #[test]
2560 fn test_valid_tx_with_decreasing_blob_fee() {
2561 let on_chain_balance = U256::MAX;
2562 let on_chain_nonce = 0;
2563 let mut f = MockTransactionFactory::default();
2564 let mut pool = AllTransactions {
2565 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2566 ..Default::default()
2567 };
2568 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2569
2570 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2571 let valid_tx = f.validated(tx.clone());
2572 let InsertOk { state, .. } =
2573 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2574 assert!(state.contains(TxState::NO_NONCE_GAPS));
2575 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2576
2577 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2578 pool.remove_transaction(&valid_tx.transaction_id);
2579
2580 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
2581 let InsertOk { state, .. } =
2582 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2583 assert!(state.contains(TxState::NO_NONCE_GAPS));
2584 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2585 }
2586
2587 #[test]
2588 fn test_demote_valid_tx_with_increasing_blob_fee() {
2589 let on_chain_balance = U256::MAX;
2590 let on_chain_nonce = 0;
2591 let mut f = MockTransactionFactory::default();
2592 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2593 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2594
2595 let mut block_info = pool.block_info();
2597 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2598 pool.set_block_info(block_info);
2599
2600 let validated = f.validated(tx.clone());
2601 let id = *validated.id();
2602 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2603
2604 assert!(pool.blob_pool.is_empty());
2606 assert_eq!(pool.pending_pool.len(), 1);
2607
2608 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2610 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2611 assert_eq!(internal_tx.subpool, SubPool::Pending);
2612
2613 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2615 pool.set_block_info(block_info);
2616
2617 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2619 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2620 assert_eq!(internal_tx.subpool, SubPool::Blob);
2621
2622 assert_eq!(pool.blob_pool.len(), 1);
2624 assert!(pool.pending_pool.is_empty());
2625 }
2626
2627 #[test]
2628 fn test_promote_valid_tx_with_decreasing_blob_fee() {
2629 let on_chain_balance = U256::MAX;
2630 let on_chain_nonce = 0;
2631 let mut f = MockTransactionFactory::default();
2632 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2633 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2634
2635 let mut block_info = pool.block_info();
2637 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2638 pool.set_block_info(block_info);
2639
2640 let validated = f.validated(tx.clone());
2641 let id = *validated.id();
2642 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2643
2644 assert!(pool.pending_pool.is_empty());
2646 assert_eq!(pool.blob_pool.len(), 1);
2647
2648 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2650 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2651 assert_eq!(internal_tx.subpool, SubPool::Blob);
2652
2653 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2655 pool.set_block_info(block_info);
2656
2657 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2659 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2660 assert_eq!(internal_tx.subpool, SubPool::Pending);
2661
2662 assert_eq!(pool.pending_pool.len(), 1);
2664 assert!(pool.blob_pool.is_empty());
2665 }
2666
2667 #[test]
2668 fn test_queued_count_includes_blob_pool() {
2669 let on_chain_balance = U256::MAX;
2670 let on_chain_nonce = 0;
2671 let mut f = MockTransactionFactory::default();
2672 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2673 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2674
2675 let mut block_info = pool.block_info();
2677 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2678 pool.set_block_info(block_info);
2679
2680 let validated = f.validated(tx);
2681 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2682
2683 assert_eq!(pool.blob_pool.len(), 1);
2684 assert!(pool.pending_pool.is_empty());
2685
2686 assert_eq!(pool.queued_transactions_count(), 1);
2688 }
2689
2690 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
2692 struct PromotionTest {
2693 basefee: u64,
2695 blobfee: u128,
2697 subpool: SubPool,
2699 basefee_update: u64,
2701 blobfee_update: u128,
2703 new_subpool: SubPool,
2705 }
2706
2707 impl PromotionTest {
2708 const fn opposite(&self) -> Self {
2710 Self {
2711 basefee: self.basefee_update,
2712 blobfee: self.blobfee_update,
2713 subpool: self.new_subpool,
2714 blobfee_update: self.blobfee,
2715 basefee_update: self.basefee,
2716 new_subpool: self.subpool,
2717 }
2718 }
2719
2720 fn assert_subpool_lengths<T: TransactionOrdering>(
2721 &self,
2722 pool: &TxPool<T>,
2723 failure_message: String,
2724 check_subpool: SubPool,
2725 ) {
2726 match check_subpool {
2727 SubPool::Blob => {
2728 assert_eq!(pool.blob_pool.len(), 1, "{failure_message}");
2729 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2730 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2731 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2732 }
2733 SubPool::Pending => {
2734 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2735 assert_eq!(pool.pending_pool.len(), 1, "{failure_message}");
2736 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2737 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2738 }
2739 SubPool::BaseFee => {
2740 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2741 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2742 assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}");
2743 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2744 }
2745 SubPool::Queued => {
2746 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2747 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2748 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2749 assert_eq!(pool.queued_pool.len(), 1, "{failure_message}");
2750 }
2751 }
2752 }
2753
2754 fn assert_single_tx_starting_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2758 self.assert_subpool_lengths(
2759 pool,
2760 format!("pool length check failed at start of test: {self:?}"),
2761 self.subpool,
2762 );
2763 }
2764
2765 fn assert_single_tx_ending_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2769 self.assert_subpool_lengths(
2770 pool,
2771 format!("pool length check failed at end of test: {self:?}"),
2772 self.new_subpool,
2773 );
2774 }
2775 }
2776
2777 #[test]
2778 fn test_promote_blob_tx_with_both_pending_fee_updates() {
2779 let on_chain_balance = U256::MAX;
2782 let on_chain_nonce = 0;
2783 let mut f = MockTransactionFactory::default();
2784 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2785
2786 let max_fee_per_blob_gas = tx.max_fee_per_blob_gas().unwrap();
2787 let max_fee_per_gas = tx.max_fee_per_gas() as u64;
2788
2789 let mut expected_promotions = vec![
2791 PromotionTest {
2792 blobfee: max_fee_per_blob_gas + 1,
2793 basefee: max_fee_per_gas + 1,
2794 subpool: SubPool::Blob,
2795 blobfee_update: max_fee_per_blob_gas + 1,
2796 basefee_update: max_fee_per_gas + 1,
2797 new_subpool: SubPool::Blob,
2798 },
2799 PromotionTest {
2800 blobfee: max_fee_per_blob_gas + 1,
2801 basefee: max_fee_per_gas + 1,
2802 subpool: SubPool::Blob,
2803 blobfee_update: max_fee_per_blob_gas,
2804 basefee_update: max_fee_per_gas + 1,
2805 new_subpool: SubPool::Blob,
2806 },
2807 PromotionTest {
2808 blobfee: max_fee_per_blob_gas + 1,
2809 basefee: max_fee_per_gas + 1,
2810 subpool: SubPool::Blob,
2811 blobfee_update: max_fee_per_blob_gas + 1,
2812 basefee_update: max_fee_per_gas,
2813 new_subpool: SubPool::Blob,
2814 },
2815 PromotionTest {
2816 blobfee: max_fee_per_blob_gas + 1,
2817 basefee: max_fee_per_gas + 1,
2818 subpool: SubPool::Blob,
2819 blobfee_update: max_fee_per_blob_gas,
2820 basefee_update: max_fee_per_gas,
2821 new_subpool: SubPool::Pending,
2822 },
2823 PromotionTest {
2824 blobfee: max_fee_per_blob_gas,
2825 basefee: max_fee_per_gas + 1,
2826 subpool: SubPool::Blob,
2827 blobfee_update: max_fee_per_blob_gas,
2828 basefee_update: max_fee_per_gas,
2829 new_subpool: SubPool::Pending,
2830 },
2831 PromotionTest {
2832 blobfee: max_fee_per_blob_gas + 1,
2833 basefee: max_fee_per_gas,
2834 subpool: SubPool::Blob,
2835 blobfee_update: max_fee_per_blob_gas,
2836 basefee_update: max_fee_per_gas,
2837 new_subpool: SubPool::Pending,
2838 },
2839 PromotionTest {
2840 blobfee: max_fee_per_blob_gas,
2841 basefee: max_fee_per_gas,
2842 subpool: SubPool::Pending,
2843 blobfee_update: max_fee_per_blob_gas,
2844 basefee_update: max_fee_per_gas,
2845 new_subpool: SubPool::Pending,
2846 },
2847 ];
2848
2849 let reversed = expected_promotions.iter().map(|test| test.opposite()).collect::<Vec<_>>();
2851 expected_promotions.extend(reversed);
2852
2853 let expected_promotions = expected_promotions.into_iter().collect::<HashSet<_>>();
2855
2856 for promotion_test in &expected_promotions {
2857 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2858
2859 let mut block_info = pool.block_info();
2861
2862 block_info.pending_blob_fee = Some(promotion_test.blobfee);
2863 block_info.pending_basefee = promotion_test.basefee;
2864 pool.set_block_info(block_info);
2865
2866 let validated = f.validated(tx.clone());
2867 let id = *validated.id();
2868 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2869
2870 promotion_test.assert_single_tx_starting_subpool(&pool);
2872
2873 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2875 assert_eq!(
2876 internal_tx.subpool, promotion_test.subpool,
2877 "Subpools do not match at start of test: {promotion_test:?}"
2878 );
2879
2880 block_info.pending_basefee = promotion_test.basefee_update;
2882 block_info.pending_blob_fee = Some(promotion_test.blobfee_update);
2883 pool.set_block_info(block_info);
2884
2885 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2887 assert_eq!(
2888 internal_tx.subpool, promotion_test.new_subpool,
2889 "Subpools do not match at end of test: {promotion_test:?}"
2890 );
2891
2892 promotion_test.assert_single_tx_ending_subpool(&pool);
2894 }
2895 }
2896
2897 #[test]
2898 fn test_insert_pending() {
2899 let on_chain_balance = U256::MAX;
2900 let on_chain_nonce = 0;
2901 let mut f = MockTransactionFactory::default();
2902 let mut pool = AllTransactions::default();
2903 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2904 let valid_tx = f.validated(tx);
2905 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2906 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2907 assert!(updates.is_empty());
2908 assert!(replaced_tx.is_none());
2909 assert!(state.contains(TxState::NO_NONCE_GAPS));
2910 assert!(state.contains(TxState::ENOUGH_BALANCE));
2911 assert_eq!(move_to, SubPool::Pending);
2912
2913 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2914 assert_eq!(inserted.subpool, SubPool::Pending);
2915 }
2916
2917 #[test]
2918 fn test_simple_insert() {
2919 let on_chain_balance = U256::ZERO;
2920 let on_chain_nonce = 0;
2921 let mut f = MockTransactionFactory::default();
2922 let mut pool = AllTransactions::default();
2923 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2924 tx.set_priority_fee(100);
2925 tx.set_max_fee(100);
2926 let valid_tx = f.validated(tx.clone());
2927 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2928 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2929 assert!(updates.is_empty());
2930 assert!(replaced_tx.is_none());
2931 assert!(state.contains(TxState::NO_NONCE_GAPS));
2932 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2933 assert_eq!(move_to, SubPool::Queued);
2934
2935 assert_eq!(pool.len(), 1);
2936 assert!(pool.contains(valid_tx.hash()));
2937 let expected_state = TxState::ENOUGH_FEE_CAP_BLOCK | TxState::NO_NONCE_GAPS;
2938 let inserted = pool.get(valid_tx.id()).unwrap();
2939 assert!(inserted.state.intersects(expected_state));
2940
2941 let res = pool.insert_tx(valid_tx, on_chain_balance, on_chain_nonce);
2943 res.unwrap_err();
2944 assert_eq!(pool.len(), 1);
2945
2946 let valid_tx = f.validated(tx.next());
2947 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2948 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2949
2950 assert!(updates.is_empty());
2951 assert!(replaced_tx.is_none());
2952 assert!(state.contains(TxState::NO_NONCE_GAPS));
2953 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2954 assert_eq!(move_to, SubPool::Queued);
2955
2956 assert!(pool.contains(valid_tx.hash()));
2957 assert_eq!(pool.len(), 2);
2958 let inserted = pool.get(valid_tx.id()).unwrap();
2959 assert!(inserted.state.intersects(expected_state));
2960 }
2961
2962 #[test]
2963 fn test_on_canonical_state_change_no_double_processing() {
2966 let mut tx_factory = MockTransactionFactory::default();
2967 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2968
2969 let tx = MockTransaction::eip1559().with_gas_price(50).with_gas_limit(30_000);
2971 let sender = tx.sender();
2972
2973 let mut block_info = pool.block_info();
2975 block_info.pending_basefee = 100;
2976 pool.set_block_info(block_info);
2977
2978 let validated = tx_factory.validated(tx);
2979 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2980
2981 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2983
2984 assert_eq!(pool.basefee_pool.len(), 1);
2985 assert_eq!(pool.pending_pool.len(), 0);
2986
2987 block_info.pending_basefee = 40;
2991
2992 let mut changed_senders = FxHashMap::default();
2993 changed_senders.insert(
2994 sender_id,
2995 SenderInfo {
2996 state_nonce: 0,
2997 balance: U256::from(20_000_000), },
2999 );
3000
3001 let outcome = pool.on_canonical_state_change(
3002 block_info,
3003 vec![], changed_senders,
3005 PoolUpdateKind::Commit,
3006 );
3007
3008 assert_eq!(pool.pending_pool.len(), 1, "Transaction should be in pending pool");
3010 assert_eq!(pool.basefee_pool.len(), 0, "Transaction should not be in basefee pool");
3011 assert_eq!(outcome.promoted.len(), 1, "Should report exactly one promotion");
3012 }
3013
3014 #[test]
3015 fn test_canonical_state_change_with_basefee_update_regression() {
3018 let mut tx_factory = MockTransactionFactory::default();
3019 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3020
3021 let sender_balance = U256::from(100_000_000);
3023
3024 let tx1 =
3026 MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000).with_nonce(0);
3027 let sender1 = tx1.sender();
3028
3029 let tx2 =
3031 MockTransaction::eip1559().with_gas_price(55).with_gas_limit(21_000).with_nonce(0);
3032 let sender2 = tx2.sender();
3033
3034 let tx3 =
3036 MockTransaction::eip1559().with_gas_price(45).with_gas_limit(21_000).with_nonce(0);
3037 let sender3 = tx3.sender();
3038
3039 let mut block_info = pool.block_info();
3041 block_info.pending_basefee = 70;
3042 pool.set_block_info(block_info);
3043
3044 let validated1 = tx_factory.validated(tx1);
3046 let validated2 = tx_factory.validated(tx2);
3047 let validated3 = tx_factory.validated(tx3);
3048
3049 pool.add_transaction(validated1, sender_balance, 0, None).unwrap();
3050 pool.add_transaction(validated2, sender_balance, 0, None).unwrap();
3051 pool.add_transaction(validated3, sender_balance, 0, None).unwrap();
3052
3053 let sender1_id = tx_factory.ids.sender_id(&sender1).unwrap();
3054 let sender2_id = tx_factory.ids.sender_id(&sender2).unwrap();
3055 let sender3_id = tx_factory.ids.sender_id(&sender3).unwrap();
3056
3057 assert_eq!(pool.basefee_pool.len(), 3, "All txs should be in basefee pool");
3059 assert_eq!(pool.pending_pool.len(), 0, "No txs should be in pending pool");
3060
3061 block_info.pending_basefee = 50;
3064
3065 let mut changed_senders = FxHashMap::default();
3067 changed_senders.insert(
3068 sender1_id,
3069 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
3070 );
3071 changed_senders.insert(
3072 sender2_id,
3073 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
3074 );
3075 changed_senders.insert(
3076 sender3_id,
3077 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
3078 );
3079
3080 let outcome = pool.on_canonical_state_change(
3081 block_info,
3082 vec![],
3083 changed_senders,
3084 PoolUpdateKind::Commit,
3085 );
3086
3087 assert_eq!(pool.pending_pool.len(), 2, "tx1 and tx2 should be promoted");
3089 assert_eq!(pool.basefee_pool.len(), 1, "tx3 should remain in basefee");
3090
3091 assert_eq!(
3094 outcome.promoted.len(),
3095 2,
3096 "Should report exactly 2 promotions, not double-counted"
3097 );
3098
3099 let promoted_prices: Vec<u128> =
3101 outcome.promoted.iter().map(|tx| tx.max_fee_per_gas()).collect();
3102 assert!(promoted_prices.contains(&60));
3103 assert!(promoted_prices.contains(&55));
3104 }
3105
3106 #[test]
3107 fn test_basefee_decrease_with_empty_senders() {
3108 let mut tx_factory = MockTransactionFactory::default();
3111 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3112
3113 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
3115
3116 let mut block_info = pool.block_info();
3118 block_info.pending_basefee = 100;
3119 pool.set_block_info(block_info);
3120
3121 let validated = tx_factory.validated(tx);
3123 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
3124
3125 assert_eq!(pool.basefee_pool.len(), 1);
3126 assert_eq!(pool.pending_pool.len(), 0);
3127
3128 block_info.pending_basefee = 50;
3130 let outcome = pool.on_canonical_state_change(
3131 block_info,
3132 vec![],
3133 FxHashMap::default(), PoolUpdateKind::Commit,
3135 );
3136
3137 assert_eq!(pool.pending_pool.len(), 1, "Fee decrease should promote tx");
3139 assert_eq!(pool.basefee_pool.len(), 0);
3140 assert_eq!(outcome.promoted.len(), 1, "Should report promotion from fee update");
3141 }
3142
3143 #[test]
3144 fn test_basefee_decrease_account_makes_unfundable() {
3145 let mut tx_factory = MockTransactionFactory::default();
3148 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3149
3150 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
3151 let sender = tx.sender();
3152
3153 let mut block_info = pool.block_info();
3155 block_info.pending_basefee = 100;
3156 pool.set_block_info(block_info);
3157
3158 let validated = tx_factory.validated(tx);
3159 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
3160 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
3161
3162 assert_eq!(pool.basefee_pool.len(), 1);
3163
3164 block_info.pending_basefee = 50;
3166 let mut changed_senders = FxHashMap::default();
3167 changed_senders.insert(
3168 sender_id,
3169 SenderInfo {
3170 state_nonce: 0,
3171 balance: U256::from(100), },
3173 );
3174
3175 let outcome = pool.on_canonical_state_change(
3176 block_info,
3177 vec![],
3178 changed_senders,
3179 PoolUpdateKind::Commit,
3180 );
3181
3182 assert_eq!(pool.pending_pool.len(), 0, "Unfunded tx should not be in pending");
3184 assert_eq!(pool.basefee_pool.len(), 0, "Tx no longer in basefee pool");
3185 assert_eq!(pool.queued_pool.len(), 1, "Unfunded tx should be in queued pool");
3186
3187 let tx_count = pool.all_transactions.txs.len();
3189 assert_eq!(tx_count, 1, "Transaction should still be in pool (in queued)");
3190
3191 assert_eq!(outcome.promoted.len(), 0, "Should not report promotion");
3192 assert_eq!(outcome.discarded.len(), 0, "Queued tx is not reported as discarded");
3193 }
3194
3195 #[test]
3196 fn insert_already_imported() {
3197 let on_chain_balance = U256::ZERO;
3198 let on_chain_nonce = 0;
3199 let mut f = MockTransactionFactory::default();
3200 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3201 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3202 let tx = f.validated(tx);
3203 pool.add_transaction(tx.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3204 match pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap_err().kind {
3205 PoolErrorKind::AlreadyImported => {}
3206 _ => unreachable!(),
3207 }
3208 }
3209
3210 #[test]
3211 fn insert_replace() {
3212 let on_chain_balance = U256::ZERO;
3213 let on_chain_nonce = 0;
3214 let mut f = MockTransactionFactory::default();
3215 let mut pool = AllTransactions::default();
3216 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3217 let first = f.validated(tx.clone());
3218 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3219 let replacement = f.validated(tx.rng_hash().inc_price());
3220 let InsertOk { updates, replaced_tx, .. } =
3221 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
3222 assert!(updates.is_empty());
3223 let replaced = replaced_tx.unwrap();
3224 assert_eq!(replaced.0.hash(), first.hash());
3225
3226 assert!(!pool.contains(first.hash()));
3228 assert!(pool.contains(replacement.hash()));
3229 assert_eq!(pool.len(), 1);
3230 }
3231
3232 #[test]
3233 fn insert_replace_txpool() {
3234 let on_chain_balance = U256::ZERO;
3235 let on_chain_nonce = 0;
3236 let mut f = MockTransactionFactory::default();
3237 let mut pool = TxPool::mock();
3238
3239 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3240 let first = f.validated(tx.clone());
3241 let first_added =
3242 pool.add_transaction(first, on_chain_balance, on_chain_nonce, None).unwrap();
3243 let replacement = f.validated(tx.rng_hash().inc_price());
3244 let replacement_added = pool
3245 .add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce, None)
3246 .unwrap();
3247
3248 assert!(!pool.contains(first_added.hash()));
3250 assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));
3252
3253 assert!(pool.contains(replacement.hash()));
3254 let size = pool.size();
3255 assert_eq!(size.total, 1);
3256 size.assert_invariants();
3257 }
3258
3259 #[test]
3260 fn insert_replace_underpriced() {
3261 let on_chain_balance = U256::ZERO;
3262 let on_chain_nonce = 0;
3263 let mut f = MockTransactionFactory::default();
3264 let mut pool = AllTransactions::default();
3265 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3266 let first = f.validated(tx.clone());
3267 let _res = pool.insert_tx(first, on_chain_balance, on_chain_nonce);
3268 let mut replacement = f.validated(tx.rng_hash());
3269 replacement.transaction = replacement.transaction.decr_price();
3270 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3271 assert!(matches!(err, InsertErr::Underpriced { .. }));
3272 }
3273
3274 #[test]
3275 fn insert_replace_underpriced_not_enough_bump() {
3276 let on_chain_balance = U256::ZERO;
3277 let on_chain_nonce = 0;
3278 let mut f = MockTransactionFactory::default();
3279 let mut pool = AllTransactions::default();
3280 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3281 tx.set_priority_fee(100);
3282 tx.set_max_fee(100);
3283 let first = f.validated(tx.clone());
3284 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3285 let mut replacement = f.validated(tx.rng_hash().inc_price());
3286
3287 replacement.transaction.set_priority_fee(109);
3289 replacement.transaction.set_max_fee(109);
3290 let err =
3291 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3292 assert!(matches!(err, InsertErr::Underpriced { .. }));
3293 assert!(pool.contains(first.hash()));
3295 assert_eq!(pool.len(), 1);
3296
3297 replacement.transaction.set_priority_fee(110);
3299 replacement.transaction.set_max_fee(109);
3300 let err =
3301 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3302 assert!(matches!(err, InsertErr::Underpriced { .. }));
3303 assert!(pool.contains(first.hash()));
3304 assert_eq!(pool.len(), 1);
3305
3306 replacement.transaction.set_priority_fee(109);
3308 replacement.transaction.set_max_fee(110);
3309 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3310 assert!(matches!(err, InsertErr::Underpriced { .. }));
3311 assert!(pool.contains(first.hash()));
3312 assert_eq!(pool.len(), 1);
3313 }
3314
3315 #[test]
3316 fn insert_replace_underpriced_rounds_up_minimum_bump() {
3317 let on_chain_balance = U256::ZERO;
3318 let on_chain_nonce = 0;
3319 let mut f = MockTransactionFactory::default();
3320 let mut pool = AllTransactions { minimal_protocol_basefee: 0, ..Default::default() };
3321 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3322 tx.set_priority_fee(1);
3323 tx.set_max_fee(1);
3324
3325 let first = f.validated(tx.clone());
3326 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3327
3328 let mut replacement = f.validated(tx.rng_hash().inc_price());
3329 replacement.transaction.set_priority_fee(1);
3330 replacement.transaction.set_max_fee(2);
3331 let err =
3332 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3333 assert!(matches!(err, InsertErr::Underpriced { .. }));
3334 assert!(pool.contains(first.hash()));
3335 assert_eq!(pool.len(), 1);
3336
3337 replacement.transaction.set_priority_fee(2);
3338 replacement.transaction.set_max_fee(2);
3339 let replaced = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap();
3340 assert!(replaced.replaced_tx.is_some());
3341 assert_eq!(pool.len(), 1);
3342 }
3343
3344 #[test]
3345 fn insert_conflicting_type_normal_to_blob() {
3346 let on_chain_balance = U256::from(10_000);
3347 let on_chain_nonce = 0;
3348 let mut f = MockTransactionFactory::default();
3349 let mut pool = AllTransactions::default();
3350 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3351 let first = f.validated(tx.clone());
3352 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3353 let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3354 let blob = f.validated(tx);
3355 let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
3356 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3357 }
3358
3359 #[test]
3360 fn insert_conflicting_type_blob_to_normal() {
3361 let on_chain_balance = U256::from(10_000);
3362 let on_chain_nonce = 0;
3363 let mut f = MockTransactionFactory::default();
3364 let mut pool = AllTransactions::default();
3365 let tx = MockTransaction::eip4844().inc_price().inc_limit();
3366 let first = f.validated(tx.clone());
3367 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3368 let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3369 let tx = f.validated(tx);
3370 let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
3371 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3372 }
3373
3374 #[test]
3376 fn insert_previous() {
3377 let on_chain_balance = U256::ZERO;
3378 let on_chain_nonce = 0;
3379 let mut f = MockTransactionFactory::default();
3380 let mut pool = AllTransactions::default();
3381 let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
3382 let first = f.validated(tx.clone());
3383 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3384
3385 let first_in_pool = pool.get(first.id()).unwrap();
3386
3387 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3389
3390 let prev = f.validated(tx.prev());
3391 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3392 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3393
3394 assert!(updates.is_empty());
3396 assert!(replaced_tx.is_none());
3397 assert!(state.contains(TxState::NO_NONCE_GAPS));
3398 assert_eq!(move_to, SubPool::Queued);
3399
3400 let first_in_pool = pool.get(first.id()).unwrap();
3401 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3403 }
3404
3405 #[test]
3407 fn insert_with_updates() {
3408 let on_chain_balance = U256::from(10_000);
3409 let on_chain_nonce = 0;
3410 let mut f = MockTransactionFactory::default();
3411 let mut pool = AllTransactions::default();
3412 let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
3413 let first = f.validated(tx.clone());
3414 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3415
3416 let first_in_pool = pool.get(first.id()).unwrap();
3417 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3419 assert_eq!(SubPool::Queued, first_in_pool.subpool);
3420
3421 let prev = f.validated(tx.prev());
3422 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3423 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3424
3425 assert_eq!(updates.len(), 1);
3427 assert!(replaced_tx.is_none());
3428 assert!(state.contains(TxState::NO_NONCE_GAPS));
3429 assert_eq!(move_to, SubPool::Pending);
3430
3431 let first_in_pool = pool.get(first.id()).unwrap();
3432 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3434 assert_eq!(SubPool::Pending, first_in_pool.subpool);
3435 }
3436
3437 #[test]
3438 fn insert_previous_blocking() {
3439 let on_chain_balance = U256::from(1_000);
3440 let on_chain_nonce = 0;
3441 let mut f = MockTransactionFactory::default();
3442 let mut pool = AllTransactions::default();
3443 pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
3444 let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
3445 let first = f.validated(tx.clone());
3446
3447 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3448
3449 let first_in_pool = pool.get(first.id()).unwrap();
3450
3451 assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
3452 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3454
3455 let prev = f.validated(tx.prev());
3456 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3457 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3458
3459 assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3460 assert!(updates.is_empty());
3462 assert!(replaced_tx.is_none());
3463 assert!(state.contains(TxState::NO_NONCE_GAPS));
3464 assert_eq!(move_to, SubPool::BaseFee);
3465
3466 let first_in_pool = pool.get(first.id()).unwrap();
3467 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3469 }
3470
3471 #[test]
3472 fn rejects_spammer() {
3473 let on_chain_balance = U256::from(1_000);
3474 let on_chain_nonce = 0;
3475 let mut f = MockTransactionFactory::default();
3476 let mut pool = AllTransactions::default();
3477
3478 let mut tx = MockTransaction::eip1559();
3479 let unblocked_tx = tx.clone();
3480 for _ in 0..pool.max_account_slots {
3481 tx = tx.next();
3482 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
3483 }
3484
3485 assert_eq!(
3486 pool.max_account_slots,
3487 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3488 );
3489
3490 let err =
3491 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
3492 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
3493
3494 assert!(pool
3495 .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
3496 .is_ok());
3497 }
3498
3499 #[test]
3500 fn allow_local_spamming() {
3501 let on_chain_balance = U256::from(1_000);
3502 let on_chain_nonce = 0;
3503 let mut f = MockTransactionFactory::default();
3504 let mut pool = AllTransactions::default();
3505
3506 let mut tx = MockTransaction::eip1559();
3507 for _ in 0..pool.max_account_slots {
3508 tx = tx.next();
3509 pool.insert_tx(
3510 f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
3511 on_chain_balance,
3512 on_chain_nonce,
3513 )
3514 .unwrap();
3515 }
3516
3517 assert_eq!(
3518 pool.max_account_slots,
3519 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3520 );
3521
3522 pool.insert_tx(
3523 f.validated_with_origin(TransactionOrigin::Local, tx.next()),
3524 on_chain_balance,
3525 on_chain_nonce,
3526 )
3527 .unwrap();
3528 }
3529
3530 #[test]
3531 fn reject_tx_over_gas_limit() {
3532 let on_chain_balance = U256::from(1_000);
3533 let on_chain_nonce = 0;
3534 let mut f = MockTransactionFactory::default();
3535 let mut pool = AllTransactions::default();
3536
3537 let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
3538
3539 assert!(matches!(
3540 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
3541 Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
3542 ));
3543 }
3544
3545 #[test]
3546 fn test_tx_equal_gas_limit() {
3547 let on_chain_balance = U256::from(1_000);
3548 let on_chain_nonce = 0;
3549 let mut f = MockTransactionFactory::default();
3550 let mut pool = AllTransactions::default();
3551
3552 let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
3553
3554 let InsertOk { state, .. } =
3555 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
3556 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
3557 }
3558
3559 #[test]
3560 fn full_update_sender_heuristic() {
3561 let mut pool = AllTransactions::<MockTransaction>::default();
3562 for sender in 0..(FULL_UPDATE_MIN_SENDERS - 1) as u64 {
3563 pool.tx_counter.insert(sender.into(), 1);
3564 }
3565
3566 assert!(!pool.should_update_all_senders(250));
3568 assert!(pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS - 1));
3569
3570 pool.tx_counter.insert((FULL_UPDATE_MIN_SENDERS as u64 - 1).into(), 1);
3571
3572 assert!(
3574 !pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS / FULL_UPDATE_SENDER_RATIO - 1)
3575 );
3576 assert!(pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS / FULL_UPDATE_SENDER_RATIO));
3577 assert!(pool.should_update_all_senders(FULL_UPDATE_MIN_SENDERS + 1));
3578 }
3579
3580 #[test]
3581 fn update_only_visits_changed_senders_when_fees_are_unchanged() {
3582 let mut f = MockTransactionFactory::default();
3583 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3584
3585 let a = f.validated(MockTransaction::eip1559().inc_price_by(10));
3587 let b = f.validated(MockTransaction::eip1559().inc_price_by(10));
3588 let (a_id, b_id) = (*a.id(), *b.id());
3589 assert_ne!(a_id.sender, b_id.sender);
3590 pool.add_transaction(a, U256::from(1_000_000), 0, None).unwrap();
3591 pool.add_transaction(b, U256::from(1_000_000), 0, None).unwrap();
3592
3593 pool.all_transactions.update(&Default::default());
3595 assert_eq!(pool.all_transactions.last_full_update_fees, pool.all_transactions.pending_fees);
3596
3597 let b_state_before = pool.all_transactions.txs.get(&b_id).unwrap().state;
3600 let mut changed = FxHashMap::default();
3601 changed.insert(a_id.sender, SenderInfo { state_nonce: 1, balance: U256::from(1_000_000) });
3602
3603 let produced = pool.all_transactions.update(&changed);
3604
3605 assert_eq!(produced.len(), 1, "expected exactly the changed sender's update");
3606 assert_eq!(produced[0].id, a_id);
3607 assert!(matches!(produced[0].destination, Destination::Discard));
3608 assert_eq!(
3609 pool.all_transactions.txs.get(&b_id).unwrap().state,
3610 b_state_before,
3611 "unchanged sender was modified"
3612 );
3613 }
3614
3615 #[test]
3616 fn changed_sender_update_matches_full_update() {
3617 let senders = [
3618 address!("0x000000000000000000000000000000000000000a"),
3619 address!("0x000000000000000000000000000000000000000b"),
3620 address!("0x000000000000000000000000000000000000000c"),
3621 ];
3622 let starting_nonces = [5, 11, 17];
3623
3624 let build_pool = || {
3625 let mut f = MockTransactionFactory::default();
3626 let mut pool = AllTransactions::default();
3627 pool.pending_fees.base_fee = 1;
3628
3629 for (sender, starting_nonce) in senders.into_iter().zip(starting_nonces) {
3630 for nonce in starting_nonce..starting_nonce + 3 {
3631 let tx = MockTransaction::eip1559()
3632 .with_sender(sender)
3633 .with_nonce(nonce)
3634 .inc_price_by(10)
3635 .rng_hash();
3636 pool.insert_tx(f.validated(tx), U256::from(1_000_000), starting_nonce).unwrap();
3637 }
3638 }
3639
3640 pool.update(&Default::default());
3642 let sender_ids = senders.map(|sender| f.ids.sender_id(&sender).unwrap());
3643 (pool, sender_ids)
3644 };
3645
3646 let (mut changed_senders_only, sender_ids) = build_pool();
3647 let (mut full_update, full_update_sender_ids) = build_pool();
3648 assert_eq!(sender_ids, full_update_sender_ids);
3649
3650 let mut changed = FxHashMap::default();
3651 changed.insert(
3652 sender_ids[0],
3653 SenderInfo { state_nonce: starting_nonces[0] + 1, balance: U256::from(1_000_000) },
3654 );
3655 changed.insert(
3656 sender_ids[1],
3657 SenderInfo { state_nonce: starting_nonces[1], balance: U256::ZERO },
3658 );
3659
3660 let mut changed_sender_updates = changed_senders_only.update(&changed);
3661 full_update.last_full_update_fees.base_fee =
3663 full_update.last_full_update_fees.base_fee.saturating_add(1);
3664 let mut full_updates = full_update.update(&changed);
3665
3666 let update_key = |update: &PoolUpdate| {
3667 let destination = match &update.destination {
3668 Destination::Discard => None,
3669 Destination::Pool(pool) => Some(*pool),
3670 };
3671 (update.id, update.current, destination)
3672 };
3673 changed_sender_updates.sort_unstable_by_key(|update| update.id);
3674 full_updates.sort_unstable_by_key(|update| update.id);
3675 assert_eq!(
3676 changed_sender_updates.iter().map(update_key).collect::<Vec<_>>(),
3677 full_updates.iter().map(update_key).collect::<Vec<_>>()
3678 );
3679
3680 let metadata = |pool: &AllTransactions<MockTransaction>| {
3681 pool.txs
3682 .iter()
3683 .map(|(id, tx)| (*id, tx.state, tx.subpool, tx.cumulative_cost))
3684 .collect::<Vec<_>>()
3685 };
3686 assert_eq!(metadata(&changed_senders_only), metadata(&full_update));
3687 }
3688
3689 #[test]
3690 fn update_visits_every_sender_when_the_base_fee_moved() {
3691 let mut f = MockTransactionFactory::default();
3692 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3693
3694 let tx = MockTransaction::eip1559().inc_price_by(10);
3695 let validated = f.validated(tx.clone());
3696 let id = *validated.id();
3697 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3698
3699 pool.all_transactions.update(&Default::default());
3700 assert!(pool
3701 .all_transactions
3702 .txs
3703 .get(&id)
3704 .unwrap()
3705 .state
3706 .contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3707
3708 pool.all_transactions.pending_fees.base_fee = (tx.max_fee_per_gas() + 1) as u64;
3711 pool.all_transactions.update(&Default::default());
3712
3713 assert!(
3714 !pool
3715 .all_transactions
3716 .txs
3717 .get(&id)
3718 .unwrap()
3719 .state
3720 .contains(TxState::ENOUGH_FEE_CAP_BLOCK),
3721 "fee change was not applied to an unchanged sender"
3722 );
3723 }
3724
3725 #[test]
3726 fn blob_fee_change_records_full_update() {
3727 let mut pool = AllTransactions::<MockTransaction>::default();
3728 pool.pending_fees.blob_fee += 1;
3729
3730 pool.update(&Default::default());
3731
3732 assert_eq!(pool.last_full_update_fees, pool.pending_fees);
3733 }
3734
3735 #[test]
3736 fn gap_fill_rechecks_descendant_fee_eligibility() {
3737 let mut f = MockTransactionFactory::default();
3738 let mut pool = AllTransactions::default();
3739 let sender = address!("0x000000000000000000000000000000000000000d");
3740 let balance = U256::MAX;
3741
3742 pool.pending_fees.base_fee = 100;
3743 let descendant = MockTransaction::eip1559()
3744 .with_sender(sender)
3745 .with_nonce(1)
3746 .with_gas_limit(21_000)
3747 .with_max_fee(150)
3748 .with_priority_fee(1)
3749 .rng_hash();
3750 let descendant = f.validated(descendant);
3751 let descendant_id = *descendant.id();
3752 pool.insert_tx(descendant, balance, 0).unwrap();
3753 pool.update(&Default::default());
3754
3755 pool.pending_fees.base_fee = 200;
3758 pool.update(&Default::default());
3759 assert!(pool.get(&descendant_id).unwrap().state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3760
3761 let predecessor = MockTransaction::eip1559()
3762 .with_sender(sender)
3763 .with_nonce(0)
3764 .with_gas_limit(21_000)
3765 .with_max_fee(250)
3766 .with_priority_fee(1)
3767 .rng_hash();
3768 let InsertOk { move_to, .. } =
3769 pool.insert_tx(f.validated(predecessor), balance, 0).unwrap();
3770
3771 assert_eq!(move_to, SubPool::Pending);
3772 let descendant = pool.get(&descendant_id).unwrap();
3773 assert_eq!(descendant.subpool, SubPool::BaseFee);
3774 assert!(!descendant.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3775 assert!(descendant.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3776 }
3777
3778 #[test]
3779 fn base_fee_update_unparks_all_descendants() {
3780 let mut f = MockTransactionFactory::default();
3781 let mut pool = AllTransactions::default();
3782 let sender = address!("0x000000000000000000000000000000000000000e");
3783 let mut ids = Vec::new();
3784
3785 pool.pending_fees.base_fee = 200;
3786 for nonce in 0..3 {
3787 let tx = MockTransaction::eip1559()
3788 .with_sender(sender)
3789 .with_nonce(nonce)
3790 .with_gas_limit(21_000)
3791 .with_max_fee(150)
3792 .with_priority_fee(1)
3793 .rng_hash();
3794 let tx = f.validated(tx);
3795 ids.push(*tx.id());
3796 pool.insert_tx(tx, U256::MAX, 0).unwrap();
3797 }
3798
3799 pool.pending_fees.base_fee = 100;
3800 pool.update(&Default::default());
3801
3802 for id in ids {
3803 assert_eq!(pool.get(&id).unwrap().subpool, SubPool::Pending);
3804 }
3805 }
3806
3807 #[test]
3808 fn fee_update_keeps_descendants_of_underpriced_transaction_parked() {
3809 let mut f = MockTransactionFactory::default();
3810 let mut pool = AllTransactions::default();
3811 let sender = address!("0x0000000000000000000000000000000000000010");
3812 let fee_caps = [150, 50, 150];
3813 let mut ids = Vec::new();
3814
3815 pool.pending_fees.base_fee = 200;
3816 for (nonce, fee_cap) in fee_caps.into_iter().enumerate() {
3817 let tx = MockTransaction::eip1559()
3818 .with_sender(sender)
3819 .with_nonce(nonce as u64)
3820 .with_gas_limit(21_000)
3821 .with_max_fee(fee_cap)
3822 .with_priority_fee(1)
3823 .rng_hash();
3824 let tx = f.validated(tx);
3825 ids.push(*tx.id());
3826 pool.insert_tx(tx, U256::MAX, 0).unwrap();
3827 }
3828
3829 pool.pending_fees.base_fee = 100;
3830 pool.update(&Default::default());
3831
3832 assert_eq!(pool.get(&ids[0]).unwrap().subpool, SubPool::Pending);
3833 assert_eq!(pool.get(&ids[1]).unwrap().subpool, SubPool::BaseFee);
3834 assert_eq!(pool.get(&ids[2]).unwrap().subpool, SubPool::Queued);
3835 }
3836
3837 #[test]
3838 fn blob_fee_update_unparks_all_descendants() {
3839 let mut f = MockTransactionFactory::default();
3840 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3841 let sender = address!("0x000000000000000000000000000000000000000f");
3842 let mut block_info = pool.block_info();
3843 block_info.pending_blob_fee = Some(200);
3844 pool.set_block_info(block_info);
3845
3846 for nonce in 0..3 {
3847 let tx = MockTransaction::eip4844()
3848 .with_sender(sender)
3849 .with_nonce(nonce)
3850 .with_gas_limit(21_000)
3851 .with_max_fee(1_000)
3852 .with_priority_fee(1)
3853 .with_blob_fee(150)
3854 .rng_hash();
3855 pool.add_transaction(f.validated(tx), U256::MAX, 0, None).unwrap();
3856 }
3857 assert_eq!(pool.blob_pool.len(), 3);
3858
3859 block_info.pending_blob_fee = Some(100);
3860 pool.on_canonical_state_change(
3861 block_info,
3862 Vec::new(),
3863 FxHashMap::default(),
3864 PoolUpdateKind::Commit,
3865 );
3866
3867 let nonces = pool.best_transactions().map(|tx| tx.nonce()).collect::<Vec<_>>();
3868 assert_eq!(nonces, vec![0, 1, 2]);
3869 }
3870
3871 #[test]
3872 fn update_basefee_subpools() {
3873 let mut f = MockTransactionFactory::default();
3874 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3875
3876 let tx = MockTransaction::eip1559().inc_price_by(10);
3877 let validated = f.validated(tx.clone());
3878 let id = *validated.id();
3879 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3880
3881 assert_eq!(pool.pending_pool.len(), 1);
3882
3883 pool.update_basefee((tx.max_fee_per_gas() + 1) as u64, |_| {});
3884
3885 assert!(pool.pending_pool.is_empty());
3886 assert_eq!(pool.basefee_pool.len(), 1);
3887
3888 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3889 }
3890
3891 #[test]
3892 fn update_basefee_subpools_setting_block_info() {
3893 let mut f = MockTransactionFactory::default();
3894 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3895
3896 let tx = MockTransaction::eip1559().inc_price_by(10);
3897 let validated = f.validated(tx.clone());
3898 let id = *validated.id();
3899 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3900
3901 assert_eq!(pool.pending_pool.len(), 1);
3902
3903 let mut block_info = pool.block_info();
3905 block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
3906 pool.set_block_info(block_info);
3907
3908 assert!(pool.pending_pool.is_empty());
3909 assert_eq!(pool.basefee_pool.len(), 1);
3910
3911 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3912 }
3913
3914 #[test]
3915 fn basefee_decrease_promotes_affordable_and_keeps_unaffordable() {
3916 use alloy_primitives::address;
3917 let mut f = MockTransactionFactory::default();
3918 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3919
3920 let sender_a = address!("0x000000000000000000000000000000000000000a");
3923 let sender_b = address!("0x000000000000000000000000000000000000000b");
3924 let sender_c = address!("0x000000000000000000000000000000000000000c");
3925
3926 let tx1 = MockTransaction::eip1559()
3927 .set_sender(sender_a)
3928 .set_nonce(0)
3929 .set_max_fee(500)
3930 .inc_limit();
3931 let tx2 = MockTransaction::eip1559()
3932 .set_sender(sender_b)
3933 .set_nonce(0)
3934 .set_max_fee(600)
3935 .inc_limit();
3936 let tx3 = MockTransaction::eip1559()
3937 .set_sender(sender_c)
3938 .set_nonce(0)
3939 .set_max_fee(400)
3940 .inc_limit();
3941
3942 let mut block_info = pool.block_info();
3944 block_info.pending_basefee = 700;
3945 pool.set_block_info(block_info);
3946
3947 let validated1 = f.validated(tx1);
3948 let validated2 = f.validated(tx2);
3949 let validated3 = f.validated(tx3);
3950 let id1 = *validated1.id();
3951 let id2 = *validated2.id();
3952 let id3 = *validated3.id();
3953
3954 pool.add_transaction(validated1, U256::from(10_000), 0, None).unwrap();
3958 pool.add_transaction(validated2, U256::from(10_000), 0, None).unwrap();
3959 pool.add_transaction(validated3, U256::from(10_000), 0, None).unwrap();
3960
3961 println!("Basefee pool len: {}", pool.basefee_pool.len());
3963 println!("Pending pool len: {}", pool.pending_pool.len());
3964 println!("tx1 subpool: {:?}", pool.all_transactions.txs.get(&id1).unwrap().subpool);
3965 println!("tx2 subpool: {:?}", pool.all_transactions.txs.get(&id2).unwrap().subpool);
3966 println!("tx3 subpool: {:?}", pool.all_transactions.txs.get(&id3).unwrap().subpool);
3967
3968 assert_eq!(pool.basefee_pool.len(), 3);
3970 assert_eq!(pool.pending_pool.len(), 0);
3971 assert_eq!(pool.all_transactions.txs.get(&id1).unwrap().subpool, SubPool::BaseFee);
3972 assert_eq!(pool.all_transactions.txs.get(&id2).unwrap().subpool, SubPool::BaseFee);
3973 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3974
3975 let mut block_info = pool.block_info();
3977 block_info.pending_basefee = 450; pool.set_block_info(block_info);
3980
3981 assert_eq!(pool.basefee_pool.len(), 1);
3986 assert_eq!(pool.pending_pool.len(), 2);
3987
3988 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3990
3991 let tx1_meta = pool.all_transactions.txs.get(&id1).unwrap();
3993 let tx2_meta = pool.all_transactions.txs.get(&id2).unwrap();
3994 assert_eq!(tx1_meta.subpool, SubPool::Pending);
3995 assert_eq!(tx2_meta.subpool, SubPool::Pending);
3996 assert!(tx1_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3997 assert!(tx2_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3998
3999 let best: Vec<_> = pool.best_transactions().take(3).collect();
4001 assert_eq!(best.len(), 2); assert!(best.iter().any(|tx| tx.id() == &id1));
4003 assert!(best.iter().any(|tx| tx.id() == &id2));
4004 }
4005
4006 #[test]
4007 fn apply_fee_updates_records_promotions_after_basefee_drop() {
4008 let mut f = MockTransactionFactory::default();
4009 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4010
4011 let tx = MockTransaction::eip1559()
4012 .with_gas_limit(21_000)
4013 .with_max_fee(500)
4014 .with_priority_fee(1);
4015 let validated = f.validated(tx);
4016 let id = *validated.id();
4017 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4018
4019 assert_eq!(pool.pending_pool.len(), 1);
4020
4021 pool.update_basefee(600, |_| {});
4023 assert!(pool.pending_pool.is_empty());
4024 assert_eq!(pool.basefee_pool.len(), 1);
4025
4026 let prev_base_fee = 600;
4027 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4028
4029 pool.all_transactions.pending_fees.base_fee = 400;
4031
4032 let mut outcome = UpdateOutcome::default();
4033 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
4034
4035 assert_eq!(pool.pending_pool.len(), 1);
4036 assert!(pool.basefee_pool.is_empty());
4037 assert_eq!(outcome.promoted.len(), 1);
4038 assert_eq!(outcome.promoted[0].id(), &id);
4039 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
4040 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
4041
4042 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
4043 assert_eq!(tx_meta.subpool, SubPool::Pending);
4044 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4045 }
4046
4047 #[test]
4048 fn update_blob_fee_parks_pending_when_base_fee_falls_in_the_same_block() {
4049 let mut f = MockTransactionFactory::default();
4050 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4051
4052 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4053 let initial_base_fee = 100u64;
4054 pool.all_transactions.pending_fees.base_fee = initial_base_fee;
4055
4056 let tx = MockTransaction::eip4844()
4058 .with_max_fee(500)
4059 .with_priority_fee(1)
4060 .with_blob_fee(initial_blob_fee + 100);
4061 let validated = f.validated(tx.clone());
4062 let id = *validated.id();
4063 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4064 assert_eq!(pool.pending_pool.len(), 1);
4065
4066 let raised_blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
4069 pool.all_transactions.pending_fees.base_fee = initial_base_fee - 1;
4070 pool.update_blob_fee(raised_blob_fee, Ordering::Less, |_| {});
4071
4072 assert!(pool.pending_pool.is_empty(), "transaction was left in the pending pool");
4073 assert_eq!(pool.blob_pool.len(), 1);
4074
4075 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
4076 assert!(
4077 !tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4078 "blob fee cap flag was not cleared"
4079 );
4080 assert_eq!(tx_meta.subpool, SubPool::Blob);
4081 }
4082
4083 #[test]
4084 fn update_blob_fee_demotes_and_promotes_when_base_fee_falls() {
4085 let mut f = MockTransactionFactory::default();
4086 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4087
4088 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4089 let initial_base_fee = 600;
4090 pool.all_transactions.pending_fees.base_fee = initial_base_fee;
4091
4092 let tx_to_demote = MockTransaction::eip4844()
4093 .with_max_fee(700)
4094 .with_priority_fee(1)
4095 .with_blob_fee(initial_blob_fee + 100);
4096 let validated = f.validated(tx_to_demote);
4097 let demoted_id = *validated.id();
4098 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4099
4100 let tx_to_promote = MockTransaction::eip4844()
4101 .with_max_fee(500)
4102 .with_priority_fee(1)
4103 .with_blob_fee(initial_blob_fee + 300);
4104 let validated = f.validated(tx_to_promote);
4105 let promoted_id = *validated.id();
4106 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4107
4108 assert_eq!(pool.pending_pool.len(), 1);
4109 assert_eq!(pool.blob_pool.len(), 1);
4110
4111 let raised_blob_fee = initial_blob_fee + 200;
4114 pool.all_transactions.pending_fees.base_fee = 400;
4115 let mut promoted = Vec::new();
4116 pool.update_blob_fee(raised_blob_fee, Ordering::Less, |tx| promoted.push(*tx.id()));
4117
4118 assert_eq!(pool.pending_pool.len(), 1);
4119 assert_eq!(pool.blob_pool.len(), 1);
4120 assert_eq!(promoted, vec![promoted_id]);
4121
4122 assert_eq!(pool.all_transactions.txs.get(&demoted_id).unwrap().subpool, SubPool::Blob);
4123 assert_eq!(pool.all_transactions.txs.get(&promoted_id).unwrap().subpool, SubPool::Pending);
4124 }
4125
4126 #[test]
4127 fn apply_fee_updates_records_promotions_after_blob_fee_drop() {
4128 let mut f = MockTransactionFactory::default();
4129 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4130
4131 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4132
4133 let tx = MockTransaction::eip4844().with_blob_fee(initial_blob_fee + 100);
4134 let validated = f.validated(tx.clone());
4135 let id = *validated.id();
4136 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4137
4138 assert_eq!(pool.pending_pool.len(), 1);
4139
4140 let increased_blob_fee = tx.max_fee_per_blob_gas().unwrap() + 200;
4142 pool.update_blob_fee(increased_blob_fee, Ordering::Equal, |_| {});
4143 assert!(pool.pending_pool.is_empty());
4144 assert_eq!(pool.blob_pool.len(), 1);
4145
4146 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
4147 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4148
4149 pool.all_transactions.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
4151
4152 let mut outcome = UpdateOutcome::default();
4153 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
4154
4155 assert_eq!(pool.pending_pool.len(), 1);
4156 assert!(pool.blob_pool.is_empty());
4157 assert_eq!(outcome.promoted.len(), 1);
4158 assert_eq!(outcome.promoted[0].id(), &id);
4159 assert_eq!(pool.all_transactions.pending_fees.base_fee, prev_base_fee);
4160 assert_eq!(pool.all_transactions.pending_fees.blob_fee, tx.max_fee_per_blob_gas().unwrap());
4161
4162 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
4163 assert_eq!(tx_meta.subpool, SubPool::Pending);
4164 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4165 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4166 }
4167
4168 #[test]
4169 fn apply_fee_updates_promotes_blob_after_basefee_drop() {
4170 let mut f = MockTransactionFactory::default();
4171 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4172
4173 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4174
4175 let tx = MockTransaction::eip4844()
4176 .with_max_fee(500)
4177 .with_priority_fee(1)
4178 .with_blob_fee(initial_blob_fee + 100);
4179 let validated = f.validated(tx);
4180 let id = *validated.id();
4181 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4182
4183 assert_eq!(pool.pending_pool.len(), 1);
4184
4185 let high_base_fee = 600;
4187 pool.update_basefee(high_base_fee, |_| {});
4188 assert!(pool.pending_pool.is_empty());
4189 assert_eq!(pool.blob_pool.len(), 1);
4190
4191 let prev_base_fee = high_base_fee;
4192 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4193
4194 pool.all_transactions.pending_fees.base_fee = 400;
4196
4197 let mut outcome = UpdateOutcome::default();
4198 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
4199
4200 assert_eq!(pool.pending_pool.len(), 1);
4201 assert!(pool.blob_pool.is_empty());
4202 assert_eq!(outcome.promoted.len(), 1);
4203 assert_eq!(outcome.promoted[0].id(), &id);
4204 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
4205 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
4206
4207 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
4208 assert_eq!(tx_meta.subpool, SubPool::Pending);
4209 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4210 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4211 }
4212
4213 #[test]
4214 fn queued_transactions_include_blob_pool() {
4215 let mut f = MockTransactionFactory::default();
4216 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4217
4218 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4219 let tx = MockTransaction::eip4844()
4220 .with_max_fee(500)
4221 .with_priority_fee(1)
4222 .with_blob_fee(initial_blob_fee + 100);
4223 let validated = f.validated(tx);
4224 let id = *validated.id();
4225 let sender = validated.sender_id();
4226 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4227
4228 pool.update_basefee(600, |_| {});
4230 assert_eq!(pool.blob_pool.len(), 1);
4231
4232 let queued = pool.queued_transactions();
4233 assert_eq!(queued.len(), 1);
4234 assert_eq!(queued[0].id(), &id);
4235
4236 let by_sender = pool.queued_txs_by_sender(sender);
4237 assert_eq!(by_sender.len(), 1);
4238 assert_eq!(by_sender[0].id(), &id);
4239 }
4240
4241 #[test]
4242 fn apply_fee_updates_demotes_after_basefee_rise() {
4243 let mut f = MockTransactionFactory::default();
4244 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4245
4246 let tx = MockTransaction::eip1559()
4247 .with_gas_limit(21_000)
4248 .with_max_fee(400)
4249 .with_priority_fee(1);
4250 let validated = f.validated(tx);
4251 let id = *validated.id();
4252 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
4253
4254 assert_eq!(pool.pending_pool.len(), 1);
4255
4256 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
4257 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
4258
4259 let new_base_fee = prev_base_fee + 1_000;
4261 pool.all_transactions.pending_fees.base_fee = new_base_fee;
4262
4263 let mut outcome = UpdateOutcome::default();
4264 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
4265
4266 assert!(pool.pending_pool.is_empty());
4267 assert_eq!(pool.basefee_pool.len(), 1);
4268 assert!(outcome.promoted.is_empty());
4269 assert_eq!(pool.all_transactions.pending_fees.base_fee, new_base_fee);
4270 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
4271
4272 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
4273 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
4274 assert!(!tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
4275 }
4276
4277 #[test]
4278 fn get_highest_transaction_by_sender_and_nonce() {
4279 let mut f = MockTransactionFactory::default();
4281 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4282
4283 let tx = MockTransaction::eip1559();
4285 pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0, None).unwrap();
4286
4287 let tx1 = tx.inc_price().next();
4289
4290 let tx1_validated = f.validated(tx1.clone());
4292 pool.add_transaction(tx1_validated, U256::from(1_000), 0, None).unwrap();
4293
4294 assert_eq!(
4296 pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
4297 Some(1)
4298 );
4299
4300 let highest_tx = pool
4302 .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
4303 .expect("Failed to retrieve highest transaction");
4304
4305 assert_eq!(highest_tx.as_ref().transaction, tx1);
4307 }
4308
4309 #[test]
4310 fn get_highest_consecutive_transaction_by_sender() {
4311 let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
4313 let mut f = MockTransactionFactory::default();
4314
4315 let sender = Address::random();
4317 let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
4318 for nonce in txs {
4319 let mut mock_tx = MockTransaction::eip1559();
4320 mock_tx.set_sender(sender);
4321 mock_tx.set_nonce(nonce);
4322
4323 let validated_tx = f.validated(mock_tx);
4324 pool.add_transaction(validated_tx, U256::from(1000), 0, None).unwrap();
4325 }
4326
4327 let sender_id = f.ids.sender_id(&sender).unwrap();
4329 let next_tx =
4330 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
4331 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
4332
4333 let next_tx =
4334 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
4335 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
4336
4337 let next_tx =
4338 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
4339 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
4340
4341 let mut info = SenderInfo::default();
4343 info.update(8, U256::ZERO);
4344 pool.all_transactions.sender_info.insert(sender_id, info);
4345 let next_tx =
4346 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
4347 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
4348 }
4349
4350 #[test]
4351 fn discard_nonce_too_low() {
4352 let mut f = MockTransactionFactory::default();
4353 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4354
4355 let tx = MockTransaction::eip1559().inc_price_by(10);
4356 let validated = f.validated(tx.clone());
4357 let id = *validated.id();
4358 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4359
4360 let next = tx.next();
4361 let validated = f.validated(next.clone());
4362 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4363
4364 assert_eq!(pool.pending_pool.len(), 2);
4365
4366 let mut changed_senders = HashMap::default();
4367 changed_senders.insert(
4368 id.sender,
4369 SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
4370 );
4371 let outcome = pool.update_accounts(changed_senders);
4372 assert_eq!(outcome.discarded.len(), 1);
4373 assert_eq!(pool.pending_pool.len(), 1);
4374 }
4375
4376 fn stale_validation_pool() -> TxPool<MockOrdering> {
4378 TxPool::new(
4379 MockOrdering::default(),
4380 PoolConfig { enforce_tracked_nonce: true, ..Default::default() },
4381 )
4382 }
4383
4384 #[test]
4385 fn stale_validation_accepted_by_default() {
4386 let mut f = MockTransactionFactory::default();
4387 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4388
4389 let template = MockTransaction::eip1559();
4390 let first = f.validated(template.clone().with_nonce(0).rng_hash());
4391 let sender = first.sender_id();
4392 pool.add_transaction(first, U256::from(1_000), 0, None).unwrap();
4393
4394 let changed = FxHashMap::from_iter([(
4395 sender,
4396 SenderInfo { state_nonce: 1, balance: U256::from(1_000) },
4397 )]);
4398 let outcome = pool.update_accounts(changed);
4399 assert_eq!(outcome.discarded.len(), 1);
4400 assert!(!pool.all_transactions.sender_info.contains_key(&sender));
4402
4403 let stale = f.validated(template.with_nonce(0).rng_hash());
4405 pool.add_transaction(stale, U256::from(1_000), 0, None).unwrap();
4406 assert_eq!(pool.pending_pool.len(), 1);
4407 assert_eq!(pool.all_transactions.sender_info[&sender].state_nonce, 0);
4408 }
4409
4410 #[test]
4411 fn stale_validation_does_not_regress_sender_state() {
4412 let mut f = MockTransactionFactory::default();
4413 let mut pool = stale_validation_pool();
4414
4415 let template = MockTransaction::eip1559();
4416 let first = f.validated(template.clone().with_nonce(0).rng_hash());
4417 let sender = first.sender_id();
4418 pool.add_transaction(first, U256::from(1_000), 0, None).unwrap();
4419
4420 let mut changed_senders = HashMap::default();
4421 changed_senders.insert(sender, SenderInfo { state_nonce: 1, balance: U256::from(1_000) });
4422 let outcome = pool.update_accounts(changed_senders);
4423 assert_eq!(outcome.discarded.len(), 1);
4424 assert_eq!(pool.all_transactions.sender_info[&sender].state_nonce, 1);
4425
4426 let current = f.validated(template.clone().with_nonce(1).rng_hash());
4428 pool.add_transaction(current, U256::from(1_000), 0, None).unwrap();
4429 assert_eq!(pool.pending_pool.len(), 1);
4430 assert_eq!(pool.queued_pool.len(), 0);
4431 assert_eq!(pool.all_transactions.sender_info[&sender].state_nonce, 1);
4432
4433 let stale = f.validated(template.with_nonce(0).rng_hash());
4434 let stale_hash = *stale.hash();
4435 let err = pool.add_transaction(stale, U256::from(1_000), 0, None).unwrap_err();
4436 assert!(matches!(
4437 err.kind,
4438 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Consensus(
4439 InvalidTransactionError::NonceNotConsistent { tx: 0, state: 1 }
4440 ))
4441 ));
4442 assert!(!pool.contains(&stale_hash));
4443 assert_eq!(pool.all_transactions.sender_info[&sender].state_nonce, 1);
4444 }
4445
4446 #[test]
4447 fn stale_validation_resubmission_after_commit() {
4448 let mut f = MockTransactionFactory::default();
4449 let mut pool = stale_validation_pool();
4450 let block_info = pool.block_info();
4451
4452 let template = MockTransaction::eip1559();
4453 let tx0 = template.clone().with_nonce(0).rng_hash();
4454 let tx1 = template.clone().with_nonce(1).rng_hash();
4455 let first = f.validated(tx0.clone());
4456 let sender = first.sender_id();
4457 pool.add_transaction(first, U256::from(1_000), 0, None).unwrap();
4458 pool.add_transaction(f.validated(tx1.clone()), U256::from(1_000), 0, None).unwrap();
4459 assert_eq!(pool.pending_pool.len(), 2);
4460
4461 let changed = FxHashMap::from_iter([(
4463 sender,
4464 SenderInfo { state_nonce: 1, balance: U256::from(1_000) },
4465 )]);
4466 pool.on_canonical_state_change(
4467 block_info,
4468 vec![*tx0.get_hash()],
4469 changed,
4470 PoolUpdateKind::Commit,
4471 );
4472 assert_eq!(pool.pending_pool.len(), 1);
4473
4474 let retry = f.validated(template.with_nonce(0).rng_hash());
4476 let err = pool.add_transaction(retry, U256::from(1_000), 0, None).unwrap_err();
4477 assert!(matches!(
4478 err.kind,
4479 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Consensus(
4480 InvalidTransactionError::NonceNotConsistent { tx: 0, state: 1 }
4481 ))
4482 ));
4483
4484 let replacement = f.validated(tx1.inc_price_by(10).rng_hash());
4486 let added = pool.add_transaction(replacement, U256::from(1_000), 0, None).unwrap();
4487 assert!(added.as_pending().is_some());
4488 assert!(added.replaced().is_some());
4489 assert_eq!(pool.pending_pool.len(), 1);
4490 assert_eq!(pool.all_transactions.sender_info[&sender].state_nonce, 1);
4491 let next =
4492 pool.get_highest_consecutive_transaction_by_sender(sender.into_transaction_id(1));
4493 assert_eq!(next.map(|tx| tx.nonce()), Some(1));
4494 }
4495
4496 #[test]
4497 fn stale_validation_uses_tracked_balance() {
4498 let mut f = MockTransactionFactory::default();
4499 let mut pool = stale_validation_pool();
4500
4501 let template = MockTransaction::eip1559().with_gas_price(50).with_gas_limit(30_000);
4503 let first = f.validated(template.clone().with_nonce(0).rng_hash());
4504 let sender = first.sender_id();
4505 pool.add_transaction(first, U256::from(10_000_000), 0, None).unwrap();
4506 assert_eq!(pool.pending_pool.len(), 1);
4507
4508 let changed = FxHashMap::from_iter([(
4510 sender,
4511 SenderInfo { state_nonce: 1, balance: U256::from(1_000) },
4512 )]);
4513 pool.update_accounts(changed);
4514
4515 let next = f.validated(template.with_nonce(1).rng_hash());
4517 let id = *next.id();
4518 pool.add_transaction(next, U256::from(10_000_000), 0, None).unwrap();
4519 assert_eq!(pool.pending_pool.len(), 0);
4520 assert_eq!(pool.queued_pool.len(), 1);
4521 let state = pool.all_transactions.get(&id).unwrap().state;
4522 assert!(state.contains(TxState::NO_NONCE_GAPS));
4523 assert!(!state.contains(TxState::ENOUGH_BALANCE));
4524 assert_eq!(pool.all_transactions.sender_info[&sender].balance, U256::from(1_000));
4525 }
4526
4527 #[test]
4528 fn discard_with_large_blob_txs() {
4529 reth_tracing::init_test_tracing();
4531
4532 let mut f = MockTransactionFactory::default();
4534 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4535 let default_limits = pool.config.blob_limit;
4536
4537 let a_sender = address!("0x000000000000000000000000000000000000000a");
4540
4541 let mut block_info = pool.block_info();
4543 block_info.pending_blob_fee = Some(100);
4544 block_info.pending_basefee = 100;
4545
4546 pool.set_block_info(block_info);
4548
4549 let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
4551 .into_iter()
4552 .map(|mut tx| {
4553 tx.set_size(default_limits.max_size / 2 + 1);
4554 tx.set_max_fee((block_info.pending_basefee - 1).into());
4555 tx
4556 })
4557 .collect::<Vec<_>>();
4558
4559 for tx in a_txs {
4561 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
4562 }
4563
4564 let removed = pool.discard_worst();
4566 assert_eq!(removed.len(), 1);
4567 }
4568
4569 #[test]
4570 fn discard_with_parked_large_txs() {
4571 reth_tracing::init_test_tracing();
4573
4574 let mut f = MockTransactionFactory::default();
4576 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4577 let default_limits = pool.config.queued_limit;
4578
4579 let a_sender = address!("0x000000000000000000000000000000000000000a");
4582
4583 let pool_base_fee = 100;
4585 pool.update_basefee(pool_base_fee, |_| {});
4586
4587 let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
4589 .into_iter()
4590 .map(|mut tx| {
4591 tx.set_size(default_limits.max_size / 2 + 1);
4592 tx.set_max_fee((pool_base_fee - 1).into());
4593 tx
4594 })
4595 .collect::<Vec<_>>();
4596
4597 for tx in a_txs {
4599 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
4600 }
4601
4602 let removed = pool.discard_worst();
4604 assert_eq!(removed.len(), 1);
4605 }
4606
4607 #[test]
4608 fn discard_at_capacity() {
4609 let mut f = MockTransactionFactory::default();
4610 let queued_limit = SubPoolLimit::new(1000, usize::MAX);
4611 let mut pool =
4612 TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
4613
4614 for _ in 0..queued_limit.max_txs {
4616 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
4617 let validated = f.validated(tx);
4618 let _id = *validated.id();
4619 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4620 }
4621
4622 let size = pool.size();
4623 assert_eq!(size.queued, queued_limit.max_txs);
4624
4625 for _ in 0..queued_limit.max_txs {
4626 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
4627 let validated = f.validated(tx);
4628 let _id = *validated.id();
4629 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4630
4631 pool.discard_worst();
4632 pool.assert_invariants();
4633 assert!(pool.size().queued <= queued_limit.max_txs);
4634 }
4635 }
4636
4637 #[test]
4638 fn discard_blobs_at_capacity() {
4639 let mut f = MockTransactionFactory::default();
4640 let blob_limit = SubPoolLimit::new(1000, usize::MAX);
4641 let mut pool =
4642 TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
4643 pool.all_transactions.pending_fees.blob_fee = 10000;
4644 for _ in 0..blob_limit.max_txs {
4646 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
4647 let validated = f.validated(tx);
4648 let _id = *validated.id();
4649 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4650 }
4651
4652 let size = pool.size();
4653 assert_eq!(size.blob, blob_limit.max_txs);
4654
4655 for _ in 0..blob_limit.max_txs {
4656 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
4657 let validated = f.validated(tx);
4658 let _id = *validated.id();
4659 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
4660
4661 pool.discard_worst();
4662 pool.assert_invariants();
4663 assert!(pool.size().blob <= blob_limit.max_txs);
4664 }
4665 }
4666
4667 #[test]
4668 fn reuse_update_buffer_across_pool_operations() {
4669 let mut f = MockTransactionFactory::default();
4670 let mut pool = TxPool::new(
4671 MockOrdering::default(),
4672 PoolConfig { max_account_slots: 128, ..Default::default() },
4673 );
4674 let mut tx = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4675 let first = f.validated(tx.clone());
4676 for _ in 0..80 {
4677 pool.add_transaction(f.validated(tx.clone()), U256::MAX, 0, None).unwrap();
4678 tx = tx.next();
4679 }
4680 assert_eq!(pool.all_transactions.update_buffer.capacity(), 0);
4681
4682 let changed = |balance, state_nonce| {
4683 FxHashMap::from_iter([(first.sender_id(), SenderInfo { balance, state_nonce })])
4684 };
4685 let outcome = pool.update_accounts(changed(U256::ZERO, 0));
4686 assert!(outcome.promoted.is_empty());
4687 assert!(outcome.discarded.is_empty());
4688 assert_eq!(pool.queued_pool.len(), 80);
4689 let capacity = pool.all_transactions.update_buffer.capacity();
4690 assert!(capacity >= 80);
4691 let buffer = pool.all_transactions.update_buffer.as_ptr();
4692
4693 for _ in 0..2 {
4694 let outcome = pool.update_accounts(changed(U256::MAX, 0));
4695 assert_eq!(outcome.promoted.len(), 80);
4696 assert!(outcome.discarded.is_empty());
4697 assert_eq!(pool.pending_pool.len(), 80);
4698
4699 let outcome = pool.update_accounts(FxHashMap::default());
4700 assert!(outcome.promoted.is_empty());
4701 assert!(outcome.discarded.is_empty());
4702
4703 pool.remove_transaction_by_hash(first.hash()).unwrap();
4704 assert_eq!(pool.queued_pool.len(), 79);
4705
4706 let replacement = f.validated(first.transaction.next());
4708 assert!(pool.add_transaction(replacement, U256::MAX, 0, None).is_err());
4709 assert_eq!(pool.all_transactions.update_buffer.as_ptr(), buffer);
4710
4711 let added = pool.add_transaction(first.clone(), U256::MAX, 0, None).unwrap();
4712 let AddedTransaction::Pending(added) = added else { panic!("expected pending") };
4713 assert_eq!(added.promoted.len(), 79);
4714 assert!(added.discarded.is_empty());
4715 assert_eq!(pool.pending_pool.len(), 80);
4716
4717 pool.update_accounts(changed(U256::ZERO, 0));
4718 assert_eq!(pool.queued_pool.len(), 80);
4719 assert!(pool.all_transactions.update_buffer.is_empty());
4720 assert_eq!(pool.all_transactions.update_buffer.capacity(), capacity);
4721 assert_eq!(pool.all_transactions.update_buffer.as_ptr(), buffer);
4722 pool.assert_invariants();
4723 }
4724
4725 let outcome = pool.update_accounts(changed(U256::MAX, 80));
4726 assert_eq!(outcome.discarded.len(), 80);
4727 assert!(outcome.promoted.is_empty());
4728 assert!(pool.is_empty());
4729 assert!(pool.all_transactions.update_buffer.is_empty());
4730 assert_eq!(pool.all_transactions.update_buffer.as_ptr(), buffer);
4731 }
4732
4733 #[test]
4734 fn account_updates_sender_balance() {
4735 let mut on_chain_balance = U256::from(100);
4736 let on_chain_nonce = 0;
4737 let mut f = MockTransactionFactory::default();
4738 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4739
4740 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4741 let tx_1 = tx_0.next();
4742 let tx_2 = tx_1.next();
4743
4744 let v0 = f.validated(tx_0);
4746 let v1 = f.validated(tx_1);
4747 let v2 = f.validated(tx_2);
4748
4749 let _res =
4750 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4751 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4752 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4753
4754 assert_eq!(1, pool.pending_transactions().len());
4756 assert_eq!(2, pool.queued_transactions().len());
4757
4758 let mut updated_accounts = HashMap::default();
4760 on_chain_balance = U256::from(300);
4761 updated_accounts.insert(
4762 v0.sender_id(),
4763 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4764 );
4765 pool.update_accounts(updated_accounts.clone());
4766
4767 assert_eq!(3, pool.pending_transactions().len());
4768 assert!(pool.queued_transactions().is_empty());
4769
4770 updated_accounts.entry(v0.sender_id()).and_modify(|v| v.balance = U256::from(1));
4772 pool.update_accounts(updated_accounts);
4773
4774 assert!(pool.pending_transactions().is_empty());
4775 assert_eq!(3, pool.queued_transactions().len());
4776 }
4777
4778 #[test]
4779 fn account_updates_nonce_gap() {
4780 let on_chain_balance = U256::from(10_000);
4781 let mut on_chain_nonce = 0;
4782 let mut f = MockTransactionFactory::default();
4783 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4784
4785 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4786 let tx_1 = tx_0.next();
4787 let tx_2 = tx_1.next();
4788
4789 let v0 = f.validated(tx_0);
4791 let v1 = f.validated(tx_1);
4792 let v2 = f.validated(tx_2);
4793
4794 let _res =
4796 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4797 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4798
4799 assert!(pool.queued_transactions().is_empty());
4800 assert_eq!(2, pool.pending_transactions().len());
4801
4802 pool.remove_transaction_by_hash(v0.hash());
4804
4805 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4807
4808 assert_eq!(2, pool.queued_transactions().len());
4810 assert!(pool.pending_transactions().is_empty());
4811
4812 let mut updated_accounts = HashMap::default();
4814 on_chain_nonce += 1;
4815 updated_accounts.insert(
4816 v0.sender_id(),
4817 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4818 );
4819 pool.update_accounts(updated_accounts);
4820
4821 assert!(pool.queued_transactions().is_empty());
4823 assert_eq!(2, pool.pending_transactions().len());
4824 }
4825 #[test]
4826 fn test_transaction_removal() {
4827 let on_chain_balance = U256::from(10_000);
4828 let on_chain_nonce = 0;
4829 let mut f = MockTransactionFactory::default();
4830 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4831
4832 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4833 let tx_1 = tx_0.next();
4834
4835 let v0 = f.validated(tx_0);
4837 let v1 = f.validated(tx_1);
4838
4839 let _res =
4841 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4842 let _res =
4843 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4844
4845 assert_eq!(0, pool.queued_transactions().len());
4846 assert_eq!(2, pool.pending_transactions().len());
4847
4848 pool.remove_transaction(v0.id());
4850 let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
4852 assert_eq!(vec![v1.nonce()], pool_txs);
4853 }
4854 #[test]
4855 fn test_remove_transactions() {
4856 let on_chain_balance = U256::from(10_000);
4857 let on_chain_nonce = 0;
4858 let mut f = MockTransactionFactory::default();
4859 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4860
4861 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4862 let tx_1 = tx_0.next();
4863 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4864 let tx_3 = tx_2.next();
4865
4866 let v0 = f.validated(tx_0);
4868 let v1 = f.validated(tx_1);
4869 let v2 = f.validated(tx_2);
4870 let v3 = f.validated(tx_3);
4871
4872 let _res =
4874 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4875 let _res =
4876 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4877 let _res =
4878 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4879 let _res =
4880 pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4881
4882 assert_eq!(0, pool.queued_transactions().len());
4883 assert_eq!(4, pool.pending_transactions().len());
4884
4885 pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
4886
4887 assert_eq!(2, pool.queued_transactions().len());
4888 assert!(pool.pending_transactions().is_empty());
4889 assert!(pool.contains(v1.hash()));
4890 assert!(pool.contains(v3.hash()));
4891 }
4892
4893 #[test]
4894 fn test_remove_transactions_middle_pending_hash() {
4895 let on_chain_balance = U256::from(10_000);
4896 let on_chain_nonce = 0;
4897 let mut f = MockTransactionFactory::default();
4898 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4899
4900 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4901 let tx_1 = tx_0.next();
4902 let tx_2 = tx_1.next();
4903 let tx_3 = tx_2.next();
4904
4905 let v0 = f.validated(tx_0);
4907 let v1 = f.validated(tx_1);
4908 let v2 = f.validated(tx_2);
4909 let v3 = f.validated(tx_3);
4910
4911 let _res = pool.add_transaction(v0, on_chain_balance, on_chain_nonce, None).unwrap();
4913 let _res =
4914 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4915 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4916 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4917
4918 assert_eq!(0, pool.queued_transactions().len());
4919 assert_eq!(4, pool.pending_transactions().len());
4920
4921 let mut removed_txs = pool.remove_transactions(vec![*v1.hash()]);
4922 assert_eq!(1, removed_txs.len());
4923
4924 assert_eq!(2, pool.queued_transactions().len());
4925 assert_eq!(1, pool.pending_transactions().len());
4926
4927 let removed_tx = removed_txs.pop().unwrap();
4929 let v1 = f.validated(removed_tx.transaction.clone());
4930 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4931 assert_eq!(0, pool.queued_transactions().len());
4932 assert_eq!(4, pool.pending_transactions().len());
4933 }
4934
4935 #[test]
4936 fn test_remove_transactions_and_descendants() {
4937 let on_chain_balance = U256::from(10_000);
4938 let on_chain_nonce = 0;
4939 let mut f = MockTransactionFactory::default();
4940 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4941
4942 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4943 let tx_1 = tx_0.next();
4944 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4945 let tx_3 = tx_2.next();
4946 let tx_4 = tx_3.next();
4947
4948 let v0 = f.validated(tx_0);
4950 let v1 = f.validated(tx_1);
4951 let v2 = f.validated(tx_2);
4952 let v3 = f.validated(tx_3);
4953 let v4 = f.validated(tx_4);
4954
4955 let _res =
4957 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4958 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4959 let _res =
4960 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4961 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4962 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4963
4964 assert_eq!(0, pool.queued_transactions().len());
4965 assert_eq!(5, pool.pending_transactions().len());
4966
4967 pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
4968
4969 assert_eq!(0, pool.queued_transactions().len());
4970 assert_eq!(0, pool.pending_transactions().len());
4971 }
4972 #[test]
4973 fn test_remove_descendants() {
4974 let on_chain_balance = U256::from(10_000);
4975 let on_chain_nonce = 0;
4976 let mut f = MockTransactionFactory::default();
4977 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4978
4979 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4980 let tx_1 = tx_0.next();
4981 let tx_2 = tx_1.next();
4982 let tx_3 = tx_2.next();
4983
4984 let v0 = f.validated(tx_0);
4986 let v1 = f.validated(tx_1);
4987 let v2 = f.validated(tx_2);
4988 let v3 = f.validated(tx_3);
4989
4990 let _res =
4992 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4993 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4994 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4995 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4996
4997 assert_eq!(0, pool.queued_transactions().len());
4998 assert_eq!(4, pool.pending_transactions().len());
4999
5000 let mut removed = Vec::new();
5001 pool.remove_transaction(v0.id());
5002 pool.remove_descendants(v0.id(), &mut removed);
5003
5004 assert_eq!(0, pool.queued_transactions().len());
5005 assert_eq!(0, pool.pending_transactions().len());
5006 assert_eq!(3, removed.len());
5007 }
5008 #[test]
5009 fn test_remove_transactions_by_sender() {
5010 let on_chain_balance = U256::from(10_000);
5011 let on_chain_nonce = 0;
5012 let mut f = MockTransactionFactory::default();
5013 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5014
5015 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
5016 let tx_1 = tx_0.next();
5017 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
5018 let tx_3 = tx_2.next();
5019 let tx_4 = tx_3.next();
5020
5021 let v0 = f.validated(tx_0);
5023 let v1 = f.validated(tx_1);
5024 let v2 = f.validated(tx_2);
5025 let v3 = f.validated(tx_3);
5026 let v4 = f.validated(tx_4);
5027
5028 let _res =
5030 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
5031 let _res =
5032 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
5033 let _res =
5034 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
5035 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
5036 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
5037
5038 assert_eq!(0, pool.queued_transactions().len());
5039 assert_eq!(5, pool.pending_transactions().len());
5040
5041 pool.remove_transactions_by_sender(v2.sender_id());
5042
5043 assert_eq!(0, pool.queued_transactions().len());
5044 assert_eq!(2, pool.pending_transactions().len());
5045 assert!(pool.contains(v0.hash()));
5046 assert!(pool.contains(v1.hash()));
5047 }
5048 #[test]
5049 fn wrong_best_order_of_transactions() {
5050 let on_chain_balance = U256::from(10_000);
5051 let mut on_chain_nonce = 0;
5052 let mut f = MockTransactionFactory::default();
5053 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5054
5055 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
5056 let tx_1 = tx_0.next();
5057 let tx_2 = tx_1.next();
5058 let tx_3 = tx_2.next();
5059
5060 let v0 = f.validated(tx_0);
5062 let v1 = f.validated(tx_1);
5063 let v2 = f.validated(tx_2);
5064 let v3 = f.validated(tx_3);
5065
5066 let _res =
5068 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
5069 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
5070
5071 assert_eq!(0, pool.queued_transactions().len());
5072 assert_eq!(2, pool.pending_transactions().len());
5073
5074 pool.remove_transaction(v0.id());
5076
5077 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
5079
5080 assert_eq!(1, pool.queued_transactions().len());
5082 assert_eq!(1, pool.pending_transactions().len());
5083
5084 let mut updated_accounts = HashMap::default();
5086 on_chain_nonce += 1;
5087 updated_accounts.insert(
5088 v0.sender_id(),
5089 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
5090 );
5091 pool.update_accounts(updated_accounts);
5092
5093 assert_eq!(0, pool.queued_transactions().len());
5096 assert_eq!(2, pool.pending_transactions().len());
5097
5098 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
5100 assert_eq!(0, pool.queued_transactions().len());
5101 assert_eq!(3, pool.pending_transactions().len());
5102
5103 assert_eq!(
5106 pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
5107 vec![1, 2, 3]
5108 );
5109 }
5110
5111 #[test]
5112 fn test_best_with_attributes() {
5113 let on_chain_balance = U256::MAX;
5114 let on_chain_nonce = 0;
5115 let mut f = MockTransactionFactory::default();
5116 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5117
5118 let base_fee: u128 = 100;
5119 let blob_fee: u128 = 100;
5120
5121 let mut block_info = pool.block_info();
5123 block_info.pending_basefee = base_fee as u64;
5124 block_info.pending_blob_fee = Some(blob_fee);
5125 pool.set_block_info(block_info);
5126
5127 let tx1 = MockTransaction::eip4844()
5129 .with_sender(Address::with_last_byte(1))
5130 .with_max_fee(base_fee + 10)
5131 .with_blob_fee(blob_fee + 10);
5132 let tx2 = MockTransaction::eip4844()
5133 .with_sender(Address::with_last_byte(2))
5134 .with_max_fee(base_fee + 10)
5135 .with_blob_fee(blob_fee);
5136 let tx3 = MockTransaction::eip4844()
5137 .with_sender(Address::with_last_byte(3))
5138 .with_max_fee(base_fee)
5139 .with_blob_fee(blob_fee + 10);
5140 let tx4 = MockTransaction::eip4844()
5141 .with_sender(Address::with_last_byte(4))
5142 .with_max_fee(base_fee)
5143 .with_blob_fee(blob_fee);
5144 let tx5 = MockTransaction::eip4844()
5145 .with_sender(Address::with_last_byte(5))
5146 .with_max_fee(base_fee)
5147 .with_blob_fee(blob_fee - 10);
5148 let tx6 = MockTransaction::eip4844()
5149 .with_sender(Address::with_last_byte(6))
5150 .with_max_fee(base_fee - 10)
5151 .with_blob_fee(blob_fee);
5152 let tx7 = MockTransaction::eip4844()
5153 .with_sender(Address::with_last_byte(7))
5154 .with_max_fee(base_fee - 10)
5155 .with_blob_fee(blob_fee - 10);
5156
5157 for tx in vec![
5158 tx1.clone(),
5159 tx2.clone(),
5160 tx3.clone(),
5161 tx4.clone(),
5162 tx5.clone(),
5163 tx6.clone(),
5164 tx7.clone(),
5165 ] {
5166 pool.add_transaction(f.validated(tx), on_chain_balance, on_chain_nonce, None).unwrap();
5167 }
5168
5169 let base_fee = base_fee as u64;
5170 let blob_fee = blob_fee as u64;
5171
5172 let cases = vec![
5173 (BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee + 5)), vec![tx1.clone()]),
5175 (
5177 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee)),
5178 vec![tx1.clone(), tx2.clone()],
5179 ),
5180 (
5182 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee - 5)),
5183 vec![tx1.clone(), tx2.clone()],
5184 ),
5185 (
5187 BestTransactionsAttributes::new(base_fee, Some(blob_fee + 5)),
5188 vec![tx1.clone(), tx3.clone()],
5189 ),
5190 (
5192 BestTransactionsAttributes::new(base_fee, Some(blob_fee)),
5193 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
5194 ),
5195 (
5197 BestTransactionsAttributes::new(base_fee, Some(blob_fee - 10)),
5198 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx5.clone()],
5199 ),
5200 (
5202 BestTransactionsAttributes::new(base_fee - 5, Some(blob_fee + 5)),
5203 vec![tx1.clone(), tx3.clone()],
5204 ),
5205 (
5207 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee)),
5208 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx6.clone()],
5209 ),
5210 (
5212 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee - 10)),
5213 vec![tx1, tx2, tx5, tx3, tx4, tx6, tx7],
5214 ),
5215 ];
5216
5217 for (idx, (attribute, expected)) in cases.into_iter().enumerate() {
5218 let mut best = pool.best_transactions_with_attributes(attribute);
5219
5220 for (tx_idx, expected_tx) in expected.into_iter().enumerate() {
5221 let tx = best.next().expect("Transaction should be returned");
5222 assert_eq!(
5223 tx.transaction,
5224 expected_tx,
5225 "Failed tx {} in case {}",
5226 tx_idx + 1,
5227 idx + 1
5228 );
5229 }
5230
5231 assert!(best.next().is_none());
5233 }
5234 }
5235
5236 #[test]
5237 fn test_pending_ordering() {
5238 let mut f = MockTransactionFactory::default();
5239 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5240
5241 let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
5242 let tx_1 = tx_0.next();
5243
5244 let v0 = f.validated(tx_0);
5245 let v1 = f.validated(tx_1);
5246
5247 pool.add_transaction(v0.clone(), U256::MAX, 0, None).unwrap();
5249 assert_eq!(1, pool.queued_transactions().len());
5250
5251 pool.add_transaction(v1, U256::MAX, 1, None).unwrap();
5253
5254 assert_eq!(2, pool.pending_transactions().len());
5255 assert_eq!(0, pool.queued_transactions().len());
5256
5257 assert_eq!(
5258 pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
5259 v0.nonce()
5260 );
5261 }
5262
5263 #[test]
5265 fn one_sender_one_independent_transaction() {
5266 let mut on_chain_balance = U256::from(4_999); let mut on_chain_nonce = 40;
5268 let mut f = MockTransactionFactory::default();
5269 let mut pool = TxPool::mock();
5270 let mut submitted_txs = Vec::new();
5271
5272 let template =
5274 MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
5275
5276 for tx_nonce in 40..48 {
5279 let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
5280 submitted_txs.push(*tx.id());
5281 pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap();
5282 }
5283
5284 on_chain_balance = U256::from(999_999);
5287 on_chain_nonce = 42;
5288 pool.remove_transaction(&submitted_txs[0]);
5289 pool.remove_transaction(&submitted_txs[1]);
5290
5291 for tx_nonce in 48..52 {
5293 pool.add_transaction(
5294 f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
5295 on_chain_balance,
5296 on_chain_nonce,
5297 None,
5298 )
5299 .unwrap();
5300 }
5301
5302 let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
5303 assert_eq!(best_txs.len(), 10); assert_eq!(pool.pending_pool.independent().len(), 1);
5306 }
5307
5308 #[test]
5309 fn test_insertion_disorder() {
5310 let mut f = MockTransactionFactory::default();
5311 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5312
5313 let sender = address!("0x1234567890123456789012345678901234567890");
5314 let tx0 = f.validated_arc(
5315 MockTransaction::legacy().with_sender(sender).with_nonce(0).with_gas_price(10),
5316 );
5317 let tx1 = f.validated_arc(
5318 MockTransaction::eip1559()
5319 .with_sender(sender)
5320 .with_nonce(1)
5321 .with_gas_limit(1000)
5322 .with_gas_price(10),
5323 );
5324 let tx2 = f.validated_arc(
5325 MockTransaction::legacy().with_sender(sender).with_nonce(2).with_gas_price(10),
5326 );
5327 let tx3 = f.validated_arc(
5328 MockTransaction::legacy().with_sender(sender).with_nonce(3).with_gas_price(10),
5329 );
5330
5331 pool.add_transaction((*tx0).clone(), U256::from(1000), 0, None).unwrap();
5333 let mut best = pool.best_transactions();
5334 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5335 assert_eq!(t0.id(), tx0.id());
5336 pool.add_transaction((*tx1).clone(), U256::from(1000), 0, None).unwrap();
5338 let mut best = pool.best_transactions();
5339 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5340 assert_eq!(t0.id(), tx0.id());
5341 assert!(best.next().is_none());
5342
5343 pool.add_transaction((*tx2).clone(), U256::MAX, 0, None).unwrap();
5345
5346 let mut best = pool.best_transactions();
5347
5348 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5349 let t1 = best.next().expect("tx1 should be put in the pending subpool");
5350 let t2 = best.next().expect("tx2 should be put in the pending subpool");
5351 assert_eq!(t0.id(), tx0.id());
5352 assert_eq!(t1.id(), tx1.id());
5353 assert_eq!(t2.id(), tx2.id());
5354
5355 pool.add_transaction((*tx3).clone(), U256::MAX, 0, None).unwrap();
5357 let mut best = pool.best_transactions();
5358 let t0 = best.next().expect("tx0 should be put in the pending subpool");
5359 let t1 = best.next().expect("tx1 should be put in the pending subpool");
5360 let t2 = best.next().expect("tx2 should be put in the pending subpool");
5361 let t3 = best.next().expect("tx3 should be put in the pending subpool");
5362 assert_eq!(t0.id(), tx0.id());
5363 assert_eq!(t1.id(), tx1.id());
5364 assert_eq!(t2.id(), tx2.id());
5365 assert_eq!(t3.id(), tx3.id());
5366 }
5367
5368 #[test]
5369 fn test_non_4844_blob_fee_bit_invariant() {
5370 let mut f = MockTransactionFactory::default();
5371 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5372
5373 let non_4844_tx = MockTransaction::eip1559().set_max_fee(200).inc_limit();
5374 let validated = f.validated(non_4844_tx.clone());
5375
5376 assert!(!non_4844_tx.is_eip4844());
5377 pool.add_transaction(validated.clone(), U256::from(10_000), 0, None).unwrap();
5378
5379 let tx_meta = pool.all_transactions.txs.get(validated.id()).unwrap();
5381 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
5382 assert_eq!(tx_meta.subpool, SubPool::Pending);
5383 }
5384
5385 #[test]
5386 fn test_blob_fee_enforcement_only_applies_to_eip4844() {
5387 let mut f = MockTransactionFactory::default();
5388 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5389
5390 let mut block_info = pool.block_info();
5392 block_info.pending_blob_fee = Some(160);
5393 block_info.pending_basefee = 100;
5394 pool.set_block_info(block_info);
5395
5396 let eip4844_tx = MockTransaction::eip4844()
5397 .with_sender(address!("0x000000000000000000000000000000000000000a"))
5398 .with_max_fee(200)
5399 .with_blob_fee(150) .inc_limit();
5401
5402 let non_4844_tx = MockTransaction::eip1559()
5403 .with_sender(address!("0x000000000000000000000000000000000000000b"))
5404 .set_max_fee(200)
5405 .inc_limit();
5406
5407 let validated_4844 = f.validated(eip4844_tx);
5408 let validated_non_4844 = f.validated(non_4844_tx);
5409
5410 pool.add_transaction(validated_4844.clone(), U256::from(10_000), 0, None).unwrap();
5411 pool.add_transaction(validated_non_4844.clone(), U256::from(10_000), 0, None).unwrap();
5412
5413 let tx_4844_meta = pool.all_transactions.txs.get(validated_4844.id()).unwrap();
5414 let tx_non_4844_meta = pool.all_transactions.txs.get(validated_non_4844.id()).unwrap();
5415
5416 assert!(!tx_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
5418 assert_eq!(tx_4844_meta.subpool, SubPool::Blob);
5419
5420 assert!(tx_non_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
5422 assert_eq!(tx_non_4844_meta.subpool, SubPool::Pending);
5423 }
5424
5425 #[test]
5426 fn test_basefee_decrease_preserves_non_4844_blob_fee_bit() {
5427 let mut f = MockTransactionFactory::default();
5428 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5429
5430 let non_4844_tx = MockTransaction::eip1559()
5432 .with_sender(address!("0x000000000000000000000000000000000000000a"))
5433 .set_max_fee(500) .inc_limit();
5435
5436 pool.update_basefee(600, |_| {});
5438
5439 let validated = f.validated(non_4844_tx);
5440 let tx_id = *validated.id();
5441 pool.add_transaction(validated, U256::from(10_000), 0, None).unwrap();
5442
5443 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
5445 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
5446 assert!(
5447 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
5448 "Non-4844 tx in BaseFee pool must retain ENOUGH_BLOB_FEE_CAP_BLOCK bit"
5449 );
5450
5451 pool.update_basefee(400, |_| {});
5454
5455 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
5457 assert_eq!(
5458 tx_meta.subpool,
5459 SubPool::Pending,
5460 "Non-4844 tx should be promoted from BaseFee to Pending after basefee decrease"
5461 );
5462 assert!(
5463 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
5464 "Non-4844 tx must NEVER lose ENOUGH_BLOB_FEE_CAP_BLOCK bit during basefee promotion"
5465 );
5466 assert!(
5467 tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK),
5468 "Non-4844 tx should gain ENOUGH_FEE_CAP_BLOCK bit after basefee decrease"
5469 );
5470 }
5471
5472 #[test]
5478 fn best_transactions_nonce_order_on_balance_unlock() {
5479 let mut f = MockTransactionFactory::default();
5480 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5481
5482 let sender = Address::random();
5483 let on_chain_balance = U256::from(10_000);
5484
5485 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
5487 let tx1 = tx0.next().inc_limit().with_value(U256::from(on_chain_balance));
5489 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
5491
5492 let v0 = f.validated(tx0);
5493 let v1 = f.validated(tx1);
5494 let v2 = f.validated(tx2);
5495
5496 pool.add_transaction(v0, on_chain_balance, 0, None).unwrap();
5498
5499 let mut best = pool.best_transactions();
5501
5502 let first = best.next().expect("should yield tx0");
5504 assert_eq!(first.id().nonce, 0);
5505
5506 pool.add_transaction(v1, on_chain_balance, 0, None).unwrap();
5509
5510 assert!(best.next().is_none(), "tx1 should be queued, not pending");
5512
5513 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
5517
5518 let t1 = best.next().expect("should yield a transaction");
5519 let t2 = best.next().expect("should yield a transaction");
5520
5521 assert_eq!(
5523 t1.id().nonce,
5524 1,
5525 "first yielded tx should be nonce 1, got nonce {}",
5526 t1.id().nonce
5527 );
5528 assert_eq!(
5529 t2.id().nonce,
5530 2,
5531 "second yielded tx should be nonce 2, got nonce {}",
5532 t2.id().nonce
5533 );
5534 }
5535
5536 #[test]
5540 fn best_transactions_nonce_order_on_gap_fill() {
5541 let mut f = MockTransactionFactory::default();
5542 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5543
5544 let sender = Address::random();
5545 let balance = U256::MAX;
5546
5547 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
5549 let tx1 = tx0.next().inc_limit();
5551
5552 let v0 = f.validated(tx0);
5553 let v1 = f.validated(tx1);
5554
5555 pool.add_transaction(v1, balance, 0, None).unwrap();
5557
5558 let mut best = pool.best_transactions();
5560 assert!(best.next().is_none(), "pool should have no pending txs yet");
5561
5562 pool.add_transaction(v0, balance, 0, None).unwrap();
5564
5565 let t0 = best.next().expect("should yield a transaction");
5566 let t1 = best.next().expect("should yield a transaction");
5567
5568 assert_eq!(t0.id().nonce, 0, "first yielded tx should be nonce 0, got {}", t0.id().nonce);
5569 assert_eq!(t1.id().nonce, 1, "second yielded tx should be nonce 1, got {}", t1.id().nonce);
5570 }
5571
5572 #[test]
5576 fn best_transactions_nonce_order_mixed_promotions() {
5577 let mut f = MockTransactionFactory::default();
5578 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
5579
5580 let sender = Address::random();
5581 let low_balance = U256::from(10_000);
5582
5583 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
5585 let tx1 = tx0.next().inc_limit().with_value(U256::from(low_balance));
5587 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
5589 let tx3 = tx2.next().inc_limit().with_value(U256::ZERO);
5591
5592 let v0 = f.validated(tx0);
5593 let v1 = f.validated(tx1);
5594 let v2 = f.validated(tx2);
5595 let v3 = f.validated(tx3);
5596
5597 pool.add_transaction(v0, low_balance, 0, None).unwrap();
5599
5600 pool.add_transaction(v1, low_balance, 0, None).unwrap();
5602
5603 pool.add_transaction(v3, low_balance, 0, None).unwrap();
5605
5606 let mut best = pool.best_transactions();
5607
5608 let first = best.next().expect("should yield tx0");
5610 assert_eq!(first.id().nonce, 0);
5611 assert!(best.next().is_none(), "only tx0 should be pending");
5612
5613 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
5618
5619 let t1 = best.next().expect("should yield nonce 1");
5620 let t2 = best.next().expect("should yield nonce 2");
5621 let t3 = best.next().expect("should yield nonce 3");
5622
5623 assert_eq!(t1.id().nonce, 1, "expected nonce 1, got {}", t1.id().nonce);
5624 assert_eq!(t2.id().nonce, 2, "expected nonce 2, got {}", t2.id().nonce);
5625 assert_eq!(t3.id().nonce, 3, "expected nonce 3, got {}", t3.id().nonce);
5626 }
5627}