1use crate::{
4 config::{LocalTransactionConfig, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER},
5 error::{
6 Eip4844PoolTransactionError, Eip7702PoolTransactionError, InvalidPoolTransactionError,
7 PoolError, PoolErrorKind,
8 },
9 identifier::{SenderId, TransactionId},
10 metrics::{AllTransactionsMetrics, TxPoolMetrics},
11 pool::{
12 best::BestTransactions,
13 blob::BlobTransactions,
14 parked::{BasefeeOrd, ParkedPool, QueuedOrd},
15 pending::PendingPool,
16 state::{SubPool, TxState},
17 update::{Destination, PoolUpdate, UpdateOutcome},
18 AddedPendingTransaction, AddedTransaction, OnNewCanonicalStateOutcome,
19 },
20 traits::{BestTransactionsAttributes, BlockInfo, PoolSize},
21 PoolConfig, PoolResult, PoolTransaction, PoolUpdateKind, PriceBumpConfig, TransactionOrdering,
22 ValidPoolTransaction, U256,
23};
24use alloy_consensus::constants::{
25 EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID, KECCAK_EMPTY,
26 LEGACY_TX_TYPE_ID,
27};
28use alloy_eips::{
29 eip1559::{ETHEREUM_BLOCK_GAS_LIMIT_30M, MIN_PROTOCOL_BASE_FEE},
30 eip4844::BLOB_TX_MIN_BLOB_GASPRICE,
31};
32#[cfg(test)]
33use alloy_primitives::Address;
34use alloy_primitives::{
35 map::{AddressSet, B256Map, B256Set},
36 TxHash, B256,
37};
38use rustc_hash::FxHashMap;
39use smallvec::SmallVec;
40#[cfg(test)]
41use std::collections::{HashMap, HashSet};
42use std::{
43 cmp::Ordering,
44 collections::{btree_map::Entry, hash_map, BTreeMap},
45 fmt,
46 ops::Bound::{Excluded, Unbounded},
47 sync::Arc,
48};
49use tracing::{trace, warn};
50
51#[cfg_attr(doc, aquamarine::aquamarine)]
52pub struct TxPool<T: TransactionOrdering> {
94 pending_pool: PendingPool<T>,
98 config: PoolConfig,
100 queued_pool: ParkedPool<QueuedOrd<T::Transaction>>,
107 basefee_pool: ParkedPool<BasefeeOrd<T::Transaction>>,
112 blob_pool: BlobTransactions<T::Transaction>,
119 all_transactions: AllTransactions<T::Transaction>,
121 metrics: TxPoolMetrics,
123}
124
125impl<T: TransactionOrdering> TxPool<T> {
128 pub fn new(ordering: T, config: PoolConfig) -> Self {
130 Self {
131 pending_pool: PendingPool::with_buffer(
132 ordering,
133 config.max_new_pending_txs_notifications,
134 ),
135 queued_pool: Default::default(),
136 basefee_pool: Default::default(),
137 blob_pool: Default::default(),
138 all_transactions: AllTransactions::new(&config),
139 config,
140 metrics: Default::default(),
141 }
142 }
143
144 pub fn get_highest_nonce_by_sender(&self, sender: SenderId) -> Option<u64> {
146 self.all().txs_iter(sender).last().map(|(_, tx)| tx.transaction.nonce())
147 }
148
149 pub fn get_highest_transaction_by_sender(
152 &self,
153 sender: SenderId,
154 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
155 self.all().txs_iter(sender).last().map(|(_, tx)| Arc::clone(&tx.transaction))
156 }
157
158 pub(crate) fn get_highest_consecutive_transaction_by_sender(
165 &self,
166 mut on_chain: TransactionId,
167 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
168 let mut last_consecutive_tx = None;
169
170 if let Some(current) = self.all_transactions.sender_info.get(&on_chain.sender) {
172 on_chain.nonce = on_chain.nonce.max(current.state_nonce);
173 }
174
175 let mut next_expected_nonce = on_chain.nonce;
176 for (id, tx) in self.all().descendant_txs_inclusive(&on_chain) {
177 if next_expected_nonce != id.nonce {
178 break
179 }
180 next_expected_nonce = id.next_nonce();
181 last_consecutive_tx = Some(tx);
182 }
183
184 last_consecutive_tx.map(|tx| Arc::clone(&tx.transaction))
185 }
186
187 pub(crate) const fn all(&self) -> &AllTransactions<T::Transaction> {
189 &self.all_transactions
190 }
191
192 pub(crate) fn unique_senders(&self) -> AddressSet {
194 self.all_transactions.txs.values().map(|tx| tx.transaction.sender()).collect()
195 }
196
197 pub fn size(&self) -> PoolSize {
199 PoolSize {
200 pending: self.pending_pool.len(),
201 pending_size: self.pending_pool.size(),
202 basefee: self.basefee_pool.len(),
203 basefee_size: self.basefee_pool.size(),
204 queued: self.queued_pool.len(),
205 queued_size: self.queued_pool.size(),
206 blob: self.blob_pool.len(),
207 blob_size: self.blob_pool.size(),
208 total: self.all_transactions.len(),
209 }
210 }
211
212 pub const fn block_info(&self) -> BlockInfo {
214 BlockInfo {
215 block_gas_limit: self.all_transactions.block_gas_limit,
216 last_seen_block_hash: self.all_transactions.last_seen_block_hash,
217 last_seen_block_number: self.all_transactions.last_seen_block_number,
218 pending_basefee: self.all_transactions.pending_fees.base_fee,
219 pending_blob_fee: Some(self.all_transactions.pending_fees.blob_fee),
220 }
221 }
222
223 fn update_blob_fee<F>(
225 &mut self,
226 mut pending_blob_fee: u128,
227 base_fee_update: Ordering,
228 mut on_promoted: F,
229 ) where
230 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
231 {
232 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut pending_blob_fee);
233 match (self.all_transactions.pending_fees.blob_fee.cmp(&pending_blob_fee), base_fee_update)
234 {
235 (Ordering::Equal, Ordering::Equal | Ordering::Greater) => {
236 }
238 (Ordering::Greater, Ordering::Equal | Ordering::Greater) => {
239 let removed =
241 self.pending_pool.update_blob_fee(self.all_transactions.pending_fees.blob_fee);
242 for tx in removed {
243 let to = {
244 let tx =
245 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
246
247 tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
249 tx.subpool = tx.state.into();
250 tx.subpool
251 };
252 self.add_transaction_to_subpool(to, tx);
253 }
254 }
255 (Ordering::Less, _) | (_, Ordering::Less) => {
256 let removed =
258 self.blob_pool.enforce_pending_fees(&self.all_transactions.pending_fees);
259 for tx in removed {
260 let subpool = {
261 let tx_meta =
262 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
263 tx_meta.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
264 tx_meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
265 tx_meta.subpool = tx_meta.state.into();
266 tx_meta.subpool
267 };
268
269 if subpool == SubPool::Pending {
270 on_promoted(&tx);
271 }
272
273 self.add_transaction_to_subpool(subpool, tx);
274 }
275 }
276 }
277 }
278
279 fn update_basefee<F>(&mut self, mut pending_basefee: u64, mut on_promoted: F) -> Ordering
284 where
285 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
286 {
287 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut pending_basefee);
288 match self.all_transactions.pending_fees.base_fee.cmp(&pending_basefee) {
289 Ordering::Equal => {
290 Ordering::Equal
292 }
293 Ordering::Greater => {
294 let removed =
296 self.pending_pool.update_base_fee(self.all_transactions.pending_fees.base_fee);
297 for tx in removed {
298 let to = {
299 let tx =
300 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
301 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
302 tx.subpool = tx.state.into();
303 tx.subpool
304 };
305 self.add_transaction_to_subpool(to, tx);
306 }
307
308 Ordering::Greater
309 }
310 Ordering::Less => {
311 let current_base_fee = self.all_transactions.pending_fees.base_fee;
320 self.basefee_pool.enforce_basefee_with(current_base_fee, |tx| {
321 let subpool = {
323 let meta =
324 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
325 meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
326 meta.subpool = meta.state.into();
327 meta.subpool
328 };
329
330 if subpool == SubPool::Pending {
331 on_promoted(&tx);
332 }
333
334 trace!(target: "txpool", hash=%tx.transaction.hash(), pool=?subpool, "Adding transaction to a subpool");
335 match subpool {
336 SubPool::Queued => self.queued_pool.add_transaction(tx),
337 SubPool::Pending => {
338 self.pending_pool.add_transaction(tx, current_base_fee);
339 }
340 SubPool::Blob => {
341 self.blob_pool.add_transaction(tx);
342 }
343 SubPool::BaseFee => {
344 warn!(target: "txpool", "BaseFee transactions should become Pending after basefee decrease");
347 }
348 }
349 });
350
351 Ordering::Less
352 }
353 }
354 }
355
356 pub fn set_block_info(&mut self, info: BlockInfo) -> UpdateOutcome<T::Transaction> {
362 let mut outcome = UpdateOutcome::default();
363
364 let basefee_ordering = self.update_basefee(info.pending_basefee, |tx| {
366 outcome.promoted.push(tx.clone());
367 });
368 if let Some(blob_fee) = info.pending_blob_fee {
369 self.update_blob_fee(blob_fee, basefee_ordering, |tx| {
370 outcome.promoted.push(tx.clone());
371 })
372 }
373 self.all_transactions.set_block_info(info);
375
376 outcome
377 }
378
379 pub(crate) fn best_transactions(&self) -> BestTransactions<T> {
382 self.pending_pool.best()
383 }
384
385 pub(crate) fn best_transactions_with_attributes(
392 &self,
393 best_transactions_attributes: BestTransactionsAttributes,
394 ) -> Box<dyn crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T::Transaction>>>>
395 {
396 match best_transactions_attributes.basefee.cmp(&self.all_transactions.pending_fees.base_fee)
399 {
400 Ordering::Equal => {
401 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
405 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
406 Ordering::Less => {
407 let unlocked =
409 self.blob_pool.satisfy_attributes(best_transactions_attributes);
410 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
411 unlocked,
412 best_transactions_attributes.basefee,
413 new_blob_fee,
414 ))
415 }
416 Ordering::Equal => Box::new(self.pending_pool.best()),
417 Ordering::Greater => {
418 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
420 best_transactions_attributes.basefee,
421 best_transactions_attributes.blob_fee.unwrap_or_default(),
422 ))
423 }
424 }
425 }
426 Ordering::Greater => {
427 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
429 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
430 Ordering::Less => {
431 let unlocked =
433 self.blob_pool.satisfy_attributes(best_transactions_attributes);
434 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
435 unlocked,
436 best_transactions_attributes.basefee,
437 new_blob_fee,
438 ))
439 }
440 Ordering::Equal | Ordering::Greater => {
441 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
443 best_transactions_attributes.basefee,
444 new_blob_fee,
445 ))
446 }
447 }
448 }
449 Ordering::Less => {
450 let mut unlocked = self
453 .basefee_pool
454 .satisfy_base_fee_transactions(best_transactions_attributes.basefee);
455
456 unlocked.extend(self.blob_pool.satisfy_attributes(best_transactions_attributes));
458
459 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
460 unlocked,
461 best_transactions_attributes.basefee,
462 best_transactions_attributes.blob_fee.unwrap_or_default(),
463 ))
464 }
465 }
466 }
467
468 pub(crate) fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
470 self.pending_pool.all().collect()
471 }
472 pub(crate) fn pending_transactions_iter(
474 &self,
475 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
476 self.pending_pool.all()
477 }
478
479 pub(crate) fn pending_transactions_count(&self) -> usize {
481 self.pending_pool.len()
482 }
483
484 pub(crate) fn pending_transactions_with_predicate(
486 &self,
487 mut predicate: impl FnMut(&ValidPoolTransaction<T::Transaction>) -> bool,
488 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
489 self.pending_transactions_iter().filter(|tx| predicate(tx)).collect()
490 }
491
492 pub(crate) fn pending_txs_by_sender(
494 &self,
495 sender: SenderId,
496 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
497 self.pending_pool.txs_by_sender(sender)
498 }
499
500 pub(crate) fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
502 self.basefee_pool.all().chain(self.queued_pool.all()).collect()
503 }
504
505 pub(crate) fn queued_transactions_count(&self) -> usize {
507 self.basefee_pool.len() + self.queued_pool.len()
508 }
509
510 pub fn queued_and_pending_txs_by_sender(
512 &self,
513 sender: SenderId,
514 ) -> (SmallVec<[TransactionId; TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER]>, Vec<TransactionId>) {
515 (self.queued_pool.get_txs_by_sender(sender), self.pending_pool.get_txs_by_sender(sender))
516 }
517
518 pub(crate) fn queued_txs_by_sender(
520 &self,
521 sender: SenderId,
522 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
523 let mut txs = self.basefee_pool.txs_by_sender(sender);
524 txs.extend(self.queued_pool.txs_by_sender(sender));
525 txs
526 }
527
528 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
530 self.all_transactions.contains(tx_hash)
531 }
532
533 #[cfg(test)]
535 pub(crate) fn subpool_contains(&self, subpool: SubPool, id: &TransactionId) -> bool {
536 match subpool {
537 SubPool::Queued => self.queued_pool.contains(id),
538 SubPool::Pending => self.pending_pool.contains(id),
539 SubPool::BaseFee => self.basefee_pool.contains(id),
540 SubPool::Blob => self.blob_pool.contains(id),
541 }
542 }
543
544 #[inline]
546 pub(crate) fn is_exceeded(&self) -> bool {
547 self.config.is_exceeded(self.size())
548 }
549
550 pub(crate) fn get(
552 &self,
553 tx_hash: &TxHash,
554 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
555 self.all_transactions.by_hash.get(tx_hash).cloned()
556 }
557
558 pub(crate) fn get_all(
560 &self,
561 txs: Vec<TxHash>,
562 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
563 txs.into_iter().filter_map(|tx| self.get(&tx))
564 }
565
566 pub(crate) fn get_transactions_by_sender(
568 &self,
569 sender: SenderId,
570 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
571 self.all_transactions.txs_iter(sender).map(|(_, tx)| Arc::clone(&tx.transaction)).collect()
572 }
573
574 pub(crate) fn get_pending_transaction_by_sender_and_nonce(
576 &self,
577 sender: SenderId,
578 nonce: u64,
579 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
580 self.all_transactions
581 .txs_iter(sender)
582 .find(|(id, tx)| id.nonce == nonce && tx.subpool == SubPool::Pending)
583 .map(|(_, tx)| Arc::clone(&tx.transaction))
584 }
585
586 const fn update_pending_fees_only(
589 &mut self,
590 mut new_base_fee: u64,
591 new_blob_fee: Option<u128>,
592 ) -> (u64, u128) {
593 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut new_base_fee);
594
595 let prev_blob_fee = if let Some(mut blob_fee) = new_blob_fee {
596 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut blob_fee);
597 blob_fee
598 } else {
599 self.all_transactions.pending_fees.blob_fee
600 };
601
602 (new_base_fee, prev_blob_fee)
603 }
604
605 fn apply_fee_updates(
612 &mut self,
613 prev_base_fee: u64,
614 prev_blob_fee: u128,
615 outcome: &mut UpdateOutcome<T::Transaction>,
616 ) {
617 let new_base_fee = self.all_transactions.pending_fees.base_fee;
618 let new_blob_fee = self.all_transactions.pending_fees.blob_fee;
619
620 if new_base_fee == prev_base_fee && new_blob_fee == prev_blob_fee {
621 return;
623 }
624
625 self.all_transactions.pending_fees.base_fee = prev_base_fee;
628 self.all_transactions.pending_fees.blob_fee = prev_blob_fee;
629
630 let base_fee_ordering = self.update_basefee(new_base_fee, |tx| {
631 outcome.promoted.push(tx.clone());
632 });
633
634 self.update_blob_fee(new_blob_fee, base_fee_ordering, |tx| {
635 outcome.promoted.push(tx.clone());
636 });
637 }
638
639 pub(crate) fn update_accounts(
641 &mut self,
642 changed_senders: FxHashMap<SenderId, SenderInfo>,
643 ) -> UpdateOutcome<T::Transaction> {
644 let updates = self.all_transactions.update(&changed_senders);
646
647 self.all_transactions.sender_info.extend(changed_senders);
649
650 let update = self.process_updates(updates);
652 self.update_size_metrics();
654 update
655 }
656
657 pub(crate) fn on_canonical_state_change(
662 &mut self,
663 block_info: BlockInfo,
664 mined_transactions: Vec<TxHash>,
665 changed_senders: FxHashMap<SenderId, SenderInfo>,
666 _update_kind: PoolUpdateKind,
667 ) -> OnNewCanonicalStateOutcome<T::Transaction> {
668 let block_hash = block_info.last_seen_block_hash;
670
671 let mut removed_txs_count = 0;
673 for tx_hash in &mined_transactions {
674 if self.prune_transaction_by_hash(tx_hash).is_some() {
675 removed_txs_count += 1;
676 }
677 }
678
679 self.metrics.removed_transactions.increment(removed_txs_count);
681
682 let (prev_base_fee, prev_blob_fee) =
687 self.update_pending_fees_only(block_info.pending_basefee, block_info.pending_blob_fee);
688
689 let mut outcome = self.update_accounts(changed_senders);
691
692 self.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
695
696 self.all_transactions.set_block_info(block_info);
698
699 self.update_transaction_type_metrics();
700 self.metrics.performed_state_updates.increment(1);
701
702 OnNewCanonicalStateOutcome {
703 block_hash,
704 mined: mined_transactions,
705 promoted: outcome.promoted,
706 discarded: outcome.discarded,
707 }
708 }
709
710 pub(crate) fn update_size_metrics(&self) {
712 self.all_transactions.update_size_metrics();
713 let stats = self.size();
714 self.metrics.pending_pool_transactions.set(stats.pending as f64);
715 self.metrics.pending_pool_size_bytes.set(stats.pending_size as f64);
716 self.metrics.basefee_pool_transactions.set(stats.basefee as f64);
717 self.metrics.basefee_pool_size_bytes.set(stats.basefee_size as f64);
718 self.metrics.queued_pool_transactions.set(stats.queued as f64);
719 self.metrics.queued_pool_size_bytes.set(stats.queued_size as f64);
720 self.metrics.blob_pool_transactions.set(stats.blob as f64);
721 self.metrics.blob_pool_size_bytes.set(stats.blob_size as f64);
722 self.metrics.total_transactions.set(stats.total as f64);
723 }
724
725 pub(crate) fn update_transaction_type_metrics(&self) {
727 let counts = &self.all_transactions.tx_type_counts;
728 self.metrics.total_legacy_transactions.set(counts.legacy as f64);
729 self.metrics.total_eip2930_transactions.set(counts.eip2930 as f64);
730 self.metrics.total_eip1559_transactions.set(counts.eip1559 as f64);
731 self.metrics.total_eip4844_transactions.set(counts.eip4844 as f64);
732 self.metrics.total_eip7702_transactions.set(counts.eip7702 as f64);
733 self.metrics.total_other_transactions.set(counts.other as f64);
734 }
735
736 pub(crate) fn add_transaction(
737 &mut self,
738 tx: ValidPoolTransaction<T::Transaction>,
739 on_chain_balance: U256,
740 on_chain_nonce: u64,
741 on_chain_code_hash: Option<B256>,
742 ) -> PoolResult<AddedTransaction<T::Transaction>> {
743 if self.contains(tx.hash()) {
744 return Err(PoolError::new(*tx.hash(), PoolErrorKind::AlreadyImported))
745 }
746
747 self.validate_auth(&tx, on_chain_nonce, on_chain_code_hash)?;
748
749 self.all_transactions
751 .sender_info
752 .entry(tx.sender_id())
753 .or_default()
754 .update(on_chain_nonce, on_chain_balance);
755
756 match self.all_transactions.insert_tx(tx, on_chain_balance, on_chain_nonce) {
757 Ok(InsertOk { transaction, move_to, replaced_tx, mut updates, state }) => {
758 let new_nonce = transaction.id().nonce;
766 let split = updates.iter().position(|u| u.id.nonce >= new_nonce);
767 let (promoted, discarded) = match split {
768 None => {
770 let UpdateOutcome { promoted, discarded } = self.process_updates(updates);
771 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
772 (promoted, discarded)
773 }
774 Some(0) => {
776 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
777 let UpdateOutcome { promoted, discarded } = self.process_updates(updates);
778 (promoted, discarded)
779 }
780 Some(i) => {
782 let after = updates.split_off(i);
783 let mut outcome = self.process_updates(updates);
784 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
785 let after_outcome = self.process_updates(after);
786 outcome.promoted.extend(after_outcome.promoted);
787 outcome.discarded.extend(after_outcome.discarded);
788 (outcome.promoted, outcome.discarded)
789 }
790 };
791 self.metrics.inserted_transactions.increment(1);
792
793 let replaced = replaced_tx.map(|(tx, _)| tx);
794
795 let res = if move_to.is_pending() {
797 AddedTransaction::Pending(AddedPendingTransaction {
798 transaction,
799 promoted,
800 discarded,
801 replaced,
802 })
803 } else {
804 let queued_reason = state.determine_queued_reason(move_to);
806 AddedTransaction::Parked {
807 transaction,
808 subpool: move_to,
809 replaced,
810 queued_reason,
811 }
812 };
813
814 Ok(res)
815 }
816 Err(err) => {
817 self.metrics.invalid_transactions.increment(1);
819 match err {
820 InsertErr::Underpriced { existing: _, transaction } => Err(PoolError::new(
821 *transaction.hash(),
822 PoolErrorKind::ReplacementUnderpriced,
823 )),
824 InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap } => {
825 Err(PoolError::new(
826 *transaction.hash(),
827 PoolErrorKind::FeeCapBelowMinimumProtocolFeeCap(fee_cap),
828 ))
829 }
830 InsertErr::ExceededSenderTransactionsCapacity { transaction } => {
831 Err(PoolError::new(
832 *transaction.hash(),
833 PoolErrorKind::SpammerExceededCapacity(transaction.sender()),
834 ))
835 }
836 InsertErr::TxGasLimitMoreThanAvailableBlockGas {
837 transaction,
838 block_gas_limit,
839 tx_gas_limit,
840 } => Err(PoolError::new(
841 *transaction.hash(),
842 PoolErrorKind::InvalidTransaction(
843 InvalidPoolTransactionError::ExceedsGasLimit(
844 tx_gas_limit,
845 block_gas_limit,
846 ),
847 ),
848 )),
849 InsertErr::BlobTxHasNonceGap { transaction } => Err(PoolError::new(
850 *transaction.hash(),
851 PoolErrorKind::InvalidTransaction(
852 Eip4844PoolTransactionError::Eip4844NonceGap.into(),
853 ),
854 )),
855 InsertErr::Overdraft { transaction } => Err(PoolError::new(
856 *transaction.hash(),
857 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Overdraft {
858 cost: *transaction.cost(),
859 balance: on_chain_balance,
860 }),
861 )),
862 InsertErr::TxTypeConflict { transaction } => Err(PoolError::new(
863 *transaction.hash(),
864 PoolErrorKind::ExistingConflictingTransactionType(
865 transaction.sender(),
866 transaction.tx_type(),
867 ),
868 )),
869 }
870 }
871 }
872 }
873
874 fn check_delegation_limit(
878 &self,
879 transaction: &ValidPoolTransaction<T::Transaction>,
880 on_chain_nonce: u64,
881 on_chain_code_hash: Option<B256>,
882 ) -> Result<(), PoolError> {
883 if (on_chain_code_hash.is_none() || on_chain_code_hash == Some(KECCAK_EMPTY)) &&
885 !self.all_transactions.auths.contains_key(&transaction.sender_id())
886 {
887 return Ok(())
888 }
889
890 let mut txs_by_sender =
891 self.pending_pool.iter_txs_by_sender(transaction.sender_id()).peekable();
892
893 if txs_by_sender.peek().is_none() {
894 let nonce_gap_distance = transaction.nonce().saturating_sub(on_chain_nonce);
899 if nonce_gap_distance >= self.config.max_inflight_delegated_slot_limit as u64 {
900 return Err(PoolError::new(
901 *transaction.hash(),
902 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
903 Eip7702PoolTransactionError::OutOfOrderTxFromDelegated,
904 )),
905 ))
906 }
907 return Ok(())
908 }
909
910 let mut count = 0;
911 for id in txs_by_sender {
912 if id == &transaction.transaction_id {
913 return Ok(())
915 }
916 count += 1;
917 }
918
919 if count < self.config.max_inflight_delegated_slot_limit {
920 return Ok(())
922 }
923
924 Err(PoolError::new(
925 *transaction.hash(),
926 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
927 Eip7702PoolTransactionError::InflightTxLimitReached,
928 )),
929 ))
930 }
931
932 fn validate_auth(
942 &self,
943 transaction: &ValidPoolTransaction<T::Transaction>,
944 on_chain_nonce: u64,
945 on_chain_code_hash: Option<B256>,
946 ) -> Result<(), PoolError> {
947 self.check_delegation_limit(transaction, on_chain_nonce, on_chain_code_hash)?;
949
950 if let Some(authority_list) = &transaction.authority_ids {
951 for sender_id in authority_list {
952 if self
954 .all_transactions
955 .txs_iter(*sender_id)
956 .nth(self.config.max_inflight_delegated_slot_limit)
957 .is_some()
958 {
959 return Err(PoolError::new(
960 *transaction.hash(),
961 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
962 Eip7702PoolTransactionError::AuthorityReserved,
963 )),
964 ))
965 }
966 }
967 }
968
969 Ok(())
970 }
971
972 fn process_updates(&mut self, updates: Vec<PoolUpdate>) -> UpdateOutcome<T::Transaction> {
976 let mut outcome = UpdateOutcome::default();
977 let mut removed = 0;
978 for PoolUpdate { id, current, destination } in updates {
979 match destination {
980 Destination::Discard => {
981 if let Some(tx) = self.prune_transaction_by_id(&id) {
983 outcome.discarded.push(tx);
984 }
985 removed += 1;
986 }
987 Destination::Pool(move_to) => {
988 debug_assert_ne!(&move_to, ¤t, "destination must be different");
989 let moved = self.move_transaction(current, move_to, &id);
990 if matches!(move_to, SubPool::Pending) &&
991 let Some(tx) = moved
992 {
993 trace!(target: "txpool", hash=%tx.transaction.hash(), "Promoted transaction to pending");
994 outcome.promoted.push(tx);
995 }
996 }
997 }
998 }
999
1000 if removed > 0 {
1001 self.metrics.removed_transactions.increment(removed);
1002 }
1003
1004 outcome
1005 }
1006
1007 fn move_transaction(
1012 &mut self,
1013 from: SubPool,
1014 to: SubPool,
1015 id: &TransactionId,
1016 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1017 let tx = self.remove_from_subpool(from, id)?;
1018 self.add_transaction_to_subpool(to, tx.clone());
1019 Some(tx)
1020 }
1021
1022 pub(crate) fn remove_transactions(
1027 &mut self,
1028 hashes: Vec<TxHash>,
1029 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1030 let txs =
1031 hashes.into_iter().filter_map(|hash| self.remove_transaction_by_hash(&hash)).collect();
1032 self.update_size_metrics();
1033 txs
1034 }
1035
1036 pub(crate) fn remove_transactions_and_descendants(
1038 &mut self,
1039 hashes: Vec<TxHash>,
1040 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1041 let mut removed = Vec::new();
1042 for hash in hashes {
1043 if let Some(tx) = self.remove_transaction_by_hash(&hash) {
1044 removed.push(tx.clone());
1045 self.remove_descendants(tx.id(), &mut removed);
1046 }
1047 }
1048 self.update_size_metrics();
1049 removed
1050 }
1051
1052 pub(crate) fn remove_transactions_by_sender(
1054 &mut self,
1055 sender_id: SenderId,
1056 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1057 let mut removed = Vec::new();
1058 let txs = self.get_transactions_by_sender(sender_id);
1059 for tx in txs {
1060 if let Some(tx) = self.remove_transaction(tx.id()) {
1061 removed.push(tx);
1062 }
1063 }
1064 self.update_size_metrics();
1065 removed
1066 }
1067
1068 pub(crate) fn prune_transactions(
1074 &mut self,
1075 hashes: Vec<TxHash>,
1076 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1077 let txs =
1078 hashes.into_iter().filter_map(|hash| self.prune_transaction_by_hash(&hash)).collect();
1079 self.update_size_metrics();
1080 txs
1081 }
1082
1083 fn remove_transaction(
1087 &mut self,
1088 id: &TransactionId,
1089 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1090 let (tx, pool) = self.all_transactions.remove_transaction(id)?;
1091 self.remove_from_subpool(pool, tx.id())
1092 }
1093
1094 fn remove_transaction_by_hash(
1100 &mut self,
1101 tx_hash: &B256,
1102 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1103 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1104
1105 let updates = self.all_transactions.park_descendant_transactions(tx.id());
1107 self.process_updates(updates);
1108 self.remove_from_subpool(pool, tx.id())
1109 }
1110
1111 fn prune_transaction_by_hash(
1118 &mut self,
1119 tx_hash: &B256,
1120 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1121 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1122 self.remove_from_subpool(pool, tx.id())
1123 }
1124 fn prune_transaction_by_id(
1129 &mut self,
1130 tx_id: &TransactionId,
1131 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1132 let (tx, pool) = self.all_transactions.remove_transaction_by_id(tx_id)?;
1133 self.remove_from_subpool(pool, tx.id())
1134 }
1135
1136 fn remove_from_subpool(
1140 &mut self,
1141 pool: SubPool,
1142 tx: &TransactionId,
1143 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1144 let tx = match pool {
1145 SubPool::Queued => self.queued_pool.remove_transaction(tx),
1146 SubPool::Pending => self.pending_pool.remove_transaction(tx),
1147 SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
1148 SubPool::Blob => self.blob_pool.remove_transaction(tx),
1149 };
1150
1151 if let Some(ref tx) = tx {
1152 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Removed transaction from a subpool");
1156 }
1157
1158 tx
1159 }
1160
1161 fn remove_descendants(
1165 &mut self,
1166 tx: &TransactionId,
1167 removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
1168 ) {
1169 let mut id = *tx;
1170
1171 loop {
1173 let descendant =
1174 self.all_transactions.descendant_txs_exclusive(&id).map(|(id, _)| *id).next();
1175 if let Some(descendant) = descendant {
1176 if let Some(tx) = self.remove_transaction(&descendant) {
1177 removed.push(tx)
1178 }
1179 id = descendant;
1180 } else {
1181 return
1182 }
1183 }
1184 }
1185
1186 fn add_transaction_to_subpool(
1188 &mut self,
1189 pool: SubPool,
1190 tx: Arc<ValidPoolTransaction<T::Transaction>>,
1191 ) {
1192 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Adding transaction to a subpool");
1196 match pool {
1197 SubPool::Queued => self.queued_pool.add_transaction(tx),
1198 SubPool::Pending => {
1199 self.pending_pool.add_transaction(tx, self.all_transactions.pending_fees.base_fee);
1200 }
1201 SubPool::BaseFee => {
1202 self.basefee_pool.add_transaction(tx);
1203 }
1204 SubPool::Blob => {
1205 self.blob_pool.add_transaction(tx);
1206 }
1207 }
1208 }
1209
1210 fn add_new_transaction(
1213 &mut self,
1214 transaction: Arc<ValidPoolTransaction<T::Transaction>>,
1215 replaced: Option<(Arc<ValidPoolTransaction<T::Transaction>>, SubPool)>,
1216 pool: SubPool,
1217 ) {
1218 if let Some((replaced, replaced_pool)) = replaced {
1219 self.remove_from_subpool(replaced_pool, replaced.id());
1221 }
1222
1223 self.add_transaction_to_subpool(pool, transaction)
1224 }
1225
1226 pub(crate) fn discard_worst(&mut self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1233 let mut removed = Vec::new();
1234
1235 macro_rules! discard_worst {
1237 ($this:ident, $removed:ident, [$($limit:ident => ($pool:ident, $metric:ident)),* $(,)*]) => {
1238 $ (
1239 while $this.$pool.exceeds(&$this.config.$limit)
1240 {
1241 trace!(
1242 target: "txpool",
1243 "discarding transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1244 stringify!($pool),
1245 $this.config.$limit,
1246 $this.$pool.size(),
1247 $this.$pool.len(),
1248 );
1249
1250 let removed_from_subpool = $this.$pool.truncate_pool($this.config.$limit.clone());
1252
1253 trace!(
1254 target: "txpool",
1255 "removed {} transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1256 removed_from_subpool.len(),
1257 stringify!($pool),
1258 $this.config.$limit,
1259 $this.$pool.size(),
1260 $this.$pool.len()
1261 );
1262 $this.metrics.$metric.increment(removed_from_subpool.len() as u64);
1263
1264 for tx in removed_from_subpool {
1266 $this.all_transactions.remove_transaction(tx.id());
1267
1268 let id = *tx.id();
1269
1270 removed.push(tx);
1272
1273 $this.remove_descendants(&id, &mut $removed);
1275 }
1276 }
1277
1278 )*
1279 };
1280 }
1281
1282 discard_worst!(
1283 self, removed, [
1284 pending_limit => (pending_pool, pending_transactions_evicted),
1285 basefee_limit => (basefee_pool, basefee_transactions_evicted),
1286 blob_limit => (blob_pool, blob_transactions_evicted),
1287 queued_limit => (queued_pool, queued_transactions_evicted),
1288 ]
1289 );
1290
1291 removed
1292 }
1293
1294 pub(crate) fn len(&self) -> usize {
1296 self.all_transactions.len()
1297 }
1298
1299 pub(crate) fn is_empty(&self) -> bool {
1301 self.all_transactions.is_empty()
1302 }
1303
1304 #[cfg(any(test, feature = "test-utils"))]
1312 pub fn assert_invariants(&self) {
1313 let size = self.size();
1314 let actual = size.basefee + size.pending + size.queued + size.blob;
1315 assert_eq!(
1316 size.total, actual,
1317 "total size must be equal to the sum of all sub-pools, basefee:{}, pending:{}, queued:{}, blob:{}",
1318 size.basefee, size.pending, size.queued, size.blob
1319 );
1320 self.all_transactions.assert_invariants();
1321 self.pending_pool.assert_invariants();
1322 self.basefee_pool.assert_invariants();
1323 self.queued_pool.assert_invariants();
1324 self.blob_pool.assert_invariants();
1325 }
1326}
1327
1328#[cfg(any(test, feature = "test-utils"))]
1329impl TxPool<crate::test_utils::MockOrdering> {
1330 pub fn mock() -> Self {
1332 Self::new(crate::test_utils::MockOrdering::default(), PoolConfig::default())
1333 }
1334}
1335
1336#[cfg(test)]
1337impl<T: TransactionOrdering> Drop for TxPool<T> {
1338 fn drop(&mut self) {
1339 self.assert_invariants();
1340 }
1341}
1342
1343impl<T: TransactionOrdering> TxPool<T> {
1344 pub const fn pending(&self) -> &PendingPool<T> {
1346 &self.pending_pool
1347 }
1348
1349 pub const fn base_fee(&self) -> &ParkedPool<BasefeeOrd<T::Transaction>> {
1351 &self.basefee_pool
1352 }
1353
1354 pub const fn queued(&self) -> &ParkedPool<QueuedOrd<T::Transaction>> {
1356 &self.queued_pool
1357 }
1358}
1359
1360impl<T: TransactionOrdering> fmt::Debug for TxPool<T> {
1361 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1362 f.debug_struct("TxPool").field("config", &self.config).finish_non_exhaustive()
1363 }
1364}
1365
1366pub(crate) struct AllTransactions<T: PoolTransaction> {
1371 minimal_protocol_basefee: u64,
1375 block_gas_limit: u64,
1377 max_account_slots: usize,
1379 by_hash: B256Map<Arc<ValidPoolTransaction<T>>>,
1381 txs: BTreeMap<TransactionId, PoolInternalTransaction<T>>,
1383 sender_info: FxHashMap<SenderId, SenderInfo>,
1385 tx_counter: FxHashMap<SenderId, usize>,
1387 last_seen_block_number: u64,
1389 last_seen_block_hash: B256,
1391 pending_fees: PendingFees,
1393 price_bumps: PriceBumpConfig,
1395 local_transactions_config: LocalTransactionConfig,
1397 auths: FxHashMap<SenderId, B256Set>,
1399 tx_type_counts: TxTypeCounts,
1402 metrics: AllTransactionsMetrics,
1404}
1405
1406impl<T: PoolTransaction> AllTransactions<T> {
1407 fn new(config: &PoolConfig) -> Self {
1409 Self {
1410 max_account_slots: config.max_account_slots,
1411 price_bumps: config.price_bumps,
1412 local_transactions_config: config.local_transactions_config.clone(),
1413 minimal_protocol_basefee: config.minimal_protocol_basefee,
1414 block_gas_limit: config.gas_limit,
1415 ..Default::default()
1416 }
1417 }
1418
1419 #[expect(dead_code)]
1421 pub(crate) fn hashes_iter(&self) -> impl Iterator<Item = TxHash> + '_ {
1422 self.by_hash.keys().copied()
1423 }
1424
1425 pub(crate) fn transactions_iter(
1427 &self,
1428 ) -> impl Iterator<Item = &Arc<ValidPoolTransaction<T>>> + '_ {
1429 self.by_hash.values()
1430 }
1431
1432 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
1434 self.by_hash.contains_key(tx_hash)
1435 }
1436
1437 pub(crate) fn get(&self, id: &TransactionId) -> Option<&PoolInternalTransaction<T>> {
1439 self.txs.get(id)
1440 }
1441
1442 pub(crate) fn tx_inc(&mut self, sender: SenderId) {
1444 let count = self.tx_counter.entry(sender).or_default();
1445 *count += 1;
1446 self.metrics.all_transactions_by_all_senders.increment(1.0);
1447 }
1448
1449 pub(crate) fn tx_decr(&mut self, sender: SenderId) {
1451 if let hash_map::Entry::Occupied(mut entry) = self.tx_counter.entry(sender) {
1452 let count = entry.get_mut();
1453 if *count == 1 {
1454 entry.remove();
1455 self.sender_info.remove(&sender);
1456 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1457 return
1458 }
1459 *count -= 1;
1460 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1461 }
1462 }
1463
1464 fn set_block_info(&mut self, block_info: BlockInfo) {
1466 let BlockInfo {
1467 block_gas_limit,
1468 last_seen_block_hash,
1469 last_seen_block_number,
1470 pending_basefee,
1471 pending_blob_fee,
1472 } = block_info;
1473 self.last_seen_block_number = last_seen_block_number;
1474 self.last_seen_block_hash = last_seen_block_hash;
1475
1476 self.pending_fees.base_fee = pending_basefee;
1477 self.metrics.base_fee.set(pending_basefee as f64);
1478
1479 self.block_gas_limit = block_gas_limit;
1480
1481 if let Some(pending_blob_fee) = pending_blob_fee {
1482 self.pending_fees.blob_fee = pending_blob_fee;
1483 self.metrics.blob_base_fee.set(pending_blob_fee as f64);
1484 }
1485 }
1486
1487 pub(crate) fn update_size_metrics(&self) {
1489 self.metrics.all_transactions_by_hash.set(self.by_hash.len() as f64);
1490 self.metrics.all_transactions_by_id.set(self.txs.len() as f64);
1491 }
1492
1493 pub(crate) fn update(
1510 &mut self,
1511 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1512 ) -> Vec<PoolUpdate> {
1513 let mut updates = Vec::with_capacity(64);
1515
1516 let mut iter = self.txs.iter_mut().peekable();
1517
1518 'transactions: while let Some((id, tx)) = iter.next() {
1528 macro_rules! next_sender {
1529 ($iter:ident) => {
1530 'this: while let Some((peek, _)) = iter.peek() {
1531 if peek.sender != id.sender {
1532 break 'this
1533 }
1534 iter.next();
1535 }
1536 };
1537 }
1538
1539 let changed_balance = if let Some(info) = changed_accounts.get(&id.sender) {
1542 if id.nonce < info.state_nonce {
1544 updates.push(PoolUpdate {
1545 id: *tx.transaction.id(),
1546 current: tx.subpool,
1547 destination: Destination::Discard,
1548 });
1549 continue 'transactions
1550 }
1551
1552 let ancestor = TransactionId::ancestor(id.nonce, info.state_nonce, id.sender);
1553 if ancestor.is_none() {
1555 tx.state.insert(TxState::NO_NONCE_GAPS);
1556 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1557 tx.cumulative_cost = U256::ZERO;
1558 if tx.transaction.cost() > &info.balance {
1559 tx.state.remove(TxState::ENOUGH_BALANCE);
1561 } else {
1562 tx.state.insert(TxState::ENOUGH_BALANCE);
1563 }
1564 }
1565
1566 Some(&info.balance)
1567 } else {
1568 None
1569 };
1570
1571 if tx.state.has_nonce_gap() {
1573 next_sender!(iter);
1574 continue 'transactions
1575 }
1576
1577 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1579
1580 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1582 Self::record_subpool_update(&mut updates, tx);
1584
1585 let mut has_parked_ancestor = !tx.state.is_pending();
1587
1588 let mut cumulative_cost = tx.next_cumulative_cost();
1589
1590 let mut next_nonce_in_line = tx.transaction.nonce().saturating_add(1);
1592
1593 while let Some((peek, tx)) = iter.peek_mut() {
1595 if peek.sender != id.sender {
1596 continue 'transactions
1598 }
1599
1600 if tx.transaction.nonce() == next_nonce_in_line {
1601 tx.state.insert(TxState::NO_NONCE_GAPS);
1603 } else {
1604 next_sender!(iter);
1606 continue 'transactions
1607 }
1608
1609 next_nonce_in_line = next_nonce_in_line.saturating_add(1);
1611
1612 tx.cumulative_cost = cumulative_cost;
1614 cumulative_cost = tx.next_cumulative_cost();
1616
1617 if let Some(changed_balance) = changed_balance {
1619 if &cumulative_cost > changed_balance {
1620 tx.state.remove(TxState::ENOUGH_BALANCE);
1622 } else {
1623 tx.state.insert(TxState::ENOUGH_BALANCE);
1624 }
1625 }
1626
1627 if has_parked_ancestor {
1629 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1630 } else {
1631 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1632 }
1633 has_parked_ancestor = !tx.state.is_pending();
1634
1635 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1637 Self::record_subpool_update(&mut updates, tx);
1638
1639 iter.next();
1641 }
1642 }
1643
1644 updates
1645 }
1646
1647 fn record_subpool_update(updates: &mut Vec<PoolUpdate>, tx: &mut PoolInternalTransaction<T>) {
1652 let current_pool = tx.subpool;
1653 tx.subpool = tx.state.into();
1654 if current_pool != tx.subpool {
1655 updates.push(PoolUpdate {
1656 id: *tx.transaction.id(),
1657 current: current_pool,
1658 destination: tx.subpool.into(),
1659 })
1660 }
1661 }
1662
1663 fn update_tx_base_fee(pending_block_base_fee: u64, tx: &mut PoolInternalTransaction<T>) {
1665 match tx.transaction.max_fee_per_gas().cmp(&(pending_block_base_fee as u128)) {
1667 Ordering::Greater | Ordering::Equal => {
1668 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1669 }
1670 Ordering::Less => {
1671 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
1672 }
1673 }
1674 }
1675
1676 pub(crate) fn txs_iter(
1679 &self,
1680 sender: SenderId,
1681 ) -> impl Iterator<Item = (&TransactionId, &PoolInternalTransaction<T>)> + '_ {
1682 self.txs
1683 .range((sender.start_bound(), Unbounded))
1684 .take_while(move |(other, _)| sender == other.sender)
1685 }
1686
1687 #[cfg(test)]
1690 #[expect(dead_code)]
1691 pub(crate) fn txs_iter_mut(
1692 &mut self,
1693 sender: SenderId,
1694 ) -> impl Iterator<Item = (&TransactionId, &mut PoolInternalTransaction<T>)> + '_ {
1695 self.txs
1696 .range_mut((sender.start_bound(), Unbounded))
1697 .take_while(move |(other, _)| sender == other.sender)
1698 }
1699
1700 pub(crate) fn descendant_txs_exclusive<'a, 'b: 'a>(
1704 &'a self,
1705 id: &'b TransactionId,
1706 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1707 self.txs.range((Excluded(id), Unbounded)).take_while(|(other, _)| id.sender == other.sender)
1708 }
1709
1710 pub(crate) fn descendant_txs_inclusive<'a, 'b: 'a>(
1715 &'a self,
1716 id: &'b TransactionId,
1717 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1718 self.txs.range(id..).take_while(|(other, _)| id.sender == other.sender)
1719 }
1720
1721 pub(crate) fn descendant_txs_mut<'a, 'b: 'a>(
1726 &'a mut self,
1727 id: &'b TransactionId,
1728 ) -> impl Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)> + 'a {
1729 self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender)
1730 }
1731
1732 pub(crate) fn remove_transaction_by_hash(
1734 &mut self,
1735 tx_hash: &B256,
1736 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1737 let tx = self.by_hash.remove(tx_hash)?;
1738 let internal = self.txs.remove(&tx.transaction_id)?;
1739 self.remove_auths(&internal);
1740 self.tx_type_counts.dec(internal.transaction.transaction.ty());
1741 self.tx_decr(tx.sender_id());
1743 Some((tx, internal.subpool))
1744 }
1745
1746 pub(crate) fn remove_transaction_by_id(
1750 &mut self,
1751 tx_id: &TransactionId,
1752 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1753 let internal = self.txs.remove(tx_id)?;
1754 let tx = self.by_hash.remove(internal.transaction.hash())?;
1755 self.remove_auths(&internal);
1756 self.tx_type_counts.dec(internal.transaction.transaction.ty());
1757 self.tx_decr(tx.sender_id());
1759 Some((tx, internal.subpool))
1760 }
1761
1762 pub(crate) fn park_descendant_transactions(
1764 &mut self,
1765 tx_id: &TransactionId,
1766 ) -> Vec<PoolUpdate> {
1767 let mut updates = Vec::new();
1768
1769 for (id, tx) in self.descendant_txs_mut(tx_id) {
1770 let current_pool = tx.subpool;
1771
1772 tx.state.remove(TxState::NO_NONCE_GAPS);
1773
1774 tx.subpool = tx.state.into();
1776
1777 if current_pool != tx.subpool {
1779 updates.push(PoolUpdate {
1780 id: *id,
1781 current: current_pool,
1782 destination: tx.subpool.into(),
1783 })
1784 }
1785 }
1786
1787 updates
1788 }
1789
1790 pub(crate) fn remove_transaction(
1796 &mut self,
1797 id: &TransactionId,
1798 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1799 let internal = self.txs.remove(id)?;
1800
1801 self.tx_decr(internal.transaction.sender_id());
1803 self.tx_type_counts.dec(internal.transaction.transaction.ty());
1804
1805 let result =
1806 self.by_hash.remove(internal.transaction.hash()).map(|tx| (tx, internal.subpool));
1807
1808 self.remove_auths(&internal);
1809
1810 result
1811 }
1812
1813 fn remove_auths(&mut self, tx: &PoolInternalTransaction<T>) {
1817 let Some(auths) = &tx.transaction.authority_ids else { return };
1818
1819 let tx_hash = tx.transaction.hash();
1820 for auth in auths {
1821 if let Some(list) = self.auths.get_mut(auth) {
1822 list.remove(tx_hash);
1823 if list.is_empty() {
1824 self.auths.remove(auth);
1825 }
1826 }
1827 }
1828 }
1829
1830 #[inline]
1836 fn contains_conflicting_transaction(&self, tx: &ValidPoolTransaction<T>) -> bool {
1837 self.txs_iter(tx.transaction_id.sender)
1838 .next()
1839 .is_some_and(|(_, existing)| tx.tx_type_conflicts_with(&existing.transaction))
1840 }
1841
1842 fn ensure_valid(
1851 &self,
1852 transaction: ValidPoolTransaction<T>,
1853 on_chain_nonce: u64,
1854 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1855 if !self.local_transactions_config.is_local(transaction.origin, transaction.sender_ref()) {
1856 let current_txs =
1857 self.tx_counter.get(&transaction.sender_id()).copied().unwrap_or_default();
1858
1859 if current_txs >= self.max_account_slots && transaction.nonce() > on_chain_nonce {
1862 return Err(InsertErr::ExceededSenderTransactionsCapacity {
1863 transaction: Arc::new(transaction),
1864 })
1865 }
1866 }
1867 if transaction.gas_limit() > self.block_gas_limit {
1868 return Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas {
1869 block_gas_limit: self.block_gas_limit,
1870 tx_gas_limit: transaction.gas_limit(),
1871 transaction: Arc::new(transaction),
1872 })
1873 }
1874
1875 if self.contains_conflicting_transaction(&transaction) {
1876 return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) })
1878 }
1879
1880 Ok(transaction)
1881 }
1882
1883 fn ensure_valid_blob_transaction(
1889 &self,
1890 new_blob_tx: ValidPoolTransaction<T>,
1891 on_chain_balance: U256,
1892 ancestor: Option<TransactionId>,
1893 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1894 if let Some(ancestor) = ancestor {
1895 let Some(ancestor_tx) = self.txs.get(&ancestor) else {
1896 self.metrics.blob_transactions_nonce_gaps.increment(1);
1898 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1899 };
1900 if ancestor_tx.state.has_nonce_gap() {
1901 self.metrics.blob_transactions_nonce_gaps.increment(1);
1904 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1905 }
1906
1907 let mut cumulative_cost = ancestor_tx.next_cumulative_cost() + new_blob_tx.cost();
1909
1910 if cumulative_cost > on_chain_balance {
1912 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1914 }
1915
1916 let id = new_blob_tx.transaction_id;
1919 let mut descendants = self.descendant_txs_inclusive(&id).peekable();
1920 if let Some((maybe_replacement, _)) = descendants.peek() &&
1921 **maybe_replacement == new_blob_tx.transaction_id
1922 {
1923 descendants.next();
1925
1926 for (_, tx) in descendants {
1928 cumulative_cost += tx.transaction.cost();
1929 if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance {
1930 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1932 }
1933 }
1934 }
1935 } else if new_blob_tx.cost() > &on_chain_balance {
1936 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1938 }
1939
1940 Ok(new_blob_tx)
1941 }
1942
1943 pub(crate) fn insert_tx(
1975 &mut self,
1976 transaction: ValidPoolTransaction<T>,
1977 on_chain_balance: U256,
1978 on_chain_nonce: u64,
1979 ) -> InsertResult<T> {
1980 assert!(on_chain_nonce <= transaction.nonce(), "Invalid transaction");
1981
1982 let mut transaction = self.ensure_valid(transaction, on_chain_nonce)?;
1983
1984 let inserted_tx_id = *transaction.id();
1985 let mut state = TxState::default();
1986 let mut cumulative_cost = U256::ZERO;
1987 let mut updates = Vec::new();
1988
1989 state.insert(TxState::NOT_TOO_MUCH_GAS);
1991
1992 let ancestor = TransactionId::ancestor(
1995 transaction.transaction.nonce(),
1996 on_chain_nonce,
1997 inserted_tx_id.sender,
1998 );
1999
2000 if transaction.is_eip4844() {
2003 state.insert(TxState::BLOB_TRANSACTION);
2004
2005 transaction =
2006 self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
2007 let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();
2008 if blob_fee_cap >= self.pending_fees.blob_fee {
2009 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
2010 }
2011 } else {
2012 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
2014 }
2015
2016 let transaction = Arc::new(transaction);
2017
2018 if ancestor.is_none() {
2020 state.insert(TxState::NO_NONCE_GAPS);
2021 state.insert(TxState::NO_PARKED_ANCESTORS);
2022 }
2023
2024 let fee_cap = transaction.max_fee_per_gas();
2026
2027 if fee_cap < self.minimal_protocol_basefee as u128 {
2028 return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap })
2029 }
2030 if fee_cap >= self.pending_fees.base_fee as u128 {
2031 state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
2032 }
2033
2034 let mut replaced_tx = None;
2036
2037 let pool_tx = PoolInternalTransaction {
2038 transaction: Arc::clone(&transaction),
2039 subpool: state.into(),
2040 state,
2041 cumulative_cost,
2042 };
2043
2044 match self.txs.entry(*transaction.id()) {
2046 Entry::Vacant(entry) => {
2047 self.by_hash.insert(*pool_tx.transaction.hash(), pool_tx.transaction.clone());
2049 self.tx_type_counts.inc(pool_tx.transaction.transaction.ty());
2050 entry.insert(pool_tx);
2051 }
2052 Entry::Occupied(mut entry) => {
2053 let existing_transaction = entry.get().transaction.as_ref();
2055 let maybe_replacement = transaction.as_ref();
2056
2057 if existing_transaction.is_underpriced(maybe_replacement, &self.price_bumps) {
2059 return Err(InsertErr::Underpriced {
2060 transaction: pool_tx.transaction,
2061 existing: *entry.get().transaction.hash(),
2062 })
2063 }
2064 let new_hash = *pool_tx.transaction.hash();
2065 let new_transaction = pool_tx.transaction.clone();
2066 self.tx_type_counts.inc(pool_tx.transaction.transaction.ty());
2067 let replaced = entry.insert(pool_tx);
2068 self.tx_type_counts.dec(replaced.transaction.transaction.ty());
2069 self.by_hash.remove(replaced.transaction.hash());
2070 self.by_hash.insert(new_hash, new_transaction);
2071
2072 self.remove_auths(&replaced);
2073
2074 replaced_tx = Some((replaced.transaction, replaced.subpool));
2076 }
2077 }
2078
2079 if let Some(auths) = &transaction.authority_ids {
2080 let tx_hash = transaction.hash();
2081 for auth in auths {
2082 self.auths.entry(*auth).or_default().insert(*tx_hash);
2083 }
2084 }
2085
2086 let on_chain_id = TransactionId::new(transaction.sender_id(), on_chain_nonce);
2088 {
2089 let mut next_nonce = on_chain_id.nonce;
2091
2092 let mut has_parked_ancestor = false;
2096
2097 for (id, tx) in self.descendant_txs_mut(&on_chain_id) {
2100 let current_pool = tx.subpool;
2101
2102 if next_nonce != id.nonce {
2104 break
2105 }
2106
2107 tx.state.insert(TxState::NO_NONCE_GAPS);
2109
2110 tx.cumulative_cost = cumulative_cost;
2112
2113 cumulative_cost = tx.next_cumulative_cost();
2115
2116 if cumulative_cost > on_chain_balance {
2117 tx.state.remove(TxState::ENOUGH_BALANCE);
2119 } else {
2120 tx.state.insert(TxState::ENOUGH_BALANCE);
2121 }
2122
2123 if has_parked_ancestor {
2125 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
2126 } else {
2127 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
2128 }
2129 has_parked_ancestor = !tx.state.is_pending();
2130
2131 tx.subpool = tx.state.into();
2133
2134 if inserted_tx_id.eq(id) {
2135 state = tx.state;
2137 } else {
2138 if current_pool != tx.subpool {
2140 updates.push(PoolUpdate {
2141 id: *id,
2142 current: current_pool,
2143 destination: tx.subpool.into(),
2144 })
2145 }
2146 }
2147
2148 next_nonce = id.next_nonce();
2150 }
2151 }
2152
2153 if replaced_tx.is_none() {
2155 self.tx_inc(inserted_tx_id.sender);
2156 }
2157
2158 Ok(InsertOk { transaction, move_to: state.into(), state, replaced_tx, updates })
2159 }
2160
2161 pub(crate) fn len(&self) -> usize {
2163 self.txs.len()
2164 }
2165
2166 pub(crate) fn is_empty(&self) -> bool {
2168 self.txs.is_empty()
2169 }
2170
2171 #[cfg(any(test, feature = "test-utils"))]
2173 pub(crate) fn assert_invariants(&self) {
2174 assert_eq!(self.by_hash.len(), self.txs.len(), "by_hash.len() != txs.len()");
2175 assert!(self.auths.len() <= self.txs.len(), "auths.len() > txs.len()");
2176 }
2177}
2178
2179#[cfg(test)]
2180impl<T: PoolTransaction> AllTransactions<T> {
2181 pub(crate) fn tx_count(&self, sender: SenderId) -> usize {
2185 self.tx_counter.get(&sender).copied().unwrap_or_default()
2186 }
2187}
2188
2189impl<T: PoolTransaction> Default for AllTransactions<T> {
2190 fn default() -> Self {
2191 Self {
2192 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
2193 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
2194 block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
2195 by_hash: Default::default(),
2196 txs: Default::default(),
2197 sender_info: Default::default(),
2198 tx_counter: Default::default(),
2199 last_seen_block_number: Default::default(),
2200 last_seen_block_hash: Default::default(),
2201 pending_fees: Default::default(),
2202 price_bumps: Default::default(),
2203 local_transactions_config: Default::default(),
2204 auths: Default::default(),
2205 tx_type_counts: Default::default(),
2206 metrics: Default::default(),
2207 }
2208 }
2209}
2210
2211#[derive(Debug, Clone, Copy, Default)]
2216pub(crate) struct TxTypeCounts {
2217 legacy: u64,
2218 eip2930: u64,
2219 eip1559: u64,
2220 eip4844: u64,
2221 eip7702: u64,
2222 other: u64,
2223}
2224
2225impl TxTypeCounts {
2226 const fn counter_mut(&mut self, tx_type: u8) -> &mut u64 {
2228 match tx_type {
2229 LEGACY_TX_TYPE_ID => &mut self.legacy,
2230 EIP2930_TX_TYPE_ID => &mut self.eip2930,
2231 EIP1559_TX_TYPE_ID => &mut self.eip1559,
2232 EIP4844_TX_TYPE_ID => &mut self.eip4844,
2233 EIP7702_TX_TYPE_ID => &mut self.eip7702,
2234 _ => &mut self.other,
2235 }
2236 }
2237
2238 const fn inc(&mut self, tx_type: u8) {
2240 *self.counter_mut(tx_type) += 1;
2241 }
2242
2243 const fn dec(&mut self, tx_type: u8) {
2245 *self.counter_mut(tx_type) -= 1;
2246 }
2247}
2248
2249#[derive(Debug, Clone)]
2251pub(crate) struct PendingFees {
2252 pub(crate) base_fee: u64,
2254 pub(crate) blob_fee: u128,
2256}
2257
2258impl Default for PendingFees {
2259 fn default() -> Self {
2260 Self { base_fee: Default::default(), blob_fee: BLOB_TX_MIN_BLOB_GASPRICE }
2261 }
2262}
2263
2264pub(crate) type InsertResult<T> = Result<InsertOk<T>, InsertErr<T>>;
2266
2267#[derive(Debug)]
2269pub(crate) enum InsertErr<T: PoolTransaction> {
2270 Underpriced {
2272 transaction: Arc<ValidPoolTransaction<T>>,
2273 #[expect(dead_code)]
2274 existing: TxHash,
2275 },
2276 BlobTxHasNonceGap { transaction: Arc<ValidPoolTransaction<T>> },
2278 Overdraft { transaction: Arc<ValidPoolTransaction<T>> },
2281 FeeCapBelowMinimumProtocolFeeCap { transaction: Arc<ValidPoolTransaction<T>>, fee_cap: u128 },
2285 ExceededSenderTransactionsCapacity { transaction: Arc<ValidPoolTransaction<T>> },
2289 TxGasLimitMoreThanAvailableBlockGas {
2291 transaction: Arc<ValidPoolTransaction<T>>,
2292 block_gas_limit: u64,
2293 tx_gas_limit: u64,
2294 },
2295 TxTypeConflict { transaction: Arc<ValidPoolTransaction<T>> },
2297}
2298
2299#[derive(Debug)]
2301pub(crate) struct InsertOk<T: PoolTransaction> {
2302 transaction: Arc<ValidPoolTransaction<T>>,
2304 move_to: SubPool,
2306 state: TxState,
2308 replaced_tx: Option<(Arc<ValidPoolTransaction<T>>, SubPool)>,
2310 updates: Vec<PoolUpdate>,
2312}
2313
2314#[derive(Debug)]
2317pub(crate) struct PoolInternalTransaction<T: PoolTransaction> {
2318 pub(crate) transaction: Arc<ValidPoolTransaction<T>>,
2320 pub(crate) subpool: SubPool,
2322 pub(crate) state: TxState,
2325 pub(crate) cumulative_cost: U256,
2330}
2331
2332impl<T: PoolTransaction> PoolInternalTransaction<T> {
2335 fn next_cumulative_cost(&self) -> U256 {
2336 self.cumulative_cost + self.transaction.cost()
2337 }
2338}
2339
2340#[derive(Debug, Clone, Default)]
2342pub(crate) struct SenderInfo {
2343 pub(crate) state_nonce: u64,
2345 pub(crate) balance: U256,
2347}
2348
2349impl SenderInfo {
2352 const fn update(&mut self, state_nonce: u64, balance: U256) {
2354 *self = Self { state_nonce, balance };
2355 }
2356}
2357
2358#[cfg(test)]
2359mod tests {
2360 use super::*;
2361 use crate::{
2362 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
2363 traits::TransactionOrigin,
2364 SubPoolLimit,
2365 };
2366 use alloy_consensus::{Transaction, TxType};
2367 use alloy_primitives::address;
2368
2369 #[test]
2370 fn test_insert_blob() {
2371 let on_chain_balance = U256::MAX;
2372 let on_chain_nonce = 0;
2373 let mut f = MockTransactionFactory::default();
2374 let mut pool = AllTransactions::default();
2375 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2376 let valid_tx = f.validated(tx);
2377 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2378 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2379 assert!(updates.is_empty());
2380 assert!(replaced_tx.is_none());
2381 assert!(state.contains(TxState::NO_NONCE_GAPS));
2382 assert!(state.contains(TxState::ENOUGH_BALANCE));
2383 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2384 assert_eq!(move_to, SubPool::Pending);
2385
2386 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2387 assert_eq!(inserted.subpool, SubPool::Pending);
2388 }
2389
2390 #[test]
2391 fn test_insert_blob_not_enough_blob_fee() {
2392 let on_chain_balance = U256::MAX;
2393 let on_chain_nonce = 0;
2394 let mut f = MockTransactionFactory::default();
2395 let mut pool = AllTransactions {
2396 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2397 ..Default::default()
2398 };
2399 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2400 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2401 let valid_tx = f.validated(tx);
2402 let InsertOk { state, .. } =
2403 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2404 assert!(state.contains(TxState::NO_NONCE_GAPS));
2405 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2406
2407 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2408 }
2409
2410 #[test]
2411 fn test_valid_tx_with_decreasing_blob_fee() {
2412 let on_chain_balance = U256::MAX;
2413 let on_chain_nonce = 0;
2414 let mut f = MockTransactionFactory::default();
2415 let mut pool = AllTransactions {
2416 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2417 ..Default::default()
2418 };
2419 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2420
2421 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2422 let valid_tx = f.validated(tx.clone());
2423 let InsertOk { state, .. } =
2424 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2425 assert!(state.contains(TxState::NO_NONCE_GAPS));
2426 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2427
2428 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2429 pool.remove_transaction(&valid_tx.transaction_id);
2430
2431 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
2432 let InsertOk { state, .. } =
2433 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2434 assert!(state.contains(TxState::NO_NONCE_GAPS));
2435 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2436 }
2437
2438 #[test]
2439 fn test_demote_valid_tx_with_increasing_blob_fee() {
2440 let on_chain_balance = U256::MAX;
2441 let on_chain_nonce = 0;
2442 let mut f = MockTransactionFactory::default();
2443 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2444 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2445
2446 let mut block_info = pool.block_info();
2448 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2449 pool.set_block_info(block_info);
2450
2451 let validated = f.validated(tx.clone());
2452 let id = *validated.id();
2453 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2454
2455 assert!(pool.blob_pool.is_empty());
2457 assert_eq!(pool.pending_pool.len(), 1);
2458
2459 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2461 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2462 assert_eq!(internal_tx.subpool, SubPool::Pending);
2463
2464 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2466 pool.set_block_info(block_info);
2467
2468 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2470 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2471 assert_eq!(internal_tx.subpool, SubPool::Blob);
2472
2473 assert_eq!(pool.blob_pool.len(), 1);
2475 assert!(pool.pending_pool.is_empty());
2476 }
2477
2478 #[test]
2479 fn test_promote_valid_tx_with_decreasing_blob_fee() {
2480 let on_chain_balance = U256::MAX;
2481 let on_chain_nonce = 0;
2482 let mut f = MockTransactionFactory::default();
2483 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2484 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2485
2486 let mut block_info = pool.block_info();
2488 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2489 pool.set_block_info(block_info);
2490
2491 let validated = f.validated(tx.clone());
2492 let id = *validated.id();
2493 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2494
2495 assert!(pool.pending_pool.is_empty());
2497 assert_eq!(pool.blob_pool.len(), 1);
2498
2499 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2501 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2502 assert_eq!(internal_tx.subpool, SubPool::Blob);
2503
2504 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2506 pool.set_block_info(block_info);
2507
2508 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2510 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2511 assert_eq!(internal_tx.subpool, SubPool::Pending);
2512
2513 assert_eq!(pool.pending_pool.len(), 1);
2515 assert!(pool.blob_pool.is_empty());
2516 }
2517
2518 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
2520 struct PromotionTest {
2521 basefee: u64,
2523 blobfee: u128,
2525 subpool: SubPool,
2527 basefee_update: u64,
2529 blobfee_update: u128,
2531 new_subpool: SubPool,
2533 }
2534
2535 impl PromotionTest {
2536 const fn opposite(&self) -> Self {
2538 Self {
2539 basefee: self.basefee_update,
2540 blobfee: self.blobfee_update,
2541 subpool: self.new_subpool,
2542 blobfee_update: self.blobfee,
2543 basefee_update: self.basefee,
2544 new_subpool: self.subpool,
2545 }
2546 }
2547
2548 fn assert_subpool_lengths<T: TransactionOrdering>(
2549 &self,
2550 pool: &TxPool<T>,
2551 failure_message: String,
2552 check_subpool: SubPool,
2553 ) {
2554 match check_subpool {
2555 SubPool::Blob => {
2556 assert_eq!(pool.blob_pool.len(), 1, "{failure_message}");
2557 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2558 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2559 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2560 }
2561 SubPool::Pending => {
2562 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2563 assert_eq!(pool.pending_pool.len(), 1, "{failure_message}");
2564 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2565 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2566 }
2567 SubPool::BaseFee => {
2568 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2569 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2570 assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}");
2571 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2572 }
2573 SubPool::Queued => {
2574 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2575 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2576 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2577 assert_eq!(pool.queued_pool.len(), 1, "{failure_message}");
2578 }
2579 }
2580 }
2581
2582 fn assert_single_tx_starting_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2586 self.assert_subpool_lengths(
2587 pool,
2588 format!("pool length check failed at start of test: {self:?}"),
2589 self.subpool,
2590 );
2591 }
2592
2593 fn assert_single_tx_ending_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2597 self.assert_subpool_lengths(
2598 pool,
2599 format!("pool length check failed at end of test: {self:?}"),
2600 self.new_subpool,
2601 );
2602 }
2603 }
2604
2605 #[test]
2606 fn test_promote_blob_tx_with_both_pending_fee_updates() {
2607 let on_chain_balance = U256::MAX;
2610 let on_chain_nonce = 0;
2611 let mut f = MockTransactionFactory::default();
2612 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2613
2614 let max_fee_per_blob_gas = tx.max_fee_per_blob_gas().unwrap();
2615 let max_fee_per_gas = tx.max_fee_per_gas() as u64;
2616
2617 let mut expected_promotions = vec![
2619 PromotionTest {
2620 blobfee: max_fee_per_blob_gas + 1,
2621 basefee: max_fee_per_gas + 1,
2622 subpool: SubPool::Blob,
2623 blobfee_update: max_fee_per_blob_gas + 1,
2624 basefee_update: max_fee_per_gas + 1,
2625 new_subpool: SubPool::Blob,
2626 },
2627 PromotionTest {
2628 blobfee: max_fee_per_blob_gas + 1,
2629 basefee: max_fee_per_gas + 1,
2630 subpool: SubPool::Blob,
2631 blobfee_update: max_fee_per_blob_gas,
2632 basefee_update: max_fee_per_gas + 1,
2633 new_subpool: SubPool::Blob,
2634 },
2635 PromotionTest {
2636 blobfee: max_fee_per_blob_gas + 1,
2637 basefee: max_fee_per_gas + 1,
2638 subpool: SubPool::Blob,
2639 blobfee_update: max_fee_per_blob_gas + 1,
2640 basefee_update: max_fee_per_gas,
2641 new_subpool: SubPool::Blob,
2642 },
2643 PromotionTest {
2644 blobfee: max_fee_per_blob_gas + 1,
2645 basefee: max_fee_per_gas + 1,
2646 subpool: SubPool::Blob,
2647 blobfee_update: max_fee_per_blob_gas,
2648 basefee_update: max_fee_per_gas,
2649 new_subpool: SubPool::Pending,
2650 },
2651 PromotionTest {
2652 blobfee: max_fee_per_blob_gas,
2653 basefee: max_fee_per_gas + 1,
2654 subpool: SubPool::Blob,
2655 blobfee_update: max_fee_per_blob_gas,
2656 basefee_update: max_fee_per_gas,
2657 new_subpool: SubPool::Pending,
2658 },
2659 PromotionTest {
2660 blobfee: max_fee_per_blob_gas + 1,
2661 basefee: max_fee_per_gas,
2662 subpool: SubPool::Blob,
2663 blobfee_update: max_fee_per_blob_gas,
2664 basefee_update: max_fee_per_gas,
2665 new_subpool: SubPool::Pending,
2666 },
2667 PromotionTest {
2668 blobfee: max_fee_per_blob_gas,
2669 basefee: max_fee_per_gas,
2670 subpool: SubPool::Pending,
2671 blobfee_update: max_fee_per_blob_gas,
2672 basefee_update: max_fee_per_gas,
2673 new_subpool: SubPool::Pending,
2674 },
2675 ];
2676
2677 let reversed = expected_promotions.iter().map(|test| test.opposite()).collect::<Vec<_>>();
2679 expected_promotions.extend(reversed);
2680
2681 let expected_promotions = expected_promotions.into_iter().collect::<HashSet<_>>();
2683
2684 for promotion_test in &expected_promotions {
2685 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2686
2687 let mut block_info = pool.block_info();
2689
2690 block_info.pending_blob_fee = Some(promotion_test.blobfee);
2691 block_info.pending_basefee = promotion_test.basefee;
2692 pool.set_block_info(block_info);
2693
2694 let validated = f.validated(tx.clone());
2695 let id = *validated.id();
2696 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2697
2698 promotion_test.assert_single_tx_starting_subpool(&pool);
2700
2701 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2703 assert_eq!(
2704 internal_tx.subpool, promotion_test.subpool,
2705 "Subpools do not match at start of test: {promotion_test:?}"
2706 );
2707
2708 block_info.pending_basefee = promotion_test.basefee_update;
2710 block_info.pending_blob_fee = Some(promotion_test.blobfee_update);
2711 pool.set_block_info(block_info);
2712
2713 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2715 assert_eq!(
2716 internal_tx.subpool, promotion_test.new_subpool,
2717 "Subpools do not match at end of test: {promotion_test:?}"
2718 );
2719
2720 promotion_test.assert_single_tx_ending_subpool(&pool);
2722 }
2723 }
2724
2725 #[test]
2726 fn test_insert_pending() {
2727 let on_chain_balance = U256::MAX;
2728 let on_chain_nonce = 0;
2729 let mut f = MockTransactionFactory::default();
2730 let mut pool = AllTransactions::default();
2731 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2732 let valid_tx = f.validated(tx);
2733 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2734 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2735 assert!(updates.is_empty());
2736 assert!(replaced_tx.is_none());
2737 assert!(state.contains(TxState::NO_NONCE_GAPS));
2738 assert!(state.contains(TxState::ENOUGH_BALANCE));
2739 assert_eq!(move_to, SubPool::Pending);
2740
2741 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2742 assert_eq!(inserted.subpool, SubPool::Pending);
2743 }
2744
2745 #[test]
2746 fn test_simple_insert() {
2747 let on_chain_balance = U256::ZERO;
2748 let on_chain_nonce = 0;
2749 let mut f = MockTransactionFactory::default();
2750 let mut pool = AllTransactions::default();
2751 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2752 tx.set_priority_fee(100);
2753 tx.set_max_fee(100);
2754 let valid_tx = f.validated(tx.clone());
2755 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2756 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2757 assert!(updates.is_empty());
2758 assert!(replaced_tx.is_none());
2759 assert!(state.contains(TxState::NO_NONCE_GAPS));
2760 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2761 assert_eq!(move_to, SubPool::Queued);
2762
2763 assert_eq!(pool.len(), 1);
2764 assert!(pool.contains(valid_tx.hash()));
2765 let expected_state = TxState::ENOUGH_FEE_CAP_BLOCK | TxState::NO_NONCE_GAPS;
2766 let inserted = pool.get(valid_tx.id()).unwrap();
2767 assert!(inserted.state.intersects(expected_state));
2768
2769 let res = pool.insert_tx(valid_tx, on_chain_balance, on_chain_nonce);
2771 res.unwrap_err();
2772 assert_eq!(pool.len(), 1);
2773
2774 let valid_tx = f.validated(tx.next());
2775 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2776 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2777
2778 assert!(updates.is_empty());
2779 assert!(replaced_tx.is_none());
2780 assert!(state.contains(TxState::NO_NONCE_GAPS));
2781 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2782 assert_eq!(move_to, SubPool::Queued);
2783
2784 assert!(pool.contains(valid_tx.hash()));
2785 assert_eq!(pool.len(), 2);
2786 let inserted = pool.get(valid_tx.id()).unwrap();
2787 assert!(inserted.state.intersects(expected_state));
2788 }
2789
2790 #[test]
2791 fn test_on_canonical_state_change_no_double_processing() {
2794 let mut tx_factory = MockTransactionFactory::default();
2795 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2796
2797 let tx = MockTransaction::eip1559().with_gas_price(50).with_gas_limit(30_000);
2799 let sender = tx.sender();
2800
2801 let mut block_info = pool.block_info();
2803 block_info.pending_basefee = 100;
2804 pool.set_block_info(block_info);
2805
2806 let validated = tx_factory.validated(tx);
2807 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2808
2809 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2811
2812 assert_eq!(pool.basefee_pool.len(), 1);
2813 assert_eq!(pool.pending_pool.len(), 0);
2814
2815 block_info.pending_basefee = 40;
2819
2820 let mut changed_senders = FxHashMap::default();
2821 changed_senders.insert(
2822 sender_id,
2823 SenderInfo {
2824 state_nonce: 0,
2825 balance: U256::from(20_000_000), },
2827 );
2828
2829 let outcome = pool.on_canonical_state_change(
2830 block_info,
2831 vec![], changed_senders,
2833 PoolUpdateKind::Commit,
2834 );
2835
2836 assert_eq!(pool.pending_pool.len(), 1, "Transaction should be in pending pool");
2838 assert_eq!(pool.basefee_pool.len(), 0, "Transaction should not be in basefee pool");
2839 assert_eq!(outcome.promoted.len(), 1, "Should report exactly one promotion");
2840 }
2841
2842 #[test]
2843 fn test_canonical_state_change_with_basefee_update_regression() {
2846 let mut tx_factory = MockTransactionFactory::default();
2847 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2848
2849 let sender_balance = U256::from(100_000_000);
2851
2852 let tx1 =
2854 MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000).with_nonce(0);
2855 let sender1 = tx1.sender();
2856
2857 let tx2 =
2859 MockTransaction::eip1559().with_gas_price(55).with_gas_limit(21_000).with_nonce(0);
2860 let sender2 = tx2.sender();
2861
2862 let tx3 =
2864 MockTransaction::eip1559().with_gas_price(45).with_gas_limit(21_000).with_nonce(0);
2865 let sender3 = tx3.sender();
2866
2867 let mut block_info = pool.block_info();
2869 block_info.pending_basefee = 70;
2870 pool.set_block_info(block_info);
2871
2872 let validated1 = tx_factory.validated(tx1);
2874 let validated2 = tx_factory.validated(tx2);
2875 let validated3 = tx_factory.validated(tx3);
2876
2877 pool.add_transaction(validated1, sender_balance, 0, None).unwrap();
2878 pool.add_transaction(validated2, sender_balance, 0, None).unwrap();
2879 pool.add_transaction(validated3, sender_balance, 0, None).unwrap();
2880
2881 let sender1_id = tx_factory.ids.sender_id(&sender1).unwrap();
2882 let sender2_id = tx_factory.ids.sender_id(&sender2).unwrap();
2883 let sender3_id = tx_factory.ids.sender_id(&sender3).unwrap();
2884
2885 assert_eq!(pool.basefee_pool.len(), 3, "All txs should be in basefee pool");
2887 assert_eq!(pool.pending_pool.len(), 0, "No txs should be in pending pool");
2888
2889 block_info.pending_basefee = 50;
2892
2893 let mut changed_senders = FxHashMap::default();
2895 changed_senders.insert(
2896 sender1_id,
2897 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2898 );
2899 changed_senders.insert(
2900 sender2_id,
2901 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2902 );
2903 changed_senders.insert(
2904 sender3_id,
2905 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2906 );
2907
2908 let outcome = pool.on_canonical_state_change(
2909 block_info,
2910 vec![],
2911 changed_senders,
2912 PoolUpdateKind::Commit,
2913 );
2914
2915 assert_eq!(pool.pending_pool.len(), 2, "tx1 and tx2 should be promoted");
2917 assert_eq!(pool.basefee_pool.len(), 1, "tx3 should remain in basefee");
2918
2919 assert_eq!(
2922 outcome.promoted.len(),
2923 2,
2924 "Should report exactly 2 promotions, not double-counted"
2925 );
2926
2927 let promoted_prices: Vec<u128> =
2929 outcome.promoted.iter().map(|tx| tx.max_fee_per_gas()).collect();
2930 assert!(promoted_prices.contains(&60));
2931 assert!(promoted_prices.contains(&55));
2932 }
2933
2934 #[test]
2935 fn test_basefee_decrease_with_empty_senders() {
2936 let mut tx_factory = MockTransactionFactory::default();
2939 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2940
2941 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2943
2944 let mut block_info = pool.block_info();
2946 block_info.pending_basefee = 100;
2947 pool.set_block_info(block_info);
2948
2949 let validated = tx_factory.validated(tx);
2951 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2952
2953 assert_eq!(pool.basefee_pool.len(), 1);
2954 assert_eq!(pool.pending_pool.len(), 0);
2955
2956 block_info.pending_basefee = 50;
2958 let outcome = pool.on_canonical_state_change(
2959 block_info,
2960 vec![],
2961 FxHashMap::default(), PoolUpdateKind::Commit,
2963 );
2964
2965 assert_eq!(pool.pending_pool.len(), 1, "Fee decrease should promote tx");
2967 assert_eq!(pool.basefee_pool.len(), 0);
2968 assert_eq!(outcome.promoted.len(), 1, "Should report promotion from fee update");
2969 }
2970
2971 #[test]
2972 fn test_basefee_decrease_account_makes_unfundable() {
2973 let mut tx_factory = MockTransactionFactory::default();
2976 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2977
2978 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2979 let sender = tx.sender();
2980
2981 let mut block_info = pool.block_info();
2983 block_info.pending_basefee = 100;
2984 pool.set_block_info(block_info);
2985
2986 let validated = tx_factory.validated(tx);
2987 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2988 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2989
2990 assert_eq!(pool.basefee_pool.len(), 1);
2991
2992 block_info.pending_basefee = 50;
2994 let mut changed_senders = FxHashMap::default();
2995 changed_senders.insert(
2996 sender_id,
2997 SenderInfo {
2998 state_nonce: 0,
2999 balance: U256::from(100), },
3001 );
3002
3003 let outcome = pool.on_canonical_state_change(
3004 block_info,
3005 vec![],
3006 changed_senders,
3007 PoolUpdateKind::Commit,
3008 );
3009
3010 assert_eq!(pool.pending_pool.len(), 0, "Unfunded tx should not be in pending");
3012 assert_eq!(pool.basefee_pool.len(), 0, "Tx no longer in basefee pool");
3013 assert_eq!(pool.queued_pool.len(), 1, "Unfunded tx should be in queued pool");
3014
3015 let tx_count = pool.all_transactions.txs.len();
3017 assert_eq!(tx_count, 1, "Transaction should still be in pool (in queued)");
3018
3019 assert_eq!(outcome.promoted.len(), 0, "Should not report promotion");
3020 assert_eq!(outcome.discarded.len(), 0, "Queued tx is not reported as discarded");
3021 }
3022
3023 #[test]
3024 fn insert_already_imported() {
3025 let on_chain_balance = U256::ZERO;
3026 let on_chain_nonce = 0;
3027 let mut f = MockTransactionFactory::default();
3028 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3029 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3030 let tx = f.validated(tx);
3031 pool.add_transaction(tx.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3032 match pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap_err().kind {
3033 PoolErrorKind::AlreadyImported => {}
3034 _ => unreachable!(),
3035 }
3036 }
3037
3038 #[test]
3039 fn insert_replace() {
3040 let on_chain_balance = U256::ZERO;
3041 let on_chain_nonce = 0;
3042 let mut f = MockTransactionFactory::default();
3043 let mut pool = AllTransactions::default();
3044 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3045 let first = f.validated(tx.clone());
3046 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3047 let replacement = f.validated(tx.rng_hash().inc_price());
3048 let InsertOk { updates, replaced_tx, .. } =
3049 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
3050 assert!(updates.is_empty());
3051 let replaced = replaced_tx.unwrap();
3052 assert_eq!(replaced.0.hash(), first.hash());
3053
3054 assert!(!pool.contains(first.hash()));
3056 assert!(pool.contains(replacement.hash()));
3057 assert_eq!(pool.len(), 1);
3058 }
3059
3060 #[test]
3061 fn insert_replace_txpool() {
3062 let on_chain_balance = U256::ZERO;
3063 let on_chain_nonce = 0;
3064 let mut f = MockTransactionFactory::default();
3065 let mut pool = TxPool::mock();
3066
3067 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3068 let first = f.validated(tx.clone());
3069 let first_added =
3070 pool.add_transaction(first, on_chain_balance, on_chain_nonce, None).unwrap();
3071 let replacement = f.validated(tx.rng_hash().inc_price());
3072 let replacement_added = pool
3073 .add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce, None)
3074 .unwrap();
3075
3076 assert!(!pool.contains(first_added.hash()));
3078 assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));
3080
3081 assert!(pool.contains(replacement.hash()));
3082 let size = pool.size();
3083 assert_eq!(size.total, 1);
3084 size.assert_invariants();
3085 }
3086
3087 #[test]
3088 fn insert_replace_underpriced() {
3089 let on_chain_balance = U256::ZERO;
3090 let on_chain_nonce = 0;
3091 let mut f = MockTransactionFactory::default();
3092 let mut pool = AllTransactions::default();
3093 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3094 let first = f.validated(tx.clone());
3095 let _res = pool.insert_tx(first, on_chain_balance, on_chain_nonce);
3096 let mut replacement = f.validated(tx.rng_hash());
3097 replacement.transaction = replacement.transaction.decr_price();
3098 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3099 assert!(matches!(err, InsertErr::Underpriced { .. }));
3100 }
3101
3102 #[test]
3103 fn insert_replace_underpriced_not_enough_bump() {
3104 let on_chain_balance = U256::ZERO;
3105 let on_chain_nonce = 0;
3106 let mut f = MockTransactionFactory::default();
3107 let mut pool = AllTransactions::default();
3108 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3109 tx.set_priority_fee(100);
3110 tx.set_max_fee(100);
3111 let first = f.validated(tx.clone());
3112 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3113 let mut replacement = f.validated(tx.rng_hash().inc_price());
3114
3115 replacement.transaction.set_priority_fee(109);
3117 replacement.transaction.set_max_fee(109);
3118 let err =
3119 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3120 assert!(matches!(err, InsertErr::Underpriced { .. }));
3121 assert!(pool.contains(first.hash()));
3123 assert_eq!(pool.len(), 1);
3124
3125 replacement.transaction.set_priority_fee(110);
3127 replacement.transaction.set_max_fee(109);
3128 let err =
3129 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3130 assert!(matches!(err, InsertErr::Underpriced { .. }));
3131 assert!(pool.contains(first.hash()));
3132 assert_eq!(pool.len(), 1);
3133
3134 replacement.transaction.set_priority_fee(109);
3136 replacement.transaction.set_max_fee(110);
3137 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3138 assert!(matches!(err, InsertErr::Underpriced { .. }));
3139 assert!(pool.contains(first.hash()));
3140 assert_eq!(pool.len(), 1);
3141 }
3142
3143 #[test]
3144 fn insert_replace_underpriced_rounds_up_minimum_bump() {
3145 let on_chain_balance = U256::ZERO;
3146 let on_chain_nonce = 0;
3147 let mut f = MockTransactionFactory::default();
3148 let mut pool = AllTransactions { minimal_protocol_basefee: 0, ..Default::default() };
3149 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3150 tx.set_priority_fee(1);
3151 tx.set_max_fee(1);
3152
3153 let first = f.validated(tx.clone());
3154 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3155
3156 let mut replacement = f.validated(tx.rng_hash().inc_price());
3157 replacement.transaction.set_priority_fee(1);
3158 replacement.transaction.set_max_fee(2);
3159 let err =
3160 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3161 assert!(matches!(err, InsertErr::Underpriced { .. }));
3162 assert!(pool.contains(first.hash()));
3163 assert_eq!(pool.len(), 1);
3164
3165 replacement.transaction.set_priority_fee(2);
3166 replacement.transaction.set_max_fee(2);
3167 let replaced = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap();
3168 assert!(replaced.replaced_tx.is_some());
3169 assert_eq!(pool.len(), 1);
3170 }
3171
3172 #[test]
3173 fn insert_conflicting_type_normal_to_blob() {
3174 let on_chain_balance = U256::from(10_000);
3175 let on_chain_nonce = 0;
3176 let mut f = MockTransactionFactory::default();
3177 let mut pool = AllTransactions::default();
3178 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3179 let first = f.validated(tx.clone());
3180 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3181 let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3182 let blob = f.validated(tx);
3183 let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
3184 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3185 }
3186
3187 #[test]
3188 fn insert_conflicting_type_blob_to_normal() {
3189 let on_chain_balance = U256::from(10_000);
3190 let on_chain_nonce = 0;
3191 let mut f = MockTransactionFactory::default();
3192 let mut pool = AllTransactions::default();
3193 let tx = MockTransaction::eip4844().inc_price().inc_limit();
3194 let first = f.validated(tx.clone());
3195 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3196 let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3197 let tx = f.validated(tx);
3198 let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
3199 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3200 }
3201
3202 #[test]
3204 fn insert_previous() {
3205 let on_chain_balance = U256::ZERO;
3206 let on_chain_nonce = 0;
3207 let mut f = MockTransactionFactory::default();
3208 let mut pool = AllTransactions::default();
3209 let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
3210 let first = f.validated(tx.clone());
3211 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3212
3213 let first_in_pool = pool.get(first.id()).unwrap();
3214
3215 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3217
3218 let prev = f.validated(tx.prev());
3219 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3220 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3221
3222 assert!(updates.is_empty());
3224 assert!(replaced_tx.is_none());
3225 assert!(state.contains(TxState::NO_NONCE_GAPS));
3226 assert_eq!(move_to, SubPool::Queued);
3227
3228 let first_in_pool = pool.get(first.id()).unwrap();
3229 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3231 }
3232
3233 #[test]
3235 fn insert_with_updates() {
3236 let on_chain_balance = U256::from(10_000);
3237 let on_chain_nonce = 0;
3238 let mut f = MockTransactionFactory::default();
3239 let mut pool = AllTransactions::default();
3240 let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
3241 let first = f.validated(tx.clone());
3242 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3243
3244 let first_in_pool = pool.get(first.id()).unwrap();
3245 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3247 assert_eq!(SubPool::Queued, first_in_pool.subpool);
3248
3249 let prev = f.validated(tx.prev());
3250 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3251 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3252
3253 assert_eq!(updates.len(), 1);
3255 assert!(replaced_tx.is_none());
3256 assert!(state.contains(TxState::NO_NONCE_GAPS));
3257 assert_eq!(move_to, SubPool::Pending);
3258
3259 let first_in_pool = pool.get(first.id()).unwrap();
3260 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3262 assert_eq!(SubPool::Pending, first_in_pool.subpool);
3263 }
3264
3265 #[test]
3266 fn insert_previous_blocking() {
3267 let on_chain_balance = U256::from(1_000);
3268 let on_chain_nonce = 0;
3269 let mut f = MockTransactionFactory::default();
3270 let mut pool = AllTransactions::default();
3271 pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
3272 let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
3273 let first = f.validated(tx.clone());
3274
3275 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3276
3277 let first_in_pool = pool.get(first.id()).unwrap();
3278
3279 assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
3280 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3282
3283 let prev = f.validated(tx.prev());
3284 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3285 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3286
3287 assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3288 assert!(updates.is_empty());
3290 assert!(replaced_tx.is_none());
3291 assert!(state.contains(TxState::NO_NONCE_GAPS));
3292 assert_eq!(move_to, SubPool::BaseFee);
3293
3294 let first_in_pool = pool.get(first.id()).unwrap();
3295 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3297 }
3298
3299 #[test]
3300 fn rejects_spammer() {
3301 let on_chain_balance = U256::from(1_000);
3302 let on_chain_nonce = 0;
3303 let mut f = MockTransactionFactory::default();
3304 let mut pool = AllTransactions::default();
3305
3306 let mut tx = MockTransaction::eip1559();
3307 let unblocked_tx = tx.clone();
3308 for _ in 0..pool.max_account_slots {
3309 tx = tx.next();
3310 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
3311 }
3312
3313 assert_eq!(
3314 pool.max_account_slots,
3315 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3316 );
3317
3318 let err =
3319 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
3320 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
3321
3322 assert!(pool
3323 .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
3324 .is_ok());
3325 }
3326
3327 #[test]
3328 fn allow_local_spamming() {
3329 let on_chain_balance = U256::from(1_000);
3330 let on_chain_nonce = 0;
3331 let mut f = MockTransactionFactory::default();
3332 let mut pool = AllTransactions::default();
3333
3334 let mut tx = MockTransaction::eip1559();
3335 for _ in 0..pool.max_account_slots {
3336 tx = tx.next();
3337 pool.insert_tx(
3338 f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
3339 on_chain_balance,
3340 on_chain_nonce,
3341 )
3342 .unwrap();
3343 }
3344
3345 assert_eq!(
3346 pool.max_account_slots,
3347 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3348 );
3349
3350 pool.insert_tx(
3351 f.validated_with_origin(TransactionOrigin::Local, tx.next()),
3352 on_chain_balance,
3353 on_chain_nonce,
3354 )
3355 .unwrap();
3356 }
3357
3358 #[test]
3359 fn reject_tx_over_gas_limit() {
3360 let on_chain_balance = U256::from(1_000);
3361 let on_chain_nonce = 0;
3362 let mut f = MockTransactionFactory::default();
3363 let mut pool = AllTransactions::default();
3364
3365 let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
3366
3367 assert!(matches!(
3368 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
3369 Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
3370 ));
3371 }
3372
3373 #[test]
3374 fn test_tx_equal_gas_limit() {
3375 let on_chain_balance = U256::from(1_000);
3376 let on_chain_nonce = 0;
3377 let mut f = MockTransactionFactory::default();
3378 let mut pool = AllTransactions::default();
3379
3380 let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
3381
3382 let InsertOk { state, .. } =
3383 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
3384 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
3385 }
3386
3387 #[test]
3388 fn update_basefee_subpools() {
3389 let mut f = MockTransactionFactory::default();
3390 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3391
3392 let tx = MockTransaction::eip1559().inc_price_by(10);
3393 let validated = f.validated(tx.clone());
3394 let id = *validated.id();
3395 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3396
3397 assert_eq!(pool.pending_pool.len(), 1);
3398
3399 pool.update_basefee((tx.max_fee_per_gas() + 1) as u64, |_| {});
3400
3401 assert!(pool.pending_pool.is_empty());
3402 assert_eq!(pool.basefee_pool.len(), 1);
3403
3404 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3405 }
3406
3407 #[test]
3408 fn update_basefee_subpools_setting_block_info() {
3409 let mut f = MockTransactionFactory::default();
3410 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3411
3412 let tx = MockTransaction::eip1559().inc_price_by(10);
3413 let validated = f.validated(tx.clone());
3414 let id = *validated.id();
3415 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3416
3417 assert_eq!(pool.pending_pool.len(), 1);
3418
3419 let mut block_info = pool.block_info();
3421 block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
3422 pool.set_block_info(block_info);
3423
3424 assert!(pool.pending_pool.is_empty());
3425 assert_eq!(pool.basefee_pool.len(), 1);
3426
3427 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3428 }
3429
3430 #[test]
3431 fn basefee_decrease_promotes_affordable_and_keeps_unaffordable() {
3432 use alloy_primitives::address;
3433 let mut f = MockTransactionFactory::default();
3434 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3435
3436 let sender_a = address!("0x000000000000000000000000000000000000000a");
3439 let sender_b = address!("0x000000000000000000000000000000000000000b");
3440 let sender_c = address!("0x000000000000000000000000000000000000000c");
3441
3442 let tx1 = MockTransaction::eip1559()
3443 .set_sender(sender_a)
3444 .set_nonce(0)
3445 .set_max_fee(500)
3446 .inc_limit();
3447 let tx2 = MockTransaction::eip1559()
3448 .set_sender(sender_b)
3449 .set_nonce(0)
3450 .set_max_fee(600)
3451 .inc_limit();
3452 let tx3 = MockTransaction::eip1559()
3453 .set_sender(sender_c)
3454 .set_nonce(0)
3455 .set_max_fee(400)
3456 .inc_limit();
3457
3458 let mut block_info = pool.block_info();
3460 block_info.pending_basefee = 700;
3461 pool.set_block_info(block_info);
3462
3463 let validated1 = f.validated(tx1);
3464 let validated2 = f.validated(tx2);
3465 let validated3 = f.validated(tx3);
3466 let id1 = *validated1.id();
3467 let id2 = *validated2.id();
3468 let id3 = *validated3.id();
3469
3470 pool.add_transaction(validated1, U256::from(10_000), 0, None).unwrap();
3474 pool.add_transaction(validated2, U256::from(10_000), 0, None).unwrap();
3475 pool.add_transaction(validated3, U256::from(10_000), 0, None).unwrap();
3476
3477 println!("Basefee pool len: {}", pool.basefee_pool.len());
3479 println!("Pending pool len: {}", pool.pending_pool.len());
3480 println!("tx1 subpool: {:?}", pool.all_transactions.txs.get(&id1).unwrap().subpool);
3481 println!("tx2 subpool: {:?}", pool.all_transactions.txs.get(&id2).unwrap().subpool);
3482 println!("tx3 subpool: {:?}", pool.all_transactions.txs.get(&id3).unwrap().subpool);
3483
3484 assert_eq!(pool.basefee_pool.len(), 3);
3486 assert_eq!(pool.pending_pool.len(), 0);
3487 assert_eq!(pool.all_transactions.txs.get(&id1).unwrap().subpool, SubPool::BaseFee);
3488 assert_eq!(pool.all_transactions.txs.get(&id2).unwrap().subpool, SubPool::BaseFee);
3489 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3490
3491 let mut block_info = pool.block_info();
3493 block_info.pending_basefee = 450; pool.set_block_info(block_info);
3495
3496 assert_eq!(pool.basefee_pool.len(), 1);
3501 assert_eq!(pool.pending_pool.len(), 2);
3502
3503 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3505
3506 let tx1_meta = pool.all_transactions.txs.get(&id1).unwrap();
3508 let tx2_meta = pool.all_transactions.txs.get(&id2).unwrap();
3509 assert_eq!(tx1_meta.subpool, SubPool::Pending);
3510 assert_eq!(tx2_meta.subpool, SubPool::Pending);
3511 assert!(tx1_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3512 assert!(tx2_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3513
3514 let best: Vec<_> = pool.best_transactions().take(3).collect();
3516 assert_eq!(best.len(), 2); assert!(best.iter().any(|tx| tx.id() == &id1));
3518 assert!(best.iter().any(|tx| tx.id() == &id2));
3519 }
3520
3521 #[test]
3522 fn apply_fee_updates_records_promotions_after_basefee_drop() {
3523 let mut f = MockTransactionFactory::default();
3524 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3525
3526 let tx = MockTransaction::eip1559()
3527 .with_gas_limit(21_000)
3528 .with_max_fee(500)
3529 .with_priority_fee(1);
3530 let validated = f.validated(tx);
3531 let id = *validated.id();
3532 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3533
3534 assert_eq!(pool.pending_pool.len(), 1);
3535
3536 pool.update_basefee(600, |_| {});
3538 assert!(pool.pending_pool.is_empty());
3539 assert_eq!(pool.basefee_pool.len(), 1);
3540
3541 let prev_base_fee = 600;
3542 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3543
3544 pool.all_transactions.pending_fees.base_fee = 400;
3546
3547 let mut outcome = UpdateOutcome::default();
3548 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3549
3550 assert_eq!(pool.pending_pool.len(), 1);
3551 assert!(pool.basefee_pool.is_empty());
3552 assert_eq!(outcome.promoted.len(), 1);
3553 assert_eq!(outcome.promoted[0].id(), &id);
3554 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3555 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3556
3557 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3558 assert_eq!(tx_meta.subpool, SubPool::Pending);
3559 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3560 }
3561
3562 #[test]
3563 fn apply_fee_updates_records_promotions_after_blob_fee_drop() {
3564 let mut f = MockTransactionFactory::default();
3565 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3566
3567 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3568
3569 let tx = MockTransaction::eip4844().with_blob_fee(initial_blob_fee + 100);
3570 let validated = f.validated(tx.clone());
3571 let id = *validated.id();
3572 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3573
3574 assert_eq!(pool.pending_pool.len(), 1);
3575
3576 let increased_blob_fee = tx.max_fee_per_blob_gas().unwrap() + 200;
3578 pool.update_blob_fee(increased_blob_fee, Ordering::Equal, |_| {});
3579 assert!(pool.pending_pool.is_empty());
3580 assert_eq!(pool.blob_pool.len(), 1);
3581
3582 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3583 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3584
3585 pool.all_transactions.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
3587
3588 let mut outcome = UpdateOutcome::default();
3589 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3590
3591 assert_eq!(pool.pending_pool.len(), 1);
3592 assert!(pool.blob_pool.is_empty());
3593 assert_eq!(outcome.promoted.len(), 1);
3594 assert_eq!(outcome.promoted[0].id(), &id);
3595 assert_eq!(pool.all_transactions.pending_fees.base_fee, prev_base_fee);
3596 assert_eq!(pool.all_transactions.pending_fees.blob_fee, tx.max_fee_per_blob_gas().unwrap());
3597
3598 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3599 assert_eq!(tx_meta.subpool, SubPool::Pending);
3600 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3601 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3602 }
3603
3604 #[test]
3605 fn apply_fee_updates_promotes_blob_after_basefee_drop() {
3606 let mut f = MockTransactionFactory::default();
3607 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3608
3609 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3610
3611 let tx = MockTransaction::eip4844()
3612 .with_max_fee(500)
3613 .with_priority_fee(1)
3614 .with_blob_fee(initial_blob_fee + 100);
3615 let validated = f.validated(tx);
3616 let id = *validated.id();
3617 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3618
3619 assert_eq!(pool.pending_pool.len(), 1);
3620
3621 let high_base_fee = 600;
3623 pool.update_basefee(high_base_fee, |_| {});
3624 assert!(pool.pending_pool.is_empty());
3625 assert_eq!(pool.blob_pool.len(), 1);
3626
3627 let prev_base_fee = high_base_fee;
3628 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3629
3630 pool.all_transactions.pending_fees.base_fee = 400;
3632
3633 let mut outcome = UpdateOutcome::default();
3634 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3635
3636 assert_eq!(pool.pending_pool.len(), 1);
3637 assert!(pool.blob_pool.is_empty());
3638 assert_eq!(outcome.promoted.len(), 1);
3639 assert_eq!(outcome.promoted[0].id(), &id);
3640 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3641 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3642
3643 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3644 assert_eq!(tx_meta.subpool, SubPool::Pending);
3645 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3646 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3647 }
3648
3649 #[test]
3650 fn apply_fee_updates_demotes_after_basefee_rise() {
3651 let mut f = MockTransactionFactory::default();
3652 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3653
3654 let tx = MockTransaction::eip1559()
3655 .with_gas_limit(21_000)
3656 .with_max_fee(400)
3657 .with_priority_fee(1);
3658 let validated = f.validated(tx);
3659 let id = *validated.id();
3660 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3661
3662 assert_eq!(pool.pending_pool.len(), 1);
3663
3664 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3665 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3666
3667 let new_base_fee = prev_base_fee + 1_000;
3669 pool.all_transactions.pending_fees.base_fee = new_base_fee;
3670
3671 let mut outcome = UpdateOutcome::default();
3672 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3673
3674 assert!(pool.pending_pool.is_empty());
3675 assert_eq!(pool.basefee_pool.len(), 1);
3676 assert!(outcome.promoted.is_empty());
3677 assert_eq!(pool.all_transactions.pending_fees.base_fee, new_base_fee);
3678 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3679
3680 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3681 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
3682 assert!(!tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3683 }
3684
3685 #[test]
3686 fn get_highest_transaction_by_sender_and_nonce() {
3687 let mut f = MockTransactionFactory::default();
3689 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3690
3691 let tx = MockTransaction::eip1559();
3693 pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0, None).unwrap();
3694
3695 let tx1 = tx.inc_price().next();
3697
3698 let tx1_validated = f.validated(tx1.clone());
3700 pool.add_transaction(tx1_validated, U256::from(1_000), 0, None).unwrap();
3701
3702 assert_eq!(
3704 pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
3705 Some(1)
3706 );
3707
3708 let highest_tx = pool
3710 .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
3711 .expect("Failed to retrieve highest transaction");
3712
3713 assert_eq!(highest_tx.as_ref().transaction, tx1);
3715 }
3716
3717 #[test]
3718 fn get_highest_consecutive_transaction_by_sender() {
3719 let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
3721 let mut f = MockTransactionFactory::default();
3722
3723 let sender = Address::random();
3725 let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
3726 for nonce in txs {
3727 let mut mock_tx = MockTransaction::eip1559();
3728 mock_tx.set_sender(sender);
3729 mock_tx.set_nonce(nonce);
3730
3731 let validated_tx = f.validated(mock_tx);
3732 pool.add_transaction(validated_tx, U256::from(1000), 0, None).unwrap();
3733 }
3734
3735 let sender_id = f.ids.sender_id(&sender).unwrap();
3737 let next_tx =
3738 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
3739 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
3740
3741 let next_tx =
3742 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
3743 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
3744
3745 let next_tx =
3746 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3747 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
3748
3749 let mut info = SenderInfo::default();
3751 info.update(8, U256::ZERO);
3752 pool.all_transactions.sender_info.insert(sender_id, info);
3753 let next_tx =
3754 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3755 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
3756 }
3757
3758 #[test]
3759 fn discard_nonce_too_low() {
3760 let mut f = MockTransactionFactory::default();
3761 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3762
3763 let tx = MockTransaction::eip1559().inc_price_by(10);
3764 let validated = f.validated(tx.clone());
3765 let id = *validated.id();
3766 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3767
3768 let next = tx.next();
3769 let validated = f.validated(next.clone());
3770 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3771
3772 assert_eq!(pool.pending_pool.len(), 2);
3773
3774 let mut changed_senders = HashMap::default();
3775 changed_senders.insert(
3776 id.sender,
3777 SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
3778 );
3779 let outcome = pool.update_accounts(changed_senders);
3780 assert_eq!(outcome.discarded.len(), 1);
3781 assert_eq!(pool.pending_pool.len(), 1);
3782 }
3783
3784 #[test]
3785 fn discard_with_large_blob_txs() {
3786 reth_tracing::init_test_tracing();
3788
3789 let mut f = MockTransactionFactory::default();
3791 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3792 let default_limits = pool.config.blob_limit;
3793
3794 let a_sender = address!("0x000000000000000000000000000000000000000a");
3797
3798 let mut block_info = pool.block_info();
3800 block_info.pending_blob_fee = Some(100);
3801 block_info.pending_basefee = 100;
3802
3803 pool.set_block_info(block_info);
3805
3806 let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
3808 .into_iter()
3809 .map(|mut tx| {
3810 tx.set_size(default_limits.max_size / 2 + 1);
3811 tx.set_max_fee((block_info.pending_basefee - 1).into());
3812 tx
3813 })
3814 .collect::<Vec<_>>();
3815
3816 for tx in a_txs {
3818 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3819 }
3820
3821 let removed = pool.discard_worst();
3823 assert_eq!(removed.len(), 1);
3824 }
3825
3826 #[test]
3827 fn discard_with_parked_large_txs() {
3828 reth_tracing::init_test_tracing();
3830
3831 let mut f = MockTransactionFactory::default();
3833 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3834 let default_limits = pool.config.queued_limit;
3835
3836 let a_sender = address!("0x000000000000000000000000000000000000000a");
3839
3840 let pool_base_fee = 100;
3842 pool.update_basefee(pool_base_fee, |_| {});
3843
3844 let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
3846 .into_iter()
3847 .map(|mut tx| {
3848 tx.set_size(default_limits.max_size / 2 + 1);
3849 tx.set_max_fee((pool_base_fee - 1).into());
3850 tx
3851 })
3852 .collect::<Vec<_>>();
3853
3854 for tx in a_txs {
3856 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3857 }
3858
3859 let removed = pool.discard_worst();
3861 assert_eq!(removed.len(), 1);
3862 }
3863
3864 #[test]
3865 fn discard_at_capacity() {
3866 let mut f = MockTransactionFactory::default();
3867 let queued_limit = SubPoolLimit::new(1000, usize::MAX);
3868 let mut pool =
3869 TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
3870
3871 for _ in 0..queued_limit.max_txs {
3873 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3874 let validated = f.validated(tx.clone());
3875 let _id = *validated.id();
3876 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3877 }
3878
3879 let size = pool.size();
3880 assert_eq!(size.queued, queued_limit.max_txs);
3881
3882 for _ in 0..queued_limit.max_txs {
3883 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3884 let validated = f.validated(tx.clone());
3885 let _id = *validated.id();
3886 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3887
3888 pool.discard_worst();
3889 pool.assert_invariants();
3890 assert!(pool.size().queued <= queued_limit.max_txs);
3891 }
3892 }
3893
3894 #[test]
3895 fn discard_blobs_at_capacity() {
3896 let mut f = MockTransactionFactory::default();
3897 let blob_limit = SubPoolLimit::new(1000, usize::MAX);
3898 let mut pool =
3899 TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
3900 pool.all_transactions.pending_fees.blob_fee = 10000;
3901 for _ in 0..blob_limit.max_txs {
3903 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3904 let validated = f.validated(tx.clone());
3905 let _id = *validated.id();
3906 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3907 }
3908
3909 let size = pool.size();
3910 assert_eq!(size.blob, blob_limit.max_txs);
3911
3912 for _ in 0..blob_limit.max_txs {
3913 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3914 let validated = f.validated(tx.clone());
3915 let _id = *validated.id();
3916 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3917
3918 pool.discard_worst();
3919 pool.assert_invariants();
3920 assert!(pool.size().blob <= blob_limit.max_txs);
3921 }
3922 }
3923
3924 #[test]
3925 fn account_updates_sender_balance() {
3926 let mut on_chain_balance = U256::from(100);
3927 let on_chain_nonce = 0;
3928 let mut f = MockTransactionFactory::default();
3929 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3930
3931 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3932 let tx_1 = tx_0.next();
3933 let tx_2 = tx_1.next();
3934
3935 let v0 = f.validated(tx_0);
3937 let v1 = f.validated(tx_1);
3938 let v2 = f.validated(tx_2);
3939
3940 let _res =
3941 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3942 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3943 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3944
3945 assert_eq!(1, pool.pending_transactions().len());
3947 assert_eq!(2, pool.queued_transactions().len());
3948
3949 let mut updated_accounts = HashMap::default();
3951 on_chain_balance = U256::from(300);
3952 updated_accounts.insert(
3953 v0.sender_id(),
3954 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3955 );
3956 pool.update_accounts(updated_accounts.clone());
3957
3958 assert_eq!(3, pool.pending_transactions().len());
3959 assert!(pool.queued_transactions().is_empty());
3960
3961 updated_accounts.entry(v0.sender_id()).and_modify(|v| v.balance = U256::from(1));
3963 pool.update_accounts(updated_accounts);
3964
3965 assert!(pool.pending_transactions().is_empty());
3966 assert_eq!(3, pool.queued_transactions().len());
3967 }
3968
3969 #[test]
3970 fn account_updates_nonce_gap() {
3971 let on_chain_balance = U256::from(10_000);
3972 let mut on_chain_nonce = 0;
3973 let mut f = MockTransactionFactory::default();
3974 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3975
3976 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3977 let tx_1 = tx_0.next();
3978 let tx_2 = tx_1.next();
3979
3980 let v0 = f.validated(tx_0);
3982 let v1 = f.validated(tx_1);
3983 let v2 = f.validated(tx_2);
3984
3985 let _res =
3987 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3988 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3989
3990 assert!(pool.queued_transactions().is_empty());
3991 assert_eq!(2, pool.pending_transactions().len());
3992
3993 pool.remove_transaction_by_hash(v0.hash());
3995
3996 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3998
3999 assert_eq!(2, pool.queued_transactions().len());
4001 assert!(pool.pending_transactions().is_empty());
4002
4003 let mut updated_accounts = HashMap::default();
4005 on_chain_nonce += 1;
4006 updated_accounts.insert(
4007 v0.sender_id(),
4008 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4009 );
4010 pool.update_accounts(updated_accounts);
4011
4012 assert!(pool.queued_transactions().is_empty());
4014 assert_eq!(2, pool.pending_transactions().len());
4015 }
4016 #[test]
4017 fn test_transaction_removal() {
4018 let on_chain_balance = U256::from(10_000);
4019 let on_chain_nonce = 0;
4020 let mut f = MockTransactionFactory::default();
4021 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4022
4023 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4024 let tx_1 = tx_0.next();
4025
4026 let v0 = f.validated(tx_0);
4028 let v1 = f.validated(tx_1);
4029
4030 let _res =
4032 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4033 let _res =
4034 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4035
4036 assert_eq!(0, pool.queued_transactions().len());
4037 assert_eq!(2, pool.pending_transactions().len());
4038
4039 pool.remove_transaction(v0.id());
4041 let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
4043 assert_eq!(vec![v1.nonce()], pool_txs);
4044 }
4045 #[test]
4046 fn test_remove_transactions() {
4047 let on_chain_balance = U256::from(10_000);
4048 let on_chain_nonce = 0;
4049 let mut f = MockTransactionFactory::default();
4050 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4051
4052 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4053 let tx_1 = tx_0.next();
4054 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4055 let tx_3 = tx_2.next();
4056
4057 let v0 = f.validated(tx_0);
4059 let v1 = f.validated(tx_1);
4060 let v2 = f.validated(tx_2);
4061 let v3 = f.validated(tx_3);
4062
4063 let _res =
4065 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4066 let _res =
4067 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4068 let _res =
4069 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4070 let _res =
4071 pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4072
4073 assert_eq!(0, pool.queued_transactions().len());
4074 assert_eq!(4, pool.pending_transactions().len());
4075
4076 pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
4077
4078 assert_eq!(2, pool.queued_transactions().len());
4079 assert!(pool.pending_transactions().is_empty());
4080 assert!(pool.contains(v1.hash()));
4081 assert!(pool.contains(v3.hash()));
4082 }
4083
4084 #[test]
4085 fn test_remove_transactions_middle_pending_hash() {
4086 let on_chain_balance = U256::from(10_000);
4087 let on_chain_nonce = 0;
4088 let mut f = MockTransactionFactory::default();
4089 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4090
4091 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4092 let tx_1 = tx_0.next();
4093 let tx_2 = tx_1.next();
4094 let tx_3 = tx_2.next();
4095
4096 let v0 = f.validated(tx_0);
4098 let v1 = f.validated(tx_1);
4099 let v2 = f.validated(tx_2);
4100 let v3 = f.validated(tx_3);
4101
4102 let _res = pool.add_transaction(v0, on_chain_balance, on_chain_nonce, None).unwrap();
4104 let _res =
4105 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4106 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4107 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4108
4109 assert_eq!(0, pool.queued_transactions().len());
4110 assert_eq!(4, pool.pending_transactions().len());
4111
4112 let mut removed_txs = pool.remove_transactions(vec![*v1.hash()]);
4113 assert_eq!(1, removed_txs.len());
4114
4115 assert_eq!(2, pool.queued_transactions().len());
4116 assert_eq!(1, pool.pending_transactions().len());
4117
4118 let removed_tx = removed_txs.pop().unwrap();
4120 let v1 = f.validated(removed_tx.transaction.clone());
4121 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4122 assert_eq!(0, pool.queued_transactions().len());
4123 assert_eq!(4, pool.pending_transactions().len());
4124 }
4125
4126 #[test]
4127 fn test_remove_transactions_and_descendants() {
4128 let on_chain_balance = U256::from(10_000);
4129 let on_chain_nonce = 0;
4130 let mut f = MockTransactionFactory::default();
4131 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4132
4133 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4134 let tx_1 = tx_0.next();
4135 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4136 let tx_3 = tx_2.next();
4137 let tx_4 = tx_3.next();
4138
4139 let v0 = f.validated(tx_0);
4141 let v1 = f.validated(tx_1);
4142 let v2 = f.validated(tx_2);
4143 let v3 = f.validated(tx_3);
4144 let v4 = f.validated(tx_4);
4145
4146 let _res =
4148 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4149 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4150 let _res =
4151 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4152 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4153 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4154
4155 assert_eq!(0, pool.queued_transactions().len());
4156 assert_eq!(5, pool.pending_transactions().len());
4157
4158 pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
4159
4160 assert_eq!(0, pool.queued_transactions().len());
4161 assert_eq!(0, pool.pending_transactions().len());
4162 }
4163 #[test]
4164 fn test_remove_descendants() {
4165 let on_chain_balance = U256::from(10_000);
4166 let on_chain_nonce = 0;
4167 let mut f = MockTransactionFactory::default();
4168 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4169
4170 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4171 let tx_1 = tx_0.next();
4172 let tx_2 = tx_1.next();
4173 let tx_3 = tx_2.next();
4174
4175 let v0 = f.validated(tx_0);
4177 let v1 = f.validated(tx_1);
4178 let v2 = f.validated(tx_2);
4179 let v3 = f.validated(tx_3);
4180
4181 let _res =
4183 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4184 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4185 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4186 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4187
4188 assert_eq!(0, pool.queued_transactions().len());
4189 assert_eq!(4, pool.pending_transactions().len());
4190
4191 let mut removed = Vec::new();
4192 pool.remove_transaction(v0.id());
4193 pool.remove_descendants(v0.id(), &mut removed);
4194
4195 assert_eq!(0, pool.queued_transactions().len());
4196 assert_eq!(0, pool.pending_transactions().len());
4197 assert_eq!(3, removed.len());
4198 }
4199 #[test]
4200 fn test_remove_transactions_by_sender() {
4201 let on_chain_balance = U256::from(10_000);
4202 let on_chain_nonce = 0;
4203 let mut f = MockTransactionFactory::default();
4204 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4205
4206 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4207 let tx_1 = tx_0.next();
4208 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4209 let tx_3 = tx_2.next();
4210 let tx_4 = tx_3.next();
4211
4212 let v0 = f.validated(tx_0);
4214 let v1 = f.validated(tx_1);
4215 let v2 = f.validated(tx_2);
4216 let v3 = f.validated(tx_3);
4217 let v4 = f.validated(tx_4);
4218
4219 let _res =
4221 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4222 let _res =
4223 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4224 let _res =
4225 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4226 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4227 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4228
4229 assert_eq!(0, pool.queued_transactions().len());
4230 assert_eq!(5, pool.pending_transactions().len());
4231
4232 pool.remove_transactions_by_sender(v2.sender_id());
4233
4234 assert_eq!(0, pool.queued_transactions().len());
4235 assert_eq!(2, pool.pending_transactions().len());
4236 assert!(pool.contains(v0.hash()));
4237 assert!(pool.contains(v1.hash()));
4238 }
4239 #[test]
4240 fn wrong_best_order_of_transactions() {
4241 let on_chain_balance = U256::from(10_000);
4242 let mut on_chain_nonce = 0;
4243 let mut f = MockTransactionFactory::default();
4244 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4245
4246 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4247 let tx_1 = tx_0.next();
4248 let tx_2 = tx_1.next();
4249 let tx_3 = tx_2.next();
4250
4251 let v0 = f.validated(tx_0);
4253 let v1 = f.validated(tx_1);
4254 let v2 = f.validated(tx_2);
4255 let v3 = f.validated(tx_3);
4256
4257 let _res =
4259 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4260 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4261
4262 assert_eq!(0, pool.queued_transactions().len());
4263 assert_eq!(2, pool.pending_transactions().len());
4264
4265 pool.remove_transaction(v0.id());
4267
4268 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4270
4271 assert_eq!(1, pool.queued_transactions().len());
4273 assert_eq!(1, pool.pending_transactions().len());
4274
4275 let mut updated_accounts = HashMap::default();
4277 on_chain_nonce += 1;
4278 updated_accounts.insert(
4279 v0.sender_id(),
4280 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4281 );
4282 pool.update_accounts(updated_accounts);
4283
4284 assert_eq!(0, pool.queued_transactions().len());
4287 assert_eq!(2, pool.pending_transactions().len());
4288
4289 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4291 assert_eq!(0, pool.queued_transactions().len());
4292 assert_eq!(3, pool.pending_transactions().len());
4293
4294 assert_eq!(
4297 pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
4298 vec![1, 2, 3]
4299 );
4300 }
4301
4302 #[test]
4303 fn test_best_with_attributes() {
4304 let on_chain_balance = U256::MAX;
4305 let on_chain_nonce = 0;
4306 let mut f = MockTransactionFactory::default();
4307 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4308
4309 let base_fee: u128 = 100;
4310 let blob_fee: u128 = 100;
4311
4312 let mut block_info = pool.block_info();
4314 block_info.pending_basefee = base_fee as u64;
4315 block_info.pending_blob_fee = Some(blob_fee);
4316 pool.set_block_info(block_info);
4317
4318 let tx1 = MockTransaction::eip4844()
4320 .with_sender(Address::with_last_byte(1))
4321 .with_max_fee(base_fee + 10)
4322 .with_blob_fee(blob_fee + 10);
4323 let tx2 = MockTransaction::eip4844()
4324 .with_sender(Address::with_last_byte(2))
4325 .with_max_fee(base_fee + 10)
4326 .with_blob_fee(blob_fee);
4327 let tx3 = MockTransaction::eip4844()
4328 .with_sender(Address::with_last_byte(3))
4329 .with_max_fee(base_fee)
4330 .with_blob_fee(blob_fee + 10);
4331 let tx4 = MockTransaction::eip4844()
4332 .with_sender(Address::with_last_byte(4))
4333 .with_max_fee(base_fee)
4334 .with_blob_fee(blob_fee);
4335 let tx5 = MockTransaction::eip4844()
4336 .with_sender(Address::with_last_byte(5))
4337 .with_max_fee(base_fee)
4338 .with_blob_fee(blob_fee - 10);
4339 let tx6 = MockTransaction::eip4844()
4340 .with_sender(Address::with_last_byte(6))
4341 .with_max_fee(base_fee - 10)
4342 .with_blob_fee(blob_fee);
4343 let tx7 = MockTransaction::eip4844()
4344 .with_sender(Address::with_last_byte(7))
4345 .with_max_fee(base_fee - 10)
4346 .with_blob_fee(blob_fee - 10);
4347
4348 for tx in vec![
4349 tx1.clone(),
4350 tx2.clone(),
4351 tx3.clone(),
4352 tx4.clone(),
4353 tx5.clone(),
4354 tx6.clone(),
4355 tx7.clone(),
4356 ] {
4357 pool.add_transaction(f.validated(tx.clone()), on_chain_balance, on_chain_nonce, None)
4358 .unwrap();
4359 }
4360
4361 let base_fee = base_fee as u64;
4362 let blob_fee = blob_fee as u64;
4363
4364 let cases = vec![
4365 (BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee + 5)), vec![tx1.clone()]),
4367 (
4369 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee)),
4370 vec![tx1.clone(), tx2.clone()],
4371 ),
4372 (
4374 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee - 5)),
4375 vec![tx1.clone(), tx2.clone()],
4376 ),
4377 (
4379 BestTransactionsAttributes::new(base_fee, Some(blob_fee + 5)),
4380 vec![tx1.clone(), tx3.clone()],
4381 ),
4382 (
4384 BestTransactionsAttributes::new(base_fee, Some(blob_fee)),
4385 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
4386 ),
4387 (
4389 BestTransactionsAttributes::new(base_fee, Some(blob_fee - 10)),
4390 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx5.clone()],
4391 ),
4392 (
4394 BestTransactionsAttributes::new(base_fee - 5, Some(blob_fee + 5)),
4395 vec![tx1.clone(), tx3.clone()],
4396 ),
4397 (
4399 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee)),
4400 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx6.clone()],
4401 ),
4402 (
4404 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee - 10)),
4405 vec![tx1, tx2, tx5, tx3, tx4, tx6, tx7],
4406 ),
4407 ];
4408
4409 for (idx, (attribute, expected)) in cases.into_iter().enumerate() {
4410 let mut best = pool.best_transactions_with_attributes(attribute);
4411
4412 for (tx_idx, expected_tx) in expected.into_iter().enumerate() {
4413 let tx = best.next().expect("Transaction should be returned");
4414 assert_eq!(
4415 tx.transaction,
4416 expected_tx,
4417 "Failed tx {} in case {}",
4418 tx_idx + 1,
4419 idx + 1
4420 );
4421 }
4422
4423 assert!(best.next().is_none());
4425 }
4426 }
4427
4428 #[test]
4429 fn test_pending_ordering() {
4430 let mut f = MockTransactionFactory::default();
4431 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4432
4433 let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
4434 let tx_1 = tx_0.next();
4435
4436 let v0 = f.validated(tx_0);
4437 let v1 = f.validated(tx_1);
4438
4439 pool.add_transaction(v0.clone(), U256::MAX, 0, None).unwrap();
4441 assert_eq!(1, pool.queued_transactions().len());
4442
4443 pool.add_transaction(v1, U256::MAX, 1, None).unwrap();
4445
4446 assert_eq!(2, pool.pending_transactions().len());
4447 assert_eq!(0, pool.queued_transactions().len());
4448
4449 assert_eq!(
4450 pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
4451 v0.nonce()
4452 );
4453 }
4454
4455 #[test]
4457 fn one_sender_one_independent_transaction() {
4458 let mut on_chain_balance = U256::from(4_999); let mut on_chain_nonce = 40;
4460 let mut f = MockTransactionFactory::default();
4461 let mut pool = TxPool::mock();
4462 let mut submitted_txs = Vec::new();
4463
4464 let template =
4466 MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
4467
4468 for tx_nonce in 40..48 {
4471 let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
4472 submitted_txs.push(*tx.id());
4473 pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap();
4474 }
4475
4476 on_chain_balance = U256::from(999_999);
4479 on_chain_nonce = 42;
4480 pool.remove_transaction(&submitted_txs[0]);
4481 pool.remove_transaction(&submitted_txs[1]);
4482
4483 for tx_nonce in 48..52 {
4485 pool.add_transaction(
4486 f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
4487 on_chain_balance,
4488 on_chain_nonce,
4489 None,
4490 )
4491 .unwrap();
4492 }
4493
4494 let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
4495 assert_eq!(best_txs.len(), 10); assert_eq!(pool.pending_pool.independent().len(), 1);
4498 }
4499
4500 #[test]
4501 fn test_insertion_disorder() {
4502 let mut f = MockTransactionFactory::default();
4503 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4504
4505 let sender = address!("0x1234567890123456789012345678901234567890");
4506 let tx0 = f.validated_arc(
4507 MockTransaction::legacy().with_sender(sender).with_nonce(0).with_gas_price(10),
4508 );
4509 let tx1 = f.validated_arc(
4510 MockTransaction::eip1559()
4511 .with_sender(sender)
4512 .with_nonce(1)
4513 .with_gas_limit(1000)
4514 .with_gas_price(10),
4515 );
4516 let tx2 = f.validated_arc(
4517 MockTransaction::legacy().with_sender(sender).with_nonce(2).with_gas_price(10),
4518 );
4519 let tx3 = f.validated_arc(
4520 MockTransaction::legacy().with_sender(sender).with_nonce(3).with_gas_price(10),
4521 );
4522
4523 pool.add_transaction((*tx0).clone(), U256::from(1000), 0, None).unwrap();
4525 let mut best = pool.best_transactions();
4526 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4527 assert_eq!(t0.id(), tx0.id());
4528 pool.add_transaction((*tx1).clone(), U256::from(1000), 0, None).unwrap();
4530 let mut best = pool.best_transactions();
4531 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4532 assert_eq!(t0.id(), tx0.id());
4533 assert!(best.next().is_none());
4534
4535 pool.add_transaction((*tx2).clone(), U256::MAX, 0, None).unwrap();
4537
4538 let mut best = pool.best_transactions();
4539
4540 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4541 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4542 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4543 assert_eq!(t0.id(), tx0.id());
4544 assert_eq!(t1.id(), tx1.id());
4545 assert_eq!(t2.id(), tx2.id());
4546
4547 pool.add_transaction((*tx3).clone(), U256::MAX, 0, None).unwrap();
4549 let mut best = pool.best_transactions();
4550 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4551 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4552 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4553 let t3 = best.next().expect("tx3 should be put in the pending subpool");
4554 assert_eq!(t0.id(), tx0.id());
4555 assert_eq!(t1.id(), tx1.id());
4556 assert_eq!(t2.id(), tx2.id());
4557 assert_eq!(t3.id(), tx3.id());
4558 }
4559
4560 #[test]
4561 fn test_non_4844_blob_fee_bit_invariant() {
4562 let mut f = MockTransactionFactory::default();
4563 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4564
4565 let non_4844_tx = MockTransaction::eip1559().set_max_fee(200).inc_limit();
4566 let validated = f.validated(non_4844_tx.clone());
4567
4568 assert!(!non_4844_tx.is_eip4844());
4569 pool.add_transaction(validated.clone(), U256::from(10_000), 0, None).unwrap();
4570
4571 let tx_meta = pool.all_transactions.txs.get(validated.id()).unwrap();
4573 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4574 assert_eq!(tx_meta.subpool, SubPool::Pending);
4575 }
4576
4577 #[test]
4578 fn test_blob_fee_enforcement_only_applies_to_eip4844() {
4579 let mut f = MockTransactionFactory::default();
4580 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4581
4582 let mut block_info = pool.block_info();
4584 block_info.pending_blob_fee = Some(160);
4585 block_info.pending_basefee = 100;
4586 pool.set_block_info(block_info);
4587
4588 let eip4844_tx = MockTransaction::eip4844()
4589 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4590 .with_max_fee(200)
4591 .with_blob_fee(150) .inc_limit();
4593
4594 let non_4844_tx = MockTransaction::eip1559()
4595 .with_sender(address!("0x000000000000000000000000000000000000000b"))
4596 .set_max_fee(200)
4597 .inc_limit();
4598
4599 let validated_4844 = f.validated(eip4844_tx);
4600 let validated_non_4844 = f.validated(non_4844_tx);
4601
4602 pool.add_transaction(validated_4844.clone(), U256::from(10_000), 0, None).unwrap();
4603 pool.add_transaction(validated_non_4844.clone(), U256::from(10_000), 0, None).unwrap();
4604
4605 let tx_4844_meta = pool.all_transactions.txs.get(validated_4844.id()).unwrap();
4606 let tx_non_4844_meta = pool.all_transactions.txs.get(validated_non_4844.id()).unwrap();
4607
4608 assert!(!tx_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4610 assert_eq!(tx_4844_meta.subpool, SubPool::Blob);
4611
4612 assert!(tx_non_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4614 assert_eq!(tx_non_4844_meta.subpool, SubPool::Pending);
4615 }
4616
4617 #[test]
4618 fn test_basefee_decrease_preserves_non_4844_blob_fee_bit() {
4619 let mut f = MockTransactionFactory::default();
4620 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4621
4622 let non_4844_tx = MockTransaction::eip1559()
4624 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4625 .set_max_fee(500) .inc_limit();
4627
4628 pool.update_basefee(600, |_| {});
4630
4631 let validated = f.validated(non_4844_tx);
4632 let tx_id = *validated.id();
4633 pool.add_transaction(validated, U256::from(10_000), 0, None).unwrap();
4634
4635 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4637 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
4638 assert!(
4639 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4640 "Non-4844 tx in BaseFee pool must retain ENOUGH_BLOB_FEE_CAP_BLOCK bit"
4641 );
4642
4643 pool.update_basefee(400, |_| {});
4646
4647 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4649 assert_eq!(
4650 tx_meta.subpool,
4651 SubPool::Pending,
4652 "Non-4844 tx should be promoted from BaseFee to Pending after basefee decrease"
4653 );
4654 assert!(
4655 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4656 "Non-4844 tx must NEVER lose ENOUGH_BLOB_FEE_CAP_BLOCK bit during basefee promotion"
4657 );
4658 assert!(
4659 tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK),
4660 "Non-4844 tx should gain ENOUGH_FEE_CAP_BLOCK bit after basefee decrease"
4661 );
4662 }
4663
4664 #[test]
4670 fn best_transactions_nonce_order_on_balance_unlock() {
4671 let mut f = MockTransactionFactory::default();
4672 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4673
4674 let sender = Address::random();
4675 let on_chain_balance = U256::from(10_000);
4676
4677 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
4679 let tx1 = tx0.next().inc_limit().with_value(U256::from(on_chain_balance));
4681 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
4683
4684 let v0 = f.validated(tx0);
4685 let v1 = f.validated(tx1);
4686 let v2 = f.validated(tx2);
4687
4688 pool.add_transaction(v0, on_chain_balance, 0, None).unwrap();
4690
4691 let mut best = pool.best_transactions();
4693
4694 let first = best.next().expect("should yield tx0");
4696 assert_eq!(first.id().nonce, 0);
4697
4698 pool.add_transaction(v1, on_chain_balance, 0, None).unwrap();
4701
4702 assert!(best.next().is_none(), "tx1 should be queued, not pending");
4704
4705 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
4709
4710 let t1 = best.next().expect("should yield a transaction");
4711 let t2 = best.next().expect("should yield a transaction");
4712
4713 assert_eq!(
4715 t1.id().nonce,
4716 1,
4717 "first yielded tx should be nonce 1, got nonce {}",
4718 t1.id().nonce
4719 );
4720 assert_eq!(
4721 t2.id().nonce,
4722 2,
4723 "second yielded tx should be nonce 2, got nonce {}",
4724 t2.id().nonce
4725 );
4726 }
4727
4728 #[test]
4732 fn best_transactions_nonce_order_on_gap_fill() {
4733 let mut f = MockTransactionFactory::default();
4734 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4735
4736 let sender = Address::random();
4737 let balance = U256::MAX;
4738
4739 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
4741 let tx1 = tx0.next().inc_limit();
4743
4744 let v0 = f.validated(tx0);
4745 let v1 = f.validated(tx1);
4746
4747 pool.add_transaction(v1, balance, 0, None).unwrap();
4749
4750 let mut best = pool.best_transactions();
4752 assert!(best.next().is_none(), "pool should have no pending txs yet");
4753
4754 pool.add_transaction(v0, balance, 0, None).unwrap();
4756
4757 let t0 = best.next().expect("should yield a transaction");
4758 let t1 = best.next().expect("should yield a transaction");
4759
4760 assert_eq!(t0.id().nonce, 0, "first yielded tx should be nonce 0, got {}", t0.id().nonce);
4761 assert_eq!(t1.id().nonce, 1, "second yielded tx should be nonce 1, got {}", t1.id().nonce);
4762 }
4763
4764 #[test]
4768 fn best_transactions_nonce_order_mixed_promotions() {
4769 let mut f = MockTransactionFactory::default();
4770 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4771
4772 let sender = Address::random();
4773 let low_balance = U256::from(10_000);
4774
4775 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
4777 let tx1 = tx0.next().inc_limit().with_value(U256::from(low_balance));
4779 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
4781 let tx3 = tx2.next().inc_limit().with_value(U256::ZERO);
4783
4784 let v0 = f.validated(tx0);
4785 let v1 = f.validated(tx1);
4786 let v2 = f.validated(tx2);
4787 let v3 = f.validated(tx3);
4788
4789 pool.add_transaction(v0, low_balance, 0, None).unwrap();
4791
4792 pool.add_transaction(v1, low_balance, 0, None).unwrap();
4794
4795 pool.add_transaction(v3, low_balance, 0, None).unwrap();
4797
4798 let mut best = pool.best_transactions();
4799
4800 let first = best.next().expect("should yield tx0");
4802 assert_eq!(first.id().nonce, 0);
4803 assert!(best.next().is_none(), "only tx0 should be pending");
4804
4805 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
4810
4811 let t1 = best.next().expect("should yield nonce 1");
4812 let t2 = best.next().expect("should yield nonce 2");
4813 let t3 = best.next().expect("should yield nonce 3");
4814
4815 assert_eq!(t1.id().nonce, 1, "expected nonce 1, got {}", t1.id().nonce);
4816 assert_eq!(t2.id().nonce, 2, "expected nonce 2, got {}", t2.id().nonce);
4817 assert_eq!(t3.id().nonce, 3, "expected nonce 3, got {}", t3.id().nonce);
4818 }
4819}