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