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 Typed2718,
32};
33#[cfg(test)]
34use alloy_primitives::Address;
35use alloy_primitives::{
36 map::{AddressSet, B256Map, B256Set},
37 TxHash, B256,
38};
39use rustc_hash::FxHashMap;
40use smallvec::SmallVec;
41#[cfg(test)]
42use std::collections::{HashMap, HashSet};
43use std::{
44 cmp::Ordering,
45 collections::{btree_map::Entry, hash_map, BTreeMap},
46 fmt,
47 ops::Bound::{Excluded, Unbounded},
48 sync::Arc,
49};
50use tracing::{trace, warn};
51
52#[cfg_attr(doc, aquamarine::aquamarine)]
53pub struct TxPool<T: TransactionOrdering> {
95 pending_pool: PendingPool<T>,
99 config: PoolConfig,
101 queued_pool: ParkedPool<QueuedOrd<T::Transaction>>,
108 basefee_pool: ParkedPool<BasefeeOrd<T::Transaction>>,
113 blob_pool: BlobTransactions<T::Transaction>,
120 all_transactions: AllTransactions<T::Transaction>,
122 metrics: TxPoolMetrics,
124}
125
126impl<T: TransactionOrdering> TxPool<T> {
129 pub fn new(ordering: T, config: PoolConfig) -> Self {
131 Self {
132 pending_pool: PendingPool::with_buffer(
133 ordering,
134 config.max_new_pending_txs_notifications,
135 ),
136 queued_pool: Default::default(),
137 basefee_pool: Default::default(),
138 blob_pool: Default::default(),
139 all_transactions: AllTransactions::new(&config),
140 config,
141 metrics: Default::default(),
142 }
143 }
144
145 pub fn get_highest_nonce_by_sender(&self, sender: SenderId) -> Option<u64> {
147 self.all().txs_iter(sender).last().map(|(_, tx)| tx.transaction.nonce())
148 }
149
150 pub fn get_highest_transaction_by_sender(
153 &self,
154 sender: SenderId,
155 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
156 self.all().txs_iter(sender).last().map(|(_, tx)| Arc::clone(&tx.transaction))
157 }
158
159 pub(crate) fn get_highest_consecutive_transaction_by_sender(
166 &self,
167 mut on_chain: TransactionId,
168 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
169 let mut last_consecutive_tx = None;
170
171 if let Some(current) = self.all_transactions.sender_info.get(&on_chain.sender) {
173 on_chain.nonce = on_chain.nonce.max(current.state_nonce);
174 }
175
176 let mut next_expected_nonce = on_chain.nonce;
177 for (id, tx) in self.all().descendant_txs_inclusive(&on_chain) {
178 if next_expected_nonce != id.nonce {
179 break
180 }
181 next_expected_nonce = id.next_nonce();
182 last_consecutive_tx = Some(tx);
183 }
184
185 last_consecutive_tx.map(|tx| Arc::clone(&tx.transaction))
186 }
187
188 pub(crate) const fn all(&self) -> &AllTransactions<T::Transaction> {
190 &self.all_transactions
191 }
192
193 pub(crate) fn unique_senders(&self) -> AddressSet {
195 self.all_transactions.txs.values().map(|tx| tx.transaction.sender()).collect()
196 }
197
198 pub fn size(&self) -> PoolSize {
200 PoolSize {
201 pending: self.pending_pool.len(),
202 pending_size: self.pending_pool.size(),
203 basefee: self.basefee_pool.len(),
204 basefee_size: self.basefee_pool.size(),
205 queued: self.queued_pool.len(),
206 queued_size: self.queued_pool.size(),
207 blob: self.blob_pool.len(),
208 blob_size: self.blob_pool.size(),
209 total: self.all_transactions.len(),
210 }
211 }
212
213 pub const fn block_info(&self) -> BlockInfo {
215 BlockInfo {
216 block_gas_limit: self.all_transactions.block_gas_limit,
217 last_seen_block_hash: self.all_transactions.last_seen_block_hash,
218 last_seen_block_number: self.all_transactions.last_seen_block_number,
219 pending_basefee: self.all_transactions.pending_fees.base_fee,
220 pending_blob_fee: Some(self.all_transactions.pending_fees.blob_fee),
221 }
222 }
223
224 fn update_blob_fee<F>(
226 &mut self,
227 mut pending_blob_fee: u128,
228 base_fee_update: Ordering,
229 mut on_promoted: F,
230 ) where
231 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
232 {
233 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut pending_blob_fee);
234 match (self.all_transactions.pending_fees.blob_fee.cmp(&pending_blob_fee), base_fee_update)
235 {
236 (Ordering::Equal, Ordering::Equal | Ordering::Greater) => {
237 }
239 (Ordering::Greater, Ordering::Equal | Ordering::Greater) => {
240 let removed =
242 self.pending_pool.update_blob_fee(self.all_transactions.pending_fees.blob_fee);
243 for tx in removed {
244 let to = {
245 let tx =
246 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
247
248 tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
250 tx.subpool = tx.state.into();
251 tx.subpool
252 };
253 self.add_transaction_to_subpool(to, tx);
254 }
255 }
256 (Ordering::Less, _) | (_, Ordering::Less) => {
257 let removed =
259 self.blob_pool.enforce_pending_fees(&self.all_transactions.pending_fees);
260 for tx in removed {
261 let subpool = {
262 let tx_meta =
263 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
264 tx_meta.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
265 tx_meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
266 tx_meta.subpool = tx_meta.state.into();
267 tx_meta.subpool
268 };
269
270 if subpool == SubPool::Pending {
271 on_promoted(&tx);
272 }
273
274 self.add_transaction_to_subpool(subpool, tx);
275 }
276 }
277 }
278 }
279
280 fn update_basefee<F>(&mut self, mut pending_basefee: u64, mut on_promoted: F) -> Ordering
285 where
286 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
287 {
288 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut pending_basefee);
289 match self.all_transactions.pending_fees.base_fee.cmp(&pending_basefee) {
290 Ordering::Equal => {
291 Ordering::Equal
293 }
294 Ordering::Greater => {
295 let removed =
297 self.pending_pool.update_base_fee(self.all_transactions.pending_fees.base_fee);
298 for tx in removed {
299 let to = {
300 let tx =
301 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
302 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
303 tx.subpool = tx.state.into();
304 tx.subpool
305 };
306 self.add_transaction_to_subpool(to, tx);
307 }
308
309 Ordering::Greater
310 }
311 Ordering::Less => {
312 let current_base_fee = self.all_transactions.pending_fees.base_fee;
321 self.basefee_pool.enforce_basefee_with(current_base_fee, |tx| {
322 let subpool = {
324 let meta =
325 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
326 meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
327 meta.subpool = meta.state.into();
328 meta.subpool
329 };
330
331 if subpool == SubPool::Pending {
332 on_promoted(&tx);
333 }
334
335 trace!(target: "txpool", hash=%tx.transaction.hash(), pool=?subpool, "Adding transaction to a subpool");
336 match subpool {
337 SubPool::Queued => self.queued_pool.add_transaction(tx),
338 SubPool::Pending => {
339 self.pending_pool.add_transaction(tx, current_base_fee);
340 }
341 SubPool::Blob => {
342 self.blob_pool.add_transaction(tx);
343 }
344 SubPool::BaseFee => {
345 warn!(target: "txpool", "BaseFee transactions should become Pending after basefee decrease");
348 }
349 }
350 });
351
352 Ordering::Less
353 }
354 }
355 }
356
357 pub fn set_block_info(&mut self, info: BlockInfo) -> UpdateOutcome<T::Transaction> {
363 let mut outcome = UpdateOutcome::default();
364
365 let basefee_ordering = self.update_basefee(info.pending_basefee, |tx| {
367 outcome.promoted.push(tx.clone());
368 });
369 if let Some(blob_fee) = info.pending_blob_fee {
370 self.update_blob_fee(blob_fee, basefee_ordering, |tx| {
371 outcome.promoted.push(tx.clone());
372 })
373 }
374 self.all_transactions.set_block_info(info);
376
377 outcome
378 }
379
380 pub(crate) fn best_transactions(&self) -> BestTransactions<T> {
383 self.pending_pool.best()
384 }
385
386 pub(crate) fn best_transactions_with_attributes(
393 &self,
394 best_transactions_attributes: BestTransactionsAttributes,
395 ) -> Box<dyn crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T::Transaction>>>>
396 {
397 match best_transactions_attributes.basefee.cmp(&self.all_transactions.pending_fees.base_fee)
400 {
401 Ordering::Equal => {
402 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
406 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
407 Ordering::Less => {
408 let unlocked =
410 self.blob_pool.satisfy_attributes(best_transactions_attributes);
411 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
412 unlocked,
413 best_transactions_attributes.basefee,
414 new_blob_fee,
415 ))
416 }
417 Ordering::Equal => Box::new(self.pending_pool.best()),
418 Ordering::Greater => {
419 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
421 best_transactions_attributes.basefee,
422 best_transactions_attributes.blob_fee.unwrap_or_default(),
423 ))
424 }
425 }
426 }
427 Ordering::Greater => {
428 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
430 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
431 Ordering::Less => {
432 let unlocked =
434 self.blob_pool.satisfy_attributes(best_transactions_attributes);
435 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
436 unlocked,
437 best_transactions_attributes.basefee,
438 new_blob_fee,
439 ))
440 }
441 Ordering::Equal | Ordering::Greater => {
442 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
444 best_transactions_attributes.basefee,
445 new_blob_fee,
446 ))
447 }
448 }
449 }
450 Ordering::Less => {
451 let mut unlocked = self
454 .basefee_pool
455 .satisfy_base_fee_transactions(best_transactions_attributes.basefee);
456
457 unlocked.extend(self.blob_pool.satisfy_attributes(best_transactions_attributes));
459
460 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
461 unlocked,
462 best_transactions_attributes.basefee,
463 best_transactions_attributes.blob_fee.unwrap_or_default(),
464 ))
465 }
466 }
467 }
468
469 pub(crate) fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
471 self.pending_pool.all().collect()
472 }
473 pub(crate) fn pending_transactions_iter(
475 &self,
476 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
477 self.pending_pool.all()
478 }
479
480 pub(crate) fn pending_transactions_count(&self) -> usize {
482 self.pending_pool.len()
483 }
484
485 pub(crate) fn pending_transactions_with_predicate(
487 &self,
488 mut predicate: impl FnMut(&ValidPoolTransaction<T::Transaction>) -> bool,
489 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
490 self.pending_transactions_iter().filter(|tx| predicate(tx)).collect()
491 }
492
493 pub(crate) fn pending_txs_by_sender(
495 &self,
496 sender: SenderId,
497 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
498 self.pending_pool.txs_by_sender(sender)
499 }
500
501 pub(crate) fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
503 self.basefee_pool.all().chain(self.queued_pool.all()).collect()
504 }
505
506 pub(crate) fn queued_transactions_count(&self) -> usize {
508 self.basefee_pool.len() + self.queued_pool.len()
509 }
510
511 pub fn queued_and_pending_txs_by_sender(
513 &self,
514 sender: SenderId,
515 ) -> (SmallVec<[TransactionId; TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER]>, Vec<TransactionId>) {
516 (self.queued_pool.get_txs_by_sender(sender), self.pending_pool.get_txs_by_sender(sender))
517 }
518
519 pub(crate) fn queued_txs_by_sender(
521 &self,
522 sender: SenderId,
523 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
524 let mut txs = self.basefee_pool.txs_by_sender(sender);
525 txs.extend(self.queued_pool.txs_by_sender(sender));
526 txs
527 }
528
529 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
531 self.all_transactions.contains(tx_hash)
532 }
533
534 #[cfg(test)]
536 pub(crate) fn subpool_contains(&self, subpool: SubPool, id: &TransactionId) -> bool {
537 match subpool {
538 SubPool::Queued => self.queued_pool.contains(id),
539 SubPool::Pending => self.pending_pool.contains(id),
540 SubPool::BaseFee => self.basefee_pool.contains(id),
541 SubPool::Blob => self.blob_pool.contains(id),
542 }
543 }
544
545 #[inline]
547 pub(crate) fn is_exceeded(&self) -> bool {
548 self.config.is_exceeded(self.size())
549 }
550
551 pub(crate) fn get(
553 &self,
554 tx_hash: &TxHash,
555 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
556 self.all_transactions.by_hash.get(tx_hash).cloned()
557 }
558
559 pub(crate) fn get_all(
561 &self,
562 txs: Vec<TxHash>,
563 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
564 txs.into_iter().filter_map(|tx| self.get(&tx))
565 }
566
567 pub(crate) fn get_transactions_by_sender(
569 &self,
570 sender: SenderId,
571 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
572 self.all_transactions.txs_iter(sender).map(|(_, tx)| Arc::clone(&tx.transaction)).collect()
573 }
574
575 pub(crate) fn get_pending_transaction_by_sender_and_nonce(
577 &self,
578 sender: SenderId,
579 nonce: u64,
580 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
581 self.all_transactions
582 .txs_iter(sender)
583 .find(|(id, tx)| id.nonce == nonce && tx.subpool == SubPool::Pending)
584 .map(|(_, tx)| Arc::clone(&tx.transaction))
585 }
586
587 const fn update_pending_fees_only(
590 &mut self,
591 mut new_base_fee: u64,
592 new_blob_fee: Option<u128>,
593 ) -> (u64, u128) {
594 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut new_base_fee);
595
596 let prev_blob_fee = if let Some(mut blob_fee) = new_blob_fee {
597 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut blob_fee);
598 blob_fee
599 } else {
600 self.all_transactions.pending_fees.blob_fee
601 };
602
603 (new_base_fee, prev_blob_fee)
604 }
605
606 fn apply_fee_updates(
613 &mut self,
614 prev_base_fee: u64,
615 prev_blob_fee: u128,
616 outcome: &mut UpdateOutcome<T::Transaction>,
617 ) {
618 let new_base_fee = self.all_transactions.pending_fees.base_fee;
619 let new_blob_fee = self.all_transactions.pending_fees.blob_fee;
620
621 if new_base_fee == prev_base_fee && new_blob_fee == prev_blob_fee {
622 return;
624 }
625
626 self.all_transactions.pending_fees.base_fee = prev_base_fee;
629 self.all_transactions.pending_fees.blob_fee = prev_blob_fee;
630
631 let base_fee_ordering = self.update_basefee(new_base_fee, |tx| {
632 outcome.promoted.push(tx.clone());
633 });
634
635 self.update_blob_fee(new_blob_fee, base_fee_ordering, |tx| {
636 outcome.promoted.push(tx.clone());
637 });
638 }
639
640 pub(crate) fn update_accounts(
642 &mut self,
643 changed_senders: FxHashMap<SenderId, SenderInfo>,
644 ) -> UpdateOutcome<T::Transaction> {
645 let updates = self.all_transactions.update(&changed_senders);
647
648 self.all_transactions.sender_info.extend(changed_senders);
650
651 let update = self.process_updates(updates);
653 self.update_size_metrics();
655 update
656 }
657
658 pub(crate) fn on_canonical_state_change(
663 &mut self,
664 block_info: BlockInfo,
665 mined_transactions: Vec<TxHash>,
666 changed_senders: FxHashMap<SenderId, SenderInfo>,
667 _update_kind: PoolUpdateKind,
668 ) -> OnNewCanonicalStateOutcome<T::Transaction> {
669 let block_hash = block_info.last_seen_block_hash;
671
672 let mut removed_txs_count = 0;
674 for tx_hash in &mined_transactions {
675 if self.prune_transaction_by_hash(tx_hash).is_some() {
676 removed_txs_count += 1;
677 }
678 }
679
680 self.metrics.removed_transactions.increment(removed_txs_count);
682
683 let (prev_base_fee, prev_blob_fee) =
688 self.update_pending_fees_only(block_info.pending_basefee, block_info.pending_blob_fee);
689
690 let mut outcome = self.update_accounts(changed_senders);
692
693 self.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
696
697 self.all_transactions.set_block_info(block_info);
699
700 self.update_transaction_type_metrics();
701 self.metrics.performed_state_updates.increment(1);
702
703 OnNewCanonicalStateOutcome {
704 block_hash,
705 mined: mined_transactions,
706 promoted: outcome.promoted,
707 discarded: outcome.discarded,
708 }
709 }
710
711 pub(crate) fn update_size_metrics(&self) {
713 let stats = self.size();
714 self.metrics.pending_pool_transactions.set(stats.pending as f64);
715 self.metrics.pending_pool_size_bytes.set(stats.pending_size as f64);
716 self.metrics.basefee_pool_transactions.set(stats.basefee as f64);
717 self.metrics.basefee_pool_size_bytes.set(stats.basefee_size as f64);
718 self.metrics.queued_pool_transactions.set(stats.queued as f64);
719 self.metrics.queued_pool_size_bytes.set(stats.queued_size as f64);
720 self.metrics.blob_pool_transactions.set(stats.blob as f64);
721 self.metrics.blob_pool_size_bytes.set(stats.blob_size as f64);
722 self.metrics.total_transactions.set(stats.total as f64);
723 }
724
725 pub(crate) fn update_transaction_type_metrics(&self) {
727 let mut legacy_count = 0;
728 let mut eip2930_count = 0;
729 let mut eip1559_count = 0;
730 let mut eip4844_count = 0;
731 let mut eip7702_count = 0;
732 let mut other_count = 0;
733
734 for tx in self.all_transactions.transactions_iter() {
735 match tx.transaction.ty() {
736 LEGACY_TX_TYPE_ID => legacy_count += 1,
737 EIP2930_TX_TYPE_ID => eip2930_count += 1,
738 EIP1559_TX_TYPE_ID => eip1559_count += 1,
739 EIP4844_TX_TYPE_ID => eip4844_count += 1,
740 EIP7702_TX_TYPE_ID => eip7702_count += 1,
741 _ => other_count += 1,
742 }
743 }
744
745 self.metrics.total_legacy_transactions.set(legacy_count as f64);
746 self.metrics.total_eip2930_transactions.set(eip2930_count as f64);
747 self.metrics.total_eip1559_transactions.set(eip1559_count as f64);
748 self.metrics.total_eip4844_transactions.set(eip4844_count as f64);
749 self.metrics.total_eip7702_transactions.set(eip7702_count as f64);
750 self.metrics.total_other_transactions.set(other_count as f64);
751 }
752
753 pub(crate) fn add_transaction(
754 &mut self,
755 tx: ValidPoolTransaction<T::Transaction>,
756 on_chain_balance: U256,
757 on_chain_nonce: u64,
758 on_chain_code_hash: Option<B256>,
759 ) -> PoolResult<AddedTransaction<T::Transaction>> {
760 if self.contains(tx.hash()) {
761 return Err(PoolError::new(*tx.hash(), PoolErrorKind::AlreadyImported))
762 }
763
764 self.validate_auth(&tx, on_chain_nonce, on_chain_code_hash)?;
765
766 self.all_transactions
768 .sender_info
769 .entry(tx.sender_id())
770 .or_default()
771 .update(on_chain_nonce, on_chain_balance);
772
773 match self.all_transactions.insert_tx(tx, on_chain_balance, on_chain_nonce) {
774 Ok(InsertOk { transaction, move_to, replaced_tx, mut updates, state }) => {
775 let new_nonce = transaction.id().nonce;
783 let split = updates.iter().position(|u| u.id.nonce >= new_nonce);
784 let (promoted, discarded) = match split {
785 None => {
787 let UpdateOutcome { promoted, discarded } = self.process_updates(updates);
788 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
789 (promoted, discarded)
790 }
791 Some(0) => {
793 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
794 let UpdateOutcome { promoted, discarded } = self.process_updates(updates);
795 (promoted, discarded)
796 }
797 Some(i) => {
799 let after = updates.split_off(i);
800 let mut outcome = self.process_updates(updates);
801 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
802 let after_outcome = self.process_updates(after);
803 outcome.promoted.extend(after_outcome.promoted);
804 outcome.discarded.extend(after_outcome.discarded);
805 (outcome.promoted, outcome.discarded)
806 }
807 };
808 self.metrics.inserted_transactions.increment(1);
809
810 let replaced = replaced_tx.map(|(tx, _)| tx);
811
812 let res = if move_to.is_pending() {
814 AddedTransaction::Pending(AddedPendingTransaction {
815 transaction,
816 promoted,
817 discarded,
818 replaced,
819 })
820 } else {
821 let queued_reason = state.determine_queued_reason(move_to);
823 AddedTransaction::Parked {
824 transaction,
825 subpool: move_to,
826 replaced,
827 queued_reason,
828 }
829 };
830
831 self.update_size_metrics();
833
834 Ok(res)
835 }
836 Err(err) => {
837 self.metrics.invalid_transactions.increment(1);
839 match err {
840 InsertErr::Underpriced { existing: _, transaction } => Err(PoolError::new(
841 *transaction.hash(),
842 PoolErrorKind::ReplacementUnderpriced,
843 )),
844 InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap } => {
845 Err(PoolError::new(
846 *transaction.hash(),
847 PoolErrorKind::FeeCapBelowMinimumProtocolFeeCap(fee_cap),
848 ))
849 }
850 InsertErr::ExceededSenderTransactionsCapacity { transaction } => {
851 Err(PoolError::new(
852 *transaction.hash(),
853 PoolErrorKind::SpammerExceededCapacity(transaction.sender()),
854 ))
855 }
856 InsertErr::TxGasLimitMoreThanAvailableBlockGas {
857 transaction,
858 block_gas_limit,
859 tx_gas_limit,
860 } => Err(PoolError::new(
861 *transaction.hash(),
862 PoolErrorKind::InvalidTransaction(
863 InvalidPoolTransactionError::ExceedsGasLimit(
864 tx_gas_limit,
865 block_gas_limit,
866 ),
867 ),
868 )),
869 InsertErr::BlobTxHasNonceGap { transaction } => Err(PoolError::new(
870 *transaction.hash(),
871 PoolErrorKind::InvalidTransaction(
872 Eip4844PoolTransactionError::Eip4844NonceGap.into(),
873 ),
874 )),
875 InsertErr::Overdraft { transaction } => Err(PoolError::new(
876 *transaction.hash(),
877 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Overdraft {
878 cost: *transaction.cost(),
879 balance: on_chain_balance,
880 }),
881 )),
882 InsertErr::TxTypeConflict { transaction } => Err(PoolError::new(
883 *transaction.hash(),
884 PoolErrorKind::ExistingConflictingTransactionType(
885 transaction.sender(),
886 transaction.tx_type(),
887 ),
888 )),
889 }
890 }
891 }
892 }
893
894 fn check_delegation_limit(
898 &self,
899 transaction: &ValidPoolTransaction<T::Transaction>,
900 on_chain_nonce: u64,
901 on_chain_code_hash: Option<B256>,
902 ) -> Result<(), PoolError> {
903 if (on_chain_code_hash.is_none() || on_chain_code_hash == Some(KECCAK_EMPTY)) &&
905 !self.all_transactions.auths.contains_key(&transaction.sender_id())
906 {
907 return Ok(())
908 }
909
910 let mut txs_by_sender =
911 self.pending_pool.iter_txs_by_sender(transaction.sender_id()).peekable();
912
913 if txs_by_sender.peek().is_none() {
914 let nonce_gap_distance = transaction.nonce().saturating_sub(on_chain_nonce);
919 if nonce_gap_distance >= self.config.max_inflight_delegated_slot_limit as u64 {
920 return Err(PoolError::new(
921 *transaction.hash(),
922 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
923 Eip7702PoolTransactionError::OutOfOrderTxFromDelegated,
924 )),
925 ))
926 }
927 return Ok(())
928 }
929
930 let mut count = 0;
931 for id in txs_by_sender {
932 if id == &transaction.transaction_id {
933 return Ok(())
935 }
936 count += 1;
937 }
938
939 if count < self.config.max_inflight_delegated_slot_limit {
940 return Ok(())
942 }
943
944 Err(PoolError::new(
945 *transaction.hash(),
946 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
947 Eip7702PoolTransactionError::InflightTxLimitReached,
948 )),
949 ))
950 }
951
952 fn validate_auth(
962 &self,
963 transaction: &ValidPoolTransaction<T::Transaction>,
964 on_chain_nonce: u64,
965 on_chain_code_hash: Option<B256>,
966 ) -> Result<(), PoolError> {
967 self.check_delegation_limit(transaction, on_chain_nonce, on_chain_code_hash)?;
969
970 if let Some(authority_list) = &transaction.authority_ids {
971 for sender_id in authority_list {
972 if self
974 .all_transactions
975 .txs_iter(*sender_id)
976 .nth(self.config.max_inflight_delegated_slot_limit)
977 .is_some()
978 {
979 return Err(PoolError::new(
980 *transaction.hash(),
981 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
982 Eip7702PoolTransactionError::AuthorityReserved,
983 )),
984 ))
985 }
986 }
987 }
988
989 Ok(())
990 }
991
992 fn process_updates(&mut self, updates: Vec<PoolUpdate>) -> UpdateOutcome<T::Transaction> {
996 let mut outcome = UpdateOutcome::default();
997 let mut removed = 0;
998 for PoolUpdate { id, current, destination } in updates {
999 match destination {
1000 Destination::Discard => {
1001 if let Some(tx) = self.prune_transaction_by_id(&id) {
1003 outcome.discarded.push(tx);
1004 }
1005 removed += 1;
1006 }
1007 Destination::Pool(move_to) => {
1008 debug_assert_ne!(&move_to, ¤t, "destination must be different");
1009 let moved = self.move_transaction(current, move_to, &id);
1010 if matches!(move_to, SubPool::Pending) &&
1011 let Some(tx) = moved
1012 {
1013 trace!(target: "txpool", hash=%tx.transaction.hash(), "Promoted transaction to pending");
1014 outcome.promoted.push(tx);
1015 }
1016 }
1017 }
1018 }
1019
1020 if removed > 0 {
1021 self.metrics.removed_transactions.increment(removed);
1022 }
1023
1024 outcome
1025 }
1026
1027 fn move_transaction(
1032 &mut self,
1033 from: SubPool,
1034 to: SubPool,
1035 id: &TransactionId,
1036 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1037 let tx = self.remove_from_subpool(from, id)?;
1038 self.add_transaction_to_subpool(to, tx.clone());
1039 Some(tx)
1040 }
1041
1042 pub(crate) fn remove_transactions(
1047 &mut self,
1048 hashes: Vec<TxHash>,
1049 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1050 let txs =
1051 hashes.into_iter().filter_map(|hash| self.remove_transaction_by_hash(&hash)).collect();
1052 self.update_size_metrics();
1053 txs
1054 }
1055
1056 pub(crate) fn remove_transactions_and_descendants(
1058 &mut self,
1059 hashes: Vec<TxHash>,
1060 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1061 let mut removed = Vec::new();
1062 for hash in hashes {
1063 if let Some(tx) = self.remove_transaction_by_hash(&hash) {
1064 removed.push(tx.clone());
1065 self.remove_descendants(tx.id(), &mut removed);
1066 }
1067 }
1068 self.update_size_metrics();
1069 removed
1070 }
1071
1072 pub(crate) fn remove_transactions_by_sender(
1074 &mut self,
1075 sender_id: SenderId,
1076 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1077 let mut removed = Vec::new();
1078 let txs = self.get_transactions_by_sender(sender_id);
1079 for tx in txs {
1080 if let Some(tx) = self.remove_transaction(tx.id()) {
1081 removed.push(tx);
1082 }
1083 }
1084 self.update_size_metrics();
1085 removed
1086 }
1087
1088 pub(crate) fn prune_transactions(
1094 &mut self,
1095 hashes: Vec<TxHash>,
1096 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1097 let txs =
1098 hashes.into_iter().filter_map(|hash| self.prune_transaction_by_hash(&hash)).collect();
1099 self.update_size_metrics();
1100 txs
1101 }
1102
1103 fn remove_transaction(
1107 &mut self,
1108 id: &TransactionId,
1109 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1110 let (tx, pool) = self.all_transactions.remove_transaction(id)?;
1111 self.remove_from_subpool(pool, tx.id())
1112 }
1113
1114 fn remove_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
1125 let updates = self.all_transactions.park_descendant_transactions(tx.id());
1127 self.process_updates(updates);
1128 self.remove_from_subpool(pool, tx.id())
1129 }
1130
1131 fn prune_transaction_by_hash(
1138 &mut self,
1139 tx_hash: &B256,
1140 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1141 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1142 self.remove_from_subpool(pool, tx.id())
1143 }
1144 fn prune_transaction_by_id(
1149 &mut self,
1150 tx_id: &TransactionId,
1151 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1152 let (tx, pool) = self.all_transactions.remove_transaction_by_id(tx_id)?;
1153 self.remove_from_subpool(pool, tx.id())
1154 }
1155
1156 fn remove_from_subpool(
1160 &mut self,
1161 pool: SubPool,
1162 tx: &TransactionId,
1163 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1164 let tx = match pool {
1165 SubPool::Queued => self.queued_pool.remove_transaction(tx),
1166 SubPool::Pending => self.pending_pool.remove_transaction(tx),
1167 SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
1168 SubPool::Blob => self.blob_pool.remove_transaction(tx),
1169 };
1170
1171 if let Some(ref tx) = tx {
1172 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Removed transaction from a subpool");
1176 }
1177
1178 tx
1179 }
1180
1181 fn remove_descendants(
1185 &mut self,
1186 tx: &TransactionId,
1187 removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
1188 ) {
1189 let mut id = *tx;
1190
1191 loop {
1193 let descendant =
1194 self.all_transactions.descendant_txs_exclusive(&id).map(|(id, _)| *id).next();
1195 if let Some(descendant) = descendant {
1196 if let Some(tx) = self.remove_transaction(&descendant) {
1197 removed.push(tx)
1198 }
1199 id = descendant;
1200 } else {
1201 return
1202 }
1203 }
1204 }
1205
1206 fn add_transaction_to_subpool(
1208 &mut self,
1209 pool: SubPool,
1210 tx: Arc<ValidPoolTransaction<T::Transaction>>,
1211 ) {
1212 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Adding transaction to a subpool");
1216 match pool {
1217 SubPool::Queued => self.queued_pool.add_transaction(tx),
1218 SubPool::Pending => {
1219 self.pending_pool.add_transaction(tx, self.all_transactions.pending_fees.base_fee);
1220 }
1221 SubPool::BaseFee => {
1222 self.basefee_pool.add_transaction(tx);
1223 }
1224 SubPool::Blob => {
1225 self.blob_pool.add_transaction(tx);
1226 }
1227 }
1228 }
1229
1230 fn add_new_transaction(
1233 &mut self,
1234 transaction: Arc<ValidPoolTransaction<T::Transaction>>,
1235 replaced: Option<(Arc<ValidPoolTransaction<T::Transaction>>, SubPool)>,
1236 pool: SubPool,
1237 ) {
1238 if let Some((replaced, replaced_pool)) = replaced {
1239 self.remove_from_subpool(replaced_pool, replaced.id());
1241 }
1242
1243 self.add_transaction_to_subpool(pool, transaction)
1244 }
1245
1246 pub(crate) fn discard_worst(&mut self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1253 let mut removed = Vec::new();
1254
1255 macro_rules! discard_worst {
1257 ($this:ident, $removed:ident, [$($limit:ident => ($pool:ident, $metric:ident)),* $(,)*]) => {
1258 $ (
1259 while $this.$pool.exceeds(&$this.config.$limit)
1260 {
1261 trace!(
1262 target: "txpool",
1263 "discarding transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1264 stringify!($pool),
1265 $this.config.$limit,
1266 $this.$pool.size(),
1267 $this.$pool.len(),
1268 );
1269
1270 let removed_from_subpool = $this.$pool.truncate_pool($this.config.$limit.clone());
1272
1273 trace!(
1274 target: "txpool",
1275 "removed {} transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1276 removed_from_subpool.len(),
1277 stringify!($pool),
1278 $this.config.$limit,
1279 $this.$pool.size(),
1280 $this.$pool.len()
1281 );
1282 $this.metrics.$metric.increment(removed_from_subpool.len() as u64);
1283
1284 for tx in removed_from_subpool {
1286 $this.all_transactions.remove_transaction(tx.id());
1287
1288 let id = *tx.id();
1289
1290 removed.push(tx);
1292
1293 $this.remove_descendants(&id, &mut $removed);
1295 }
1296 }
1297
1298 )*
1299 };
1300 }
1301
1302 discard_worst!(
1303 self, removed, [
1304 pending_limit => (pending_pool, pending_transactions_evicted),
1305 basefee_limit => (basefee_pool, basefee_transactions_evicted),
1306 blob_limit => (blob_pool, blob_transactions_evicted),
1307 queued_limit => (queued_pool, queued_transactions_evicted),
1308 ]
1309 );
1310
1311 removed
1312 }
1313
1314 pub(crate) fn len(&self) -> usize {
1316 self.all_transactions.len()
1317 }
1318
1319 pub(crate) fn is_empty(&self) -> bool {
1321 self.all_transactions.is_empty()
1322 }
1323
1324 #[cfg(any(test, feature = "test-utils"))]
1332 pub fn assert_invariants(&self) {
1333 let size = self.size();
1334 let actual = size.basefee + size.pending + size.queued + size.blob;
1335 assert_eq!(
1336 size.total, actual,
1337 "total size must be equal to the sum of all sub-pools, basefee:{}, pending:{}, queued:{}, blob:{}",
1338 size.basefee, size.pending, size.queued, size.blob
1339 );
1340 self.all_transactions.assert_invariants();
1341 self.pending_pool.assert_invariants();
1342 self.basefee_pool.assert_invariants();
1343 self.queued_pool.assert_invariants();
1344 self.blob_pool.assert_invariants();
1345 }
1346}
1347
1348#[cfg(any(test, feature = "test-utils"))]
1349impl TxPool<crate::test_utils::MockOrdering> {
1350 pub fn mock() -> Self {
1352 Self::new(crate::test_utils::MockOrdering::default(), PoolConfig::default())
1353 }
1354}
1355
1356#[cfg(test)]
1357impl<T: TransactionOrdering> Drop for TxPool<T> {
1358 fn drop(&mut self) {
1359 self.assert_invariants();
1360 }
1361}
1362
1363impl<T: TransactionOrdering> TxPool<T> {
1364 pub const fn pending(&self) -> &PendingPool<T> {
1366 &self.pending_pool
1367 }
1368
1369 pub const fn base_fee(&self) -> &ParkedPool<BasefeeOrd<T::Transaction>> {
1371 &self.basefee_pool
1372 }
1373
1374 pub const fn queued(&self) -> &ParkedPool<QueuedOrd<T::Transaction>> {
1376 &self.queued_pool
1377 }
1378}
1379
1380impl<T: TransactionOrdering> fmt::Debug for TxPool<T> {
1381 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1382 f.debug_struct("TxPool").field("config", &self.config).finish_non_exhaustive()
1383 }
1384}
1385
1386pub(crate) struct AllTransactions<T: PoolTransaction> {
1391 minimal_protocol_basefee: u64,
1395 block_gas_limit: u64,
1397 max_account_slots: usize,
1399 by_hash: B256Map<Arc<ValidPoolTransaction<T>>>,
1401 txs: BTreeMap<TransactionId, PoolInternalTransaction<T>>,
1403 sender_info: FxHashMap<SenderId, SenderInfo>,
1405 tx_counter: FxHashMap<SenderId, usize>,
1407 last_seen_block_number: u64,
1409 last_seen_block_hash: B256,
1411 pending_fees: PendingFees,
1413 price_bumps: PriceBumpConfig,
1415 local_transactions_config: LocalTransactionConfig,
1417 auths: FxHashMap<SenderId, B256Set>,
1419 metrics: AllTransactionsMetrics,
1421}
1422
1423impl<T: PoolTransaction> AllTransactions<T> {
1424 fn new(config: &PoolConfig) -> Self {
1426 Self {
1427 max_account_slots: config.max_account_slots,
1428 price_bumps: config.price_bumps,
1429 local_transactions_config: config.local_transactions_config.clone(),
1430 minimal_protocol_basefee: config.minimal_protocol_basefee,
1431 block_gas_limit: config.gas_limit,
1432 ..Default::default()
1433 }
1434 }
1435
1436 #[expect(dead_code)]
1438 pub(crate) fn hashes_iter(&self) -> impl Iterator<Item = TxHash> + '_ {
1439 self.by_hash.keys().copied()
1440 }
1441
1442 pub(crate) fn transactions_iter(
1444 &self,
1445 ) -> impl Iterator<Item = &Arc<ValidPoolTransaction<T>>> + '_ {
1446 self.by_hash.values()
1447 }
1448
1449 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
1451 self.by_hash.contains_key(tx_hash)
1452 }
1453
1454 pub(crate) fn get(&self, id: &TransactionId) -> Option<&PoolInternalTransaction<T>> {
1456 self.txs.get(id)
1457 }
1458
1459 pub(crate) fn tx_inc(&mut self, sender: SenderId) {
1461 let count = self.tx_counter.entry(sender).or_default();
1462 *count += 1;
1463 self.metrics.all_transactions_by_all_senders.increment(1.0);
1464 }
1465
1466 pub(crate) fn tx_decr(&mut self, sender: SenderId) {
1468 if let hash_map::Entry::Occupied(mut entry) = self.tx_counter.entry(sender) {
1469 let count = entry.get_mut();
1470 if *count == 1 {
1471 entry.remove();
1472 self.sender_info.remove(&sender);
1473 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1474 return
1475 }
1476 *count -= 1;
1477 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1478 }
1479 }
1480
1481 fn set_block_info(&mut self, block_info: BlockInfo) {
1483 let BlockInfo {
1484 block_gas_limit,
1485 last_seen_block_hash,
1486 last_seen_block_number,
1487 pending_basefee,
1488 pending_blob_fee,
1489 } = block_info;
1490 self.last_seen_block_number = last_seen_block_number;
1491 self.last_seen_block_hash = last_seen_block_hash;
1492
1493 self.pending_fees.base_fee = pending_basefee;
1494 self.metrics.base_fee.set(pending_basefee as f64);
1495
1496 self.block_gas_limit = block_gas_limit;
1497
1498 if let Some(pending_blob_fee) = pending_blob_fee {
1499 self.pending_fees.blob_fee = pending_blob_fee;
1500 self.metrics.blob_base_fee.set(pending_blob_fee as f64);
1501 }
1502 }
1503
1504 pub(crate) fn update_size_metrics(&self) {
1506 self.metrics.all_transactions_by_hash.set(self.by_hash.len() as f64);
1507 self.metrics.all_transactions_by_id.set(self.txs.len() as f64);
1508 }
1509
1510 pub(crate) fn update(
1527 &mut self,
1528 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1529 ) -> Vec<PoolUpdate> {
1530 let mut updates = Vec::with_capacity(64);
1532
1533 let mut iter = self.txs.iter_mut().peekable();
1534
1535 'transactions: while let Some((id, tx)) = iter.next() {
1545 macro_rules! next_sender {
1546 ($iter:ident) => {
1547 'this: while let Some((peek, _)) = iter.peek() {
1548 if peek.sender != id.sender {
1549 break 'this
1550 }
1551 iter.next();
1552 }
1553 };
1554 }
1555
1556 let changed_balance = if let Some(info) = changed_accounts.get(&id.sender) {
1559 if id.nonce < info.state_nonce {
1561 updates.push(PoolUpdate {
1562 id: *tx.transaction.id(),
1563 current: tx.subpool,
1564 destination: Destination::Discard,
1565 });
1566 continue 'transactions
1567 }
1568
1569 let ancestor = TransactionId::ancestor(id.nonce, info.state_nonce, id.sender);
1570 if ancestor.is_none() {
1572 tx.state.insert(TxState::NO_NONCE_GAPS);
1573 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1574 tx.cumulative_cost = U256::ZERO;
1575 if tx.transaction.cost() > &info.balance {
1576 tx.state.remove(TxState::ENOUGH_BALANCE);
1578 } else {
1579 tx.state.insert(TxState::ENOUGH_BALANCE);
1580 }
1581 }
1582
1583 Some(&info.balance)
1584 } else {
1585 None
1586 };
1587
1588 if tx.state.has_nonce_gap() {
1590 next_sender!(iter);
1591 continue 'transactions
1592 }
1593
1594 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1596
1597 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1599 Self::record_subpool_update(&mut updates, tx);
1601
1602 let mut has_parked_ancestor = !tx.state.is_pending();
1604
1605 let mut cumulative_cost = tx.next_cumulative_cost();
1606
1607 let mut next_nonce_in_line = tx.transaction.nonce().saturating_add(1);
1609
1610 while let Some((peek, tx)) = iter.peek_mut() {
1612 if peek.sender != id.sender {
1613 continue 'transactions
1615 }
1616
1617 if tx.transaction.nonce() == next_nonce_in_line {
1618 tx.state.insert(TxState::NO_NONCE_GAPS);
1620 } else {
1621 next_sender!(iter);
1623 continue 'transactions
1624 }
1625
1626 next_nonce_in_line = next_nonce_in_line.saturating_add(1);
1628
1629 tx.cumulative_cost = cumulative_cost;
1631 cumulative_cost = tx.next_cumulative_cost();
1633
1634 if let Some(changed_balance) = changed_balance {
1636 if &cumulative_cost > changed_balance {
1637 tx.state.remove(TxState::ENOUGH_BALANCE);
1639 } else {
1640 tx.state.insert(TxState::ENOUGH_BALANCE);
1641 }
1642 }
1643
1644 if has_parked_ancestor {
1646 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1647 } else {
1648 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1649 }
1650 has_parked_ancestor = !tx.state.is_pending();
1651
1652 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1654 Self::record_subpool_update(&mut updates, tx);
1655
1656 iter.next();
1658 }
1659 }
1660
1661 updates
1662 }
1663
1664 fn record_subpool_update(updates: &mut Vec<PoolUpdate>, tx: &mut PoolInternalTransaction<T>) {
1669 let current_pool = tx.subpool;
1670 tx.subpool = tx.state.into();
1671 if current_pool != tx.subpool {
1672 updates.push(PoolUpdate {
1673 id: *tx.transaction.id(),
1674 current: current_pool,
1675 destination: tx.subpool.into(),
1676 })
1677 }
1678 }
1679
1680 fn update_tx_base_fee(pending_block_base_fee: u64, tx: &mut PoolInternalTransaction<T>) {
1682 match tx.transaction.max_fee_per_gas().cmp(&(pending_block_base_fee as u128)) {
1684 Ordering::Greater | Ordering::Equal => {
1685 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1686 }
1687 Ordering::Less => {
1688 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
1689 }
1690 }
1691 }
1692
1693 pub(crate) fn txs_iter(
1696 &self,
1697 sender: SenderId,
1698 ) -> impl Iterator<Item = (&TransactionId, &PoolInternalTransaction<T>)> + '_ {
1699 self.txs
1700 .range((sender.start_bound(), Unbounded))
1701 .take_while(move |(other, _)| sender == other.sender)
1702 }
1703
1704 #[cfg(test)]
1707 #[expect(dead_code)]
1708 pub(crate) fn txs_iter_mut(
1709 &mut self,
1710 sender: SenderId,
1711 ) -> impl Iterator<Item = (&TransactionId, &mut PoolInternalTransaction<T>)> + '_ {
1712 self.txs
1713 .range_mut((sender.start_bound(), Unbounded))
1714 .take_while(move |(other, _)| sender == other.sender)
1715 }
1716
1717 pub(crate) fn descendant_txs_exclusive<'a, 'b: 'a>(
1721 &'a self,
1722 id: &'b TransactionId,
1723 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1724 self.txs.range((Excluded(id), Unbounded)).take_while(|(other, _)| id.sender == other.sender)
1725 }
1726
1727 pub(crate) fn descendant_txs_inclusive<'a, 'b: 'a>(
1732 &'a self,
1733 id: &'b TransactionId,
1734 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1735 self.txs.range(id..).take_while(|(other, _)| id.sender == other.sender)
1736 }
1737
1738 pub(crate) fn descendant_txs_mut<'a, 'b: 'a>(
1743 &'a mut self,
1744 id: &'b TransactionId,
1745 ) -> impl Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)> + 'a {
1746 self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender)
1747 }
1748
1749 pub(crate) fn remove_transaction_by_hash(
1751 &mut self,
1752 tx_hash: &B256,
1753 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1754 let tx = self.by_hash.remove(tx_hash)?;
1755 let internal = self.txs.remove(&tx.transaction_id)?;
1756 self.remove_auths(&internal);
1757 self.tx_decr(tx.sender_id());
1759 Some((tx, internal.subpool))
1760 }
1761
1762 pub(crate) fn remove_transaction_by_id(
1766 &mut self,
1767 tx_id: &TransactionId,
1768 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1769 let internal = self.txs.remove(tx_id)?;
1770 let tx = self.by_hash.remove(internal.transaction.hash())?;
1771 self.remove_auths(&internal);
1772 self.tx_decr(tx.sender_id());
1774 Some((tx, internal.subpool))
1775 }
1776
1777 pub(crate) fn park_descendant_transactions(
1779 &mut self,
1780 tx_id: &TransactionId,
1781 ) -> Vec<PoolUpdate> {
1782 let mut updates = Vec::new();
1783
1784 for (id, tx) in self.descendant_txs_mut(tx_id) {
1785 let current_pool = tx.subpool;
1786
1787 tx.state.remove(TxState::NO_NONCE_GAPS);
1788
1789 tx.subpool = tx.state.into();
1791
1792 if current_pool != tx.subpool {
1794 updates.push(PoolUpdate {
1795 id: *id,
1796 current: current_pool,
1797 destination: tx.subpool.into(),
1798 })
1799 }
1800 }
1801
1802 updates
1803 }
1804
1805 pub(crate) fn remove_transaction(
1811 &mut self,
1812 id: &TransactionId,
1813 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1814 let internal = self.txs.remove(id)?;
1815
1816 self.tx_decr(internal.transaction.sender_id());
1818
1819 let result =
1820 self.by_hash.remove(internal.transaction.hash()).map(|tx| (tx, internal.subpool));
1821
1822 self.remove_auths(&internal);
1823
1824 result
1825 }
1826
1827 fn remove_auths(&mut self, tx: &PoolInternalTransaction<T>) {
1831 let Some(auths) = &tx.transaction.authority_ids else { return };
1832
1833 let tx_hash = tx.transaction.hash();
1834 for auth in auths {
1835 if let Some(list) = self.auths.get_mut(auth) {
1836 list.remove(tx_hash);
1837 if list.is_empty() {
1838 self.auths.remove(auth);
1839 }
1840 }
1841 }
1842 }
1843
1844 #[inline]
1850 fn contains_conflicting_transaction(&self, tx: &ValidPoolTransaction<T>) -> bool {
1851 self.txs_iter(tx.transaction_id.sender)
1852 .next()
1853 .is_some_and(|(_, existing)| tx.tx_type_conflicts_with(&existing.transaction))
1854 }
1855
1856 fn ensure_valid(
1865 &self,
1866 transaction: ValidPoolTransaction<T>,
1867 on_chain_nonce: u64,
1868 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1869 if !self.local_transactions_config.is_local(transaction.origin, transaction.sender_ref()) {
1870 let current_txs =
1871 self.tx_counter.get(&transaction.sender_id()).copied().unwrap_or_default();
1872
1873 if current_txs >= self.max_account_slots && transaction.nonce() > on_chain_nonce {
1876 return Err(InsertErr::ExceededSenderTransactionsCapacity {
1877 transaction: Arc::new(transaction),
1878 })
1879 }
1880 }
1881 if transaction.gas_limit() > self.block_gas_limit {
1882 return Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas {
1883 block_gas_limit: self.block_gas_limit,
1884 tx_gas_limit: transaction.gas_limit(),
1885 transaction: Arc::new(transaction),
1886 })
1887 }
1888
1889 if self.contains_conflicting_transaction(&transaction) {
1890 return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) })
1892 }
1893
1894 Ok(transaction)
1895 }
1896
1897 fn ensure_valid_blob_transaction(
1903 &self,
1904 new_blob_tx: ValidPoolTransaction<T>,
1905 on_chain_balance: U256,
1906 ancestor: Option<TransactionId>,
1907 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1908 if let Some(ancestor) = ancestor {
1909 let Some(ancestor_tx) = self.txs.get(&ancestor) else {
1910 self.metrics.blob_transactions_nonce_gaps.increment(1);
1912 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1913 };
1914 if ancestor_tx.state.has_nonce_gap() {
1915 self.metrics.blob_transactions_nonce_gaps.increment(1);
1918 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1919 }
1920
1921 let mut cumulative_cost = ancestor_tx.next_cumulative_cost() + new_blob_tx.cost();
1923
1924 if cumulative_cost > on_chain_balance {
1926 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1928 }
1929
1930 let id = new_blob_tx.transaction_id;
1933 let mut descendants = self.descendant_txs_inclusive(&id).peekable();
1934 if let Some((maybe_replacement, _)) = descendants.peek() &&
1935 **maybe_replacement == new_blob_tx.transaction_id
1936 {
1937 descendants.next();
1939
1940 for (_, tx) in descendants {
1942 cumulative_cost += tx.transaction.cost();
1943 if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance {
1944 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1946 }
1947 }
1948 }
1949 } else if new_blob_tx.cost() > &on_chain_balance {
1950 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1952 }
1953
1954 Ok(new_blob_tx)
1955 }
1956
1957 pub(crate) fn insert_tx(
1989 &mut self,
1990 transaction: ValidPoolTransaction<T>,
1991 on_chain_balance: U256,
1992 on_chain_nonce: u64,
1993 ) -> InsertResult<T> {
1994 assert!(on_chain_nonce <= transaction.nonce(), "Invalid transaction");
1995
1996 let mut transaction = self.ensure_valid(transaction, on_chain_nonce)?;
1997
1998 let inserted_tx_id = *transaction.id();
1999 let mut state = TxState::default();
2000 let mut cumulative_cost = U256::ZERO;
2001 let mut updates = Vec::new();
2002
2003 state.insert(TxState::NOT_TOO_MUCH_GAS);
2005
2006 let ancestor = TransactionId::ancestor(
2009 transaction.transaction.nonce(),
2010 on_chain_nonce,
2011 inserted_tx_id.sender,
2012 );
2013
2014 if transaction.is_eip4844() {
2017 state.insert(TxState::BLOB_TRANSACTION);
2018
2019 transaction =
2020 self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
2021 let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();
2022 if blob_fee_cap >= self.pending_fees.blob_fee {
2023 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
2024 }
2025 } else {
2026 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
2028 }
2029
2030 let transaction = Arc::new(transaction);
2031
2032 if ancestor.is_none() {
2034 state.insert(TxState::NO_NONCE_GAPS);
2035 state.insert(TxState::NO_PARKED_ANCESTORS);
2036 }
2037
2038 let fee_cap = transaction.max_fee_per_gas();
2040
2041 if fee_cap < self.minimal_protocol_basefee as u128 {
2042 return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap })
2043 }
2044 if fee_cap >= self.pending_fees.base_fee as u128 {
2045 state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
2046 }
2047
2048 let mut replaced_tx = None;
2050
2051 let pool_tx = PoolInternalTransaction {
2052 transaction: Arc::clone(&transaction),
2053 subpool: state.into(),
2054 state,
2055 cumulative_cost,
2056 };
2057
2058 match self.txs.entry(*transaction.id()) {
2060 Entry::Vacant(entry) => {
2061 self.by_hash.insert(*pool_tx.transaction.hash(), pool_tx.transaction.clone());
2063 entry.insert(pool_tx);
2064 }
2065 Entry::Occupied(mut entry) => {
2066 let existing_transaction = entry.get().transaction.as_ref();
2068 let maybe_replacement = transaction.as_ref();
2069
2070 if existing_transaction.is_underpriced(maybe_replacement, &self.price_bumps) {
2072 return Err(InsertErr::Underpriced {
2073 transaction: pool_tx.transaction,
2074 existing: *entry.get().transaction.hash(),
2075 })
2076 }
2077 let new_hash = *pool_tx.transaction.hash();
2078 let new_transaction = pool_tx.transaction.clone();
2079 let replaced = entry.insert(pool_tx);
2080 self.by_hash.remove(replaced.transaction.hash());
2081 self.by_hash.insert(new_hash, new_transaction);
2082
2083 self.remove_auths(&replaced);
2084
2085 replaced_tx = Some((replaced.transaction, replaced.subpool));
2087 }
2088 }
2089
2090 if let Some(auths) = &transaction.authority_ids {
2091 let tx_hash = transaction.hash();
2092 for auth in auths {
2093 self.auths.entry(*auth).or_default().insert(*tx_hash);
2094 }
2095 }
2096
2097 let on_chain_id = TransactionId::new(transaction.sender_id(), on_chain_nonce);
2099 {
2100 let mut next_nonce = on_chain_id.nonce;
2102
2103 let mut has_parked_ancestor = false;
2107
2108 for (id, tx) in self.descendant_txs_mut(&on_chain_id) {
2111 let current_pool = tx.subpool;
2112
2113 if next_nonce != id.nonce {
2115 break
2116 }
2117
2118 tx.state.insert(TxState::NO_NONCE_GAPS);
2120
2121 tx.cumulative_cost = cumulative_cost;
2123
2124 cumulative_cost = tx.next_cumulative_cost();
2126
2127 if cumulative_cost > on_chain_balance {
2128 tx.state.remove(TxState::ENOUGH_BALANCE);
2130 } else {
2131 tx.state.insert(TxState::ENOUGH_BALANCE);
2132 }
2133
2134 if has_parked_ancestor {
2136 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
2137 } else {
2138 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
2139 }
2140 has_parked_ancestor = !tx.state.is_pending();
2141
2142 tx.subpool = tx.state.into();
2144
2145 if inserted_tx_id.eq(id) {
2146 state = tx.state;
2148 } else {
2149 if current_pool != tx.subpool {
2151 updates.push(PoolUpdate {
2152 id: *id,
2153 current: current_pool,
2154 destination: tx.subpool.into(),
2155 })
2156 }
2157 }
2158
2159 next_nonce = id.next_nonce();
2161 }
2162 }
2163
2164 if replaced_tx.is_none() {
2166 self.tx_inc(inserted_tx_id.sender);
2167 }
2168
2169 self.update_size_metrics();
2170
2171 Ok(InsertOk { transaction, move_to: state.into(), state, replaced_tx, updates })
2172 }
2173
2174 pub(crate) fn len(&self) -> usize {
2176 self.txs.len()
2177 }
2178
2179 pub(crate) fn is_empty(&self) -> bool {
2181 self.txs.is_empty()
2182 }
2183
2184 #[cfg(any(test, feature = "test-utils"))]
2186 pub(crate) fn assert_invariants(&self) {
2187 assert_eq!(self.by_hash.len(), self.txs.len(), "by_hash.len() != txs.len()");
2188 assert!(self.auths.len() <= self.txs.len(), "auths.len() > txs.len()");
2189 }
2190}
2191
2192#[cfg(test)]
2193impl<T: PoolTransaction> AllTransactions<T> {
2194 pub(crate) fn tx_count(&self, sender: SenderId) -> usize {
2198 self.tx_counter.get(&sender).copied().unwrap_or_default()
2199 }
2200}
2201
2202impl<T: PoolTransaction> Default for AllTransactions<T> {
2203 fn default() -> Self {
2204 Self {
2205 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
2206 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
2207 block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
2208 by_hash: Default::default(),
2209 txs: Default::default(),
2210 sender_info: Default::default(),
2211 tx_counter: Default::default(),
2212 last_seen_block_number: Default::default(),
2213 last_seen_block_hash: Default::default(),
2214 pending_fees: Default::default(),
2215 price_bumps: Default::default(),
2216 local_transactions_config: Default::default(),
2217 auths: Default::default(),
2218 metrics: Default::default(),
2219 }
2220 }
2221}
2222
2223#[derive(Debug, Clone)]
2225pub(crate) struct PendingFees {
2226 pub(crate) base_fee: u64,
2228 pub(crate) blob_fee: u128,
2230}
2231
2232impl Default for PendingFees {
2233 fn default() -> Self {
2234 Self { base_fee: Default::default(), blob_fee: BLOB_TX_MIN_BLOB_GASPRICE }
2235 }
2236}
2237
2238pub(crate) type InsertResult<T> = Result<InsertOk<T>, InsertErr<T>>;
2240
2241#[derive(Debug)]
2243pub(crate) enum InsertErr<T: PoolTransaction> {
2244 Underpriced {
2246 transaction: Arc<ValidPoolTransaction<T>>,
2247 #[expect(dead_code)]
2248 existing: TxHash,
2249 },
2250 BlobTxHasNonceGap { transaction: Arc<ValidPoolTransaction<T>> },
2252 Overdraft { transaction: Arc<ValidPoolTransaction<T>> },
2255 FeeCapBelowMinimumProtocolFeeCap { transaction: Arc<ValidPoolTransaction<T>>, fee_cap: u128 },
2259 ExceededSenderTransactionsCapacity { transaction: Arc<ValidPoolTransaction<T>> },
2263 TxGasLimitMoreThanAvailableBlockGas {
2265 transaction: Arc<ValidPoolTransaction<T>>,
2266 block_gas_limit: u64,
2267 tx_gas_limit: u64,
2268 },
2269 TxTypeConflict { transaction: Arc<ValidPoolTransaction<T>> },
2271}
2272
2273#[derive(Debug)]
2275pub(crate) struct InsertOk<T: PoolTransaction> {
2276 transaction: Arc<ValidPoolTransaction<T>>,
2278 move_to: SubPool,
2280 state: TxState,
2282 replaced_tx: Option<(Arc<ValidPoolTransaction<T>>, SubPool)>,
2284 updates: Vec<PoolUpdate>,
2286}
2287
2288#[derive(Debug)]
2291pub(crate) struct PoolInternalTransaction<T: PoolTransaction> {
2292 pub(crate) transaction: Arc<ValidPoolTransaction<T>>,
2294 pub(crate) subpool: SubPool,
2296 pub(crate) state: TxState,
2299 pub(crate) cumulative_cost: U256,
2304}
2305
2306impl<T: PoolTransaction> PoolInternalTransaction<T> {
2309 fn next_cumulative_cost(&self) -> U256 {
2310 self.cumulative_cost + self.transaction.cost()
2311 }
2312}
2313
2314#[derive(Debug, Clone, Default)]
2316pub(crate) struct SenderInfo {
2317 pub(crate) state_nonce: u64,
2319 pub(crate) balance: U256,
2321}
2322
2323impl SenderInfo {
2326 const fn update(&mut self, state_nonce: u64, balance: U256) {
2328 *self = Self { state_nonce, balance };
2329 }
2330}
2331
2332#[cfg(test)]
2333mod tests {
2334 use super::*;
2335 use crate::{
2336 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
2337 traits::TransactionOrigin,
2338 SubPoolLimit,
2339 };
2340 use alloy_consensus::{Transaction, TxType};
2341 use alloy_primitives::address;
2342
2343 #[test]
2344 fn test_insert_blob() {
2345 let on_chain_balance = U256::MAX;
2346 let on_chain_nonce = 0;
2347 let mut f = MockTransactionFactory::default();
2348 let mut pool = AllTransactions::default();
2349 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2350 let valid_tx = f.validated(tx);
2351 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2352 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2353 assert!(updates.is_empty());
2354 assert!(replaced_tx.is_none());
2355 assert!(state.contains(TxState::NO_NONCE_GAPS));
2356 assert!(state.contains(TxState::ENOUGH_BALANCE));
2357 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2358 assert_eq!(move_to, SubPool::Pending);
2359
2360 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2361 assert_eq!(inserted.subpool, SubPool::Pending);
2362 }
2363
2364 #[test]
2365 fn test_insert_blob_not_enough_blob_fee() {
2366 let on_chain_balance = U256::MAX;
2367 let on_chain_nonce = 0;
2368 let mut f = MockTransactionFactory::default();
2369 let mut pool = AllTransactions {
2370 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2371 ..Default::default()
2372 };
2373 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2374 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2375 let valid_tx = f.validated(tx);
2376 let InsertOk { state, .. } =
2377 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2378 assert!(state.contains(TxState::NO_NONCE_GAPS));
2379 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2380
2381 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2382 }
2383
2384 #[test]
2385 fn test_valid_tx_with_decreasing_blob_fee() {
2386 let on_chain_balance = U256::MAX;
2387 let on_chain_nonce = 0;
2388 let mut f = MockTransactionFactory::default();
2389 let mut pool = AllTransactions {
2390 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2391 ..Default::default()
2392 };
2393 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2394
2395 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2396 let valid_tx = f.validated(tx.clone());
2397 let InsertOk { state, .. } =
2398 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2399 assert!(state.contains(TxState::NO_NONCE_GAPS));
2400 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2401
2402 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2403 pool.remove_transaction(&valid_tx.transaction_id);
2404
2405 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
2406 let InsertOk { state, .. } =
2407 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2408 assert!(state.contains(TxState::NO_NONCE_GAPS));
2409 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2410 }
2411
2412 #[test]
2413 fn test_demote_valid_tx_with_increasing_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 = TxPool::new(MockOrdering::default(), Default::default());
2418 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2419
2420 let mut block_info = pool.block_info();
2422 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2423 pool.set_block_info(block_info);
2424
2425 let validated = f.validated(tx.clone());
2426 let id = *validated.id();
2427 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2428
2429 assert!(pool.blob_pool.is_empty());
2431 assert_eq!(pool.pending_pool.len(), 1);
2432
2433 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2435 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2436 assert_eq!(internal_tx.subpool, SubPool::Pending);
2437
2438 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2440 pool.set_block_info(block_info);
2441
2442 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2444 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2445 assert_eq!(internal_tx.subpool, SubPool::Blob);
2446
2447 assert_eq!(pool.blob_pool.len(), 1);
2449 assert!(pool.pending_pool.is_empty());
2450 }
2451
2452 #[test]
2453 fn test_promote_valid_tx_with_decreasing_blob_fee() {
2454 let on_chain_balance = U256::MAX;
2455 let on_chain_nonce = 0;
2456 let mut f = MockTransactionFactory::default();
2457 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2458 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2459
2460 let mut block_info = pool.block_info();
2462 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2463 pool.set_block_info(block_info);
2464
2465 let validated = f.validated(tx.clone());
2466 let id = *validated.id();
2467 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2468
2469 assert!(pool.pending_pool.is_empty());
2471 assert_eq!(pool.blob_pool.len(), 1);
2472
2473 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2475 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2476 assert_eq!(internal_tx.subpool, SubPool::Blob);
2477
2478 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2480 pool.set_block_info(block_info);
2481
2482 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2484 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2485 assert_eq!(internal_tx.subpool, SubPool::Pending);
2486
2487 assert_eq!(pool.pending_pool.len(), 1);
2489 assert!(pool.blob_pool.is_empty());
2490 }
2491
2492 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
2494 struct PromotionTest {
2495 basefee: u64,
2497 blobfee: u128,
2499 subpool: SubPool,
2501 basefee_update: u64,
2503 blobfee_update: u128,
2505 new_subpool: SubPool,
2507 }
2508
2509 impl PromotionTest {
2510 const fn opposite(&self) -> Self {
2512 Self {
2513 basefee: self.basefee_update,
2514 blobfee: self.blobfee_update,
2515 subpool: self.new_subpool,
2516 blobfee_update: self.blobfee,
2517 basefee_update: self.basefee,
2518 new_subpool: self.subpool,
2519 }
2520 }
2521
2522 fn assert_subpool_lengths<T: TransactionOrdering>(
2523 &self,
2524 pool: &TxPool<T>,
2525 failure_message: String,
2526 check_subpool: SubPool,
2527 ) {
2528 match check_subpool {
2529 SubPool::Blob => {
2530 assert_eq!(pool.blob_pool.len(), 1, "{failure_message}");
2531 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2532 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2533 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2534 }
2535 SubPool::Pending => {
2536 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2537 assert_eq!(pool.pending_pool.len(), 1, "{failure_message}");
2538 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2539 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2540 }
2541 SubPool::BaseFee => {
2542 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2543 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2544 assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}");
2545 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2546 }
2547 SubPool::Queued => {
2548 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2549 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2550 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2551 assert_eq!(pool.queued_pool.len(), 1, "{failure_message}");
2552 }
2553 }
2554 }
2555
2556 fn assert_single_tx_starting_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2560 self.assert_subpool_lengths(
2561 pool,
2562 format!("pool length check failed at start of test: {self:?}"),
2563 self.subpool,
2564 );
2565 }
2566
2567 fn assert_single_tx_ending_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2571 self.assert_subpool_lengths(
2572 pool,
2573 format!("pool length check failed at end of test: {self:?}"),
2574 self.new_subpool,
2575 );
2576 }
2577 }
2578
2579 #[test]
2580 fn test_promote_blob_tx_with_both_pending_fee_updates() {
2581 let on_chain_balance = U256::MAX;
2584 let on_chain_nonce = 0;
2585 let mut f = MockTransactionFactory::default();
2586 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2587
2588 let max_fee_per_blob_gas = tx.max_fee_per_blob_gas().unwrap();
2589 let max_fee_per_gas = tx.max_fee_per_gas() as u64;
2590
2591 let mut expected_promotions = vec![
2593 PromotionTest {
2594 blobfee: max_fee_per_blob_gas + 1,
2595 basefee: max_fee_per_gas + 1,
2596 subpool: SubPool::Blob,
2597 blobfee_update: max_fee_per_blob_gas + 1,
2598 basefee_update: max_fee_per_gas + 1,
2599 new_subpool: SubPool::Blob,
2600 },
2601 PromotionTest {
2602 blobfee: max_fee_per_blob_gas + 1,
2603 basefee: max_fee_per_gas + 1,
2604 subpool: SubPool::Blob,
2605 blobfee_update: max_fee_per_blob_gas,
2606 basefee_update: max_fee_per_gas + 1,
2607 new_subpool: SubPool::Blob,
2608 },
2609 PromotionTest {
2610 blobfee: max_fee_per_blob_gas + 1,
2611 basefee: max_fee_per_gas + 1,
2612 subpool: SubPool::Blob,
2613 blobfee_update: max_fee_per_blob_gas + 1,
2614 basefee_update: max_fee_per_gas,
2615 new_subpool: SubPool::Blob,
2616 },
2617 PromotionTest {
2618 blobfee: max_fee_per_blob_gas + 1,
2619 basefee: max_fee_per_gas + 1,
2620 subpool: SubPool::Blob,
2621 blobfee_update: max_fee_per_blob_gas,
2622 basefee_update: max_fee_per_gas,
2623 new_subpool: SubPool::Pending,
2624 },
2625 PromotionTest {
2626 blobfee: max_fee_per_blob_gas,
2627 basefee: max_fee_per_gas + 1,
2628 subpool: SubPool::Blob,
2629 blobfee_update: max_fee_per_blob_gas,
2630 basefee_update: max_fee_per_gas,
2631 new_subpool: SubPool::Pending,
2632 },
2633 PromotionTest {
2634 blobfee: max_fee_per_blob_gas + 1,
2635 basefee: max_fee_per_gas,
2636 subpool: SubPool::Blob,
2637 blobfee_update: max_fee_per_blob_gas,
2638 basefee_update: max_fee_per_gas,
2639 new_subpool: SubPool::Pending,
2640 },
2641 PromotionTest {
2642 blobfee: max_fee_per_blob_gas,
2643 basefee: max_fee_per_gas,
2644 subpool: SubPool::Pending,
2645 blobfee_update: max_fee_per_blob_gas,
2646 basefee_update: max_fee_per_gas,
2647 new_subpool: SubPool::Pending,
2648 },
2649 ];
2650
2651 let reversed = expected_promotions.iter().map(|test| test.opposite()).collect::<Vec<_>>();
2653 expected_promotions.extend(reversed);
2654
2655 let expected_promotions = expected_promotions.into_iter().collect::<HashSet<_>>();
2657
2658 for promotion_test in &expected_promotions {
2659 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2660
2661 let mut block_info = pool.block_info();
2663
2664 block_info.pending_blob_fee = Some(promotion_test.blobfee);
2665 block_info.pending_basefee = promotion_test.basefee;
2666 pool.set_block_info(block_info);
2667
2668 let validated = f.validated(tx.clone());
2669 let id = *validated.id();
2670 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2671
2672 promotion_test.assert_single_tx_starting_subpool(&pool);
2674
2675 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2677 assert_eq!(
2678 internal_tx.subpool, promotion_test.subpool,
2679 "Subpools do not match at start of test: {promotion_test:?}"
2680 );
2681
2682 block_info.pending_basefee = promotion_test.basefee_update;
2684 block_info.pending_blob_fee = Some(promotion_test.blobfee_update);
2685 pool.set_block_info(block_info);
2686
2687 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2689 assert_eq!(
2690 internal_tx.subpool, promotion_test.new_subpool,
2691 "Subpools do not match at end of test: {promotion_test:?}"
2692 );
2693
2694 promotion_test.assert_single_tx_ending_subpool(&pool);
2696 }
2697 }
2698
2699 #[test]
2700 fn test_insert_pending() {
2701 let on_chain_balance = U256::MAX;
2702 let on_chain_nonce = 0;
2703 let mut f = MockTransactionFactory::default();
2704 let mut pool = AllTransactions::default();
2705 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2706 let valid_tx = f.validated(tx);
2707 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2708 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2709 assert!(updates.is_empty());
2710 assert!(replaced_tx.is_none());
2711 assert!(state.contains(TxState::NO_NONCE_GAPS));
2712 assert!(state.contains(TxState::ENOUGH_BALANCE));
2713 assert_eq!(move_to, SubPool::Pending);
2714
2715 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2716 assert_eq!(inserted.subpool, SubPool::Pending);
2717 }
2718
2719 #[test]
2720 fn test_simple_insert() {
2721 let on_chain_balance = U256::ZERO;
2722 let on_chain_nonce = 0;
2723 let mut f = MockTransactionFactory::default();
2724 let mut pool = AllTransactions::default();
2725 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2726 tx.set_priority_fee(100);
2727 tx.set_max_fee(100);
2728 let valid_tx = f.validated(tx.clone());
2729 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2730 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2731 assert!(updates.is_empty());
2732 assert!(replaced_tx.is_none());
2733 assert!(state.contains(TxState::NO_NONCE_GAPS));
2734 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2735 assert_eq!(move_to, SubPool::Queued);
2736
2737 assert_eq!(pool.len(), 1);
2738 assert!(pool.contains(valid_tx.hash()));
2739 let expected_state = TxState::ENOUGH_FEE_CAP_BLOCK | TxState::NO_NONCE_GAPS;
2740 let inserted = pool.get(valid_tx.id()).unwrap();
2741 assert!(inserted.state.intersects(expected_state));
2742
2743 let res = pool.insert_tx(valid_tx, on_chain_balance, on_chain_nonce);
2745 res.unwrap_err();
2746 assert_eq!(pool.len(), 1);
2747
2748 let valid_tx = f.validated(tx.next());
2749 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2750 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2751
2752 assert!(updates.is_empty());
2753 assert!(replaced_tx.is_none());
2754 assert!(state.contains(TxState::NO_NONCE_GAPS));
2755 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2756 assert_eq!(move_to, SubPool::Queued);
2757
2758 assert!(pool.contains(valid_tx.hash()));
2759 assert_eq!(pool.len(), 2);
2760 let inserted = pool.get(valid_tx.id()).unwrap();
2761 assert!(inserted.state.intersects(expected_state));
2762 }
2763
2764 #[test]
2765 fn test_on_canonical_state_change_no_double_processing() {
2768 let mut tx_factory = MockTransactionFactory::default();
2769 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2770
2771 let tx = MockTransaction::eip1559().with_gas_price(50).with_gas_limit(30_000);
2773 let sender = tx.sender();
2774
2775 let mut block_info = pool.block_info();
2777 block_info.pending_basefee = 100;
2778 pool.set_block_info(block_info);
2779
2780 let validated = tx_factory.validated(tx);
2781 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2782
2783 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2785
2786 assert_eq!(pool.basefee_pool.len(), 1);
2787 assert_eq!(pool.pending_pool.len(), 0);
2788
2789 block_info.pending_basefee = 40;
2793
2794 let mut changed_senders = FxHashMap::default();
2795 changed_senders.insert(
2796 sender_id,
2797 SenderInfo {
2798 state_nonce: 0,
2799 balance: U256::from(20_000_000), },
2801 );
2802
2803 let outcome = pool.on_canonical_state_change(
2804 block_info,
2805 vec![], changed_senders,
2807 PoolUpdateKind::Commit,
2808 );
2809
2810 assert_eq!(pool.pending_pool.len(), 1, "Transaction should be in pending pool");
2812 assert_eq!(pool.basefee_pool.len(), 0, "Transaction should not be in basefee pool");
2813 assert_eq!(outcome.promoted.len(), 1, "Should report exactly one promotion");
2814 }
2815
2816 #[test]
2817 fn test_canonical_state_change_with_basefee_update_regression() {
2820 let mut tx_factory = MockTransactionFactory::default();
2821 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2822
2823 let sender_balance = U256::from(100_000_000);
2825
2826 let tx1 =
2828 MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000).with_nonce(0);
2829 let sender1 = tx1.sender();
2830
2831 let tx2 =
2833 MockTransaction::eip1559().with_gas_price(55).with_gas_limit(21_000).with_nonce(0);
2834 let sender2 = tx2.sender();
2835
2836 let tx3 =
2838 MockTransaction::eip1559().with_gas_price(45).with_gas_limit(21_000).with_nonce(0);
2839 let sender3 = tx3.sender();
2840
2841 let mut block_info = pool.block_info();
2843 block_info.pending_basefee = 70;
2844 pool.set_block_info(block_info);
2845
2846 let validated1 = tx_factory.validated(tx1);
2848 let validated2 = tx_factory.validated(tx2);
2849 let validated3 = tx_factory.validated(tx3);
2850
2851 pool.add_transaction(validated1, sender_balance, 0, None).unwrap();
2852 pool.add_transaction(validated2, sender_balance, 0, None).unwrap();
2853 pool.add_transaction(validated3, sender_balance, 0, None).unwrap();
2854
2855 let sender1_id = tx_factory.ids.sender_id(&sender1).unwrap();
2856 let sender2_id = tx_factory.ids.sender_id(&sender2).unwrap();
2857 let sender3_id = tx_factory.ids.sender_id(&sender3).unwrap();
2858
2859 assert_eq!(pool.basefee_pool.len(), 3, "All txs should be in basefee pool");
2861 assert_eq!(pool.pending_pool.len(), 0, "No txs should be in pending pool");
2862
2863 block_info.pending_basefee = 50;
2866
2867 let mut changed_senders = FxHashMap::default();
2869 changed_senders.insert(
2870 sender1_id,
2871 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2872 );
2873 changed_senders.insert(
2874 sender2_id,
2875 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2876 );
2877 changed_senders.insert(
2878 sender3_id,
2879 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2880 );
2881
2882 let outcome = pool.on_canonical_state_change(
2883 block_info,
2884 vec![],
2885 changed_senders,
2886 PoolUpdateKind::Commit,
2887 );
2888
2889 assert_eq!(pool.pending_pool.len(), 2, "tx1 and tx2 should be promoted");
2891 assert_eq!(pool.basefee_pool.len(), 1, "tx3 should remain in basefee");
2892
2893 assert_eq!(
2896 outcome.promoted.len(),
2897 2,
2898 "Should report exactly 2 promotions, not double-counted"
2899 );
2900
2901 let promoted_prices: Vec<u128> =
2903 outcome.promoted.iter().map(|tx| tx.max_fee_per_gas()).collect();
2904 assert!(promoted_prices.contains(&60));
2905 assert!(promoted_prices.contains(&55));
2906 }
2907
2908 #[test]
2909 fn test_basefee_decrease_with_empty_senders() {
2910 let mut tx_factory = MockTransactionFactory::default();
2913 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2914
2915 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2917
2918 let mut block_info = pool.block_info();
2920 block_info.pending_basefee = 100;
2921 pool.set_block_info(block_info);
2922
2923 let validated = tx_factory.validated(tx);
2925 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2926
2927 assert_eq!(pool.basefee_pool.len(), 1);
2928 assert_eq!(pool.pending_pool.len(), 0);
2929
2930 block_info.pending_basefee = 50;
2932 let outcome = pool.on_canonical_state_change(
2933 block_info,
2934 vec![],
2935 FxHashMap::default(), PoolUpdateKind::Commit,
2937 );
2938
2939 assert_eq!(pool.pending_pool.len(), 1, "Fee decrease should promote tx");
2941 assert_eq!(pool.basefee_pool.len(), 0);
2942 assert_eq!(outcome.promoted.len(), 1, "Should report promotion from fee update");
2943 }
2944
2945 #[test]
2946 fn test_basefee_decrease_account_makes_unfundable() {
2947 let mut tx_factory = MockTransactionFactory::default();
2950 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2951
2952 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2953 let sender = tx.sender();
2954
2955 let mut block_info = pool.block_info();
2957 block_info.pending_basefee = 100;
2958 pool.set_block_info(block_info);
2959
2960 let validated = tx_factory.validated(tx);
2961 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2962 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2963
2964 assert_eq!(pool.basefee_pool.len(), 1);
2965
2966 block_info.pending_basefee = 50;
2968 let mut changed_senders = FxHashMap::default();
2969 changed_senders.insert(
2970 sender_id,
2971 SenderInfo {
2972 state_nonce: 0,
2973 balance: U256::from(100), },
2975 );
2976
2977 let outcome = pool.on_canonical_state_change(
2978 block_info,
2979 vec![],
2980 changed_senders,
2981 PoolUpdateKind::Commit,
2982 );
2983
2984 assert_eq!(pool.pending_pool.len(), 0, "Unfunded tx should not be in pending");
2986 assert_eq!(pool.basefee_pool.len(), 0, "Tx no longer in basefee pool");
2987 assert_eq!(pool.queued_pool.len(), 1, "Unfunded tx should be in queued pool");
2988
2989 let tx_count = pool.all_transactions.txs.len();
2991 assert_eq!(tx_count, 1, "Transaction should still be in pool (in queued)");
2992
2993 assert_eq!(outcome.promoted.len(), 0, "Should not report promotion");
2994 assert_eq!(outcome.discarded.len(), 0, "Queued tx is not reported as discarded");
2995 }
2996
2997 #[test]
2998 fn insert_already_imported() {
2999 let on_chain_balance = U256::ZERO;
3000 let on_chain_nonce = 0;
3001 let mut f = MockTransactionFactory::default();
3002 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3003 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3004 let tx = f.validated(tx);
3005 pool.add_transaction(tx.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3006 match pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap_err().kind {
3007 PoolErrorKind::AlreadyImported => {}
3008 _ => unreachable!(),
3009 }
3010 }
3011
3012 #[test]
3013 fn insert_replace() {
3014 let on_chain_balance = U256::ZERO;
3015 let on_chain_nonce = 0;
3016 let mut f = MockTransactionFactory::default();
3017 let mut pool = AllTransactions::default();
3018 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3019 let first = f.validated(tx.clone());
3020 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3021 let replacement = f.validated(tx.rng_hash().inc_price());
3022 let InsertOk { updates, replaced_tx, .. } =
3023 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
3024 assert!(updates.is_empty());
3025 let replaced = replaced_tx.unwrap();
3026 assert_eq!(replaced.0.hash(), first.hash());
3027
3028 assert!(!pool.contains(first.hash()));
3030 assert!(pool.contains(replacement.hash()));
3031 assert_eq!(pool.len(), 1);
3032 }
3033
3034 #[test]
3035 fn insert_replace_txpool() {
3036 let on_chain_balance = U256::ZERO;
3037 let on_chain_nonce = 0;
3038 let mut f = MockTransactionFactory::default();
3039 let mut pool = TxPool::mock();
3040
3041 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3042 let first = f.validated(tx.clone());
3043 let first_added =
3044 pool.add_transaction(first, on_chain_balance, on_chain_nonce, None).unwrap();
3045 let replacement = f.validated(tx.rng_hash().inc_price());
3046 let replacement_added = pool
3047 .add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce, None)
3048 .unwrap();
3049
3050 assert!(!pool.contains(first_added.hash()));
3052 assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));
3054
3055 assert!(pool.contains(replacement.hash()));
3056 let size = pool.size();
3057 assert_eq!(size.total, 1);
3058 size.assert_invariants();
3059 }
3060
3061 #[test]
3062 fn insert_replace_underpriced() {
3063 let on_chain_balance = U256::ZERO;
3064 let on_chain_nonce = 0;
3065 let mut f = MockTransactionFactory::default();
3066 let mut pool = AllTransactions::default();
3067 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3068 let first = f.validated(tx.clone());
3069 let _res = pool.insert_tx(first, on_chain_balance, on_chain_nonce);
3070 let mut replacement = f.validated(tx.rng_hash());
3071 replacement.transaction = replacement.transaction.decr_price();
3072 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3073 assert!(matches!(err, InsertErr::Underpriced { .. }));
3074 }
3075
3076 #[test]
3077 fn insert_replace_underpriced_not_enough_bump() {
3078 let on_chain_balance = U256::ZERO;
3079 let on_chain_nonce = 0;
3080 let mut f = MockTransactionFactory::default();
3081 let mut pool = AllTransactions::default();
3082 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3083 tx.set_priority_fee(100);
3084 tx.set_max_fee(100);
3085 let first = f.validated(tx.clone());
3086 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3087 let mut replacement = f.validated(tx.rng_hash().inc_price());
3088
3089 replacement.transaction.set_priority_fee(109);
3091 replacement.transaction.set_max_fee(109);
3092 let err =
3093 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3094 assert!(matches!(err, InsertErr::Underpriced { .. }));
3095 assert!(pool.contains(first.hash()));
3097 assert_eq!(pool.len(), 1);
3098
3099 replacement.transaction.set_priority_fee(110);
3101 replacement.transaction.set_max_fee(109);
3102 let err =
3103 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3104 assert!(matches!(err, InsertErr::Underpriced { .. }));
3105 assert!(pool.contains(first.hash()));
3106 assert_eq!(pool.len(), 1);
3107
3108 replacement.transaction.set_priority_fee(109);
3110 replacement.transaction.set_max_fee(110);
3111 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3112 assert!(matches!(err, InsertErr::Underpriced { .. }));
3113 assert!(pool.contains(first.hash()));
3114 assert_eq!(pool.len(), 1);
3115 }
3116
3117 #[test]
3118 fn insert_replace_underpriced_rounds_up_minimum_bump() {
3119 let on_chain_balance = U256::ZERO;
3120 let on_chain_nonce = 0;
3121 let mut f = MockTransactionFactory::default();
3122 let mut pool = AllTransactions { minimal_protocol_basefee: 0, ..Default::default() };
3123 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3124 tx.set_priority_fee(1);
3125 tx.set_max_fee(1);
3126
3127 let first = f.validated(tx.clone());
3128 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3129
3130 let mut replacement = f.validated(tx.rng_hash().inc_price());
3131 replacement.transaction.set_priority_fee(1);
3132 replacement.transaction.set_max_fee(2);
3133 let err =
3134 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3135 assert!(matches!(err, InsertErr::Underpriced { .. }));
3136 assert!(pool.contains(first.hash()));
3137 assert_eq!(pool.len(), 1);
3138
3139 replacement.transaction.set_priority_fee(2);
3140 replacement.transaction.set_max_fee(2);
3141 let replaced = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap();
3142 assert!(replaced.replaced_tx.is_some());
3143 assert_eq!(pool.len(), 1);
3144 }
3145
3146 #[test]
3147 fn insert_conflicting_type_normal_to_blob() {
3148 let on_chain_balance = U256::from(10_000);
3149 let on_chain_nonce = 0;
3150 let mut f = MockTransactionFactory::default();
3151 let mut pool = AllTransactions::default();
3152 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3153 let first = f.validated(tx.clone());
3154 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3155 let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3156 let blob = f.validated(tx);
3157 let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
3158 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3159 }
3160
3161 #[test]
3162 fn insert_conflicting_type_blob_to_normal() {
3163 let on_chain_balance = U256::from(10_000);
3164 let on_chain_nonce = 0;
3165 let mut f = MockTransactionFactory::default();
3166 let mut pool = AllTransactions::default();
3167 let tx = MockTransaction::eip4844().inc_price().inc_limit();
3168 let first = f.validated(tx.clone());
3169 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3170 let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3171 let tx = f.validated(tx);
3172 let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
3173 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3174 }
3175
3176 #[test]
3178 fn insert_previous() {
3179 let on_chain_balance = U256::ZERO;
3180 let on_chain_nonce = 0;
3181 let mut f = MockTransactionFactory::default();
3182 let mut pool = AllTransactions::default();
3183 let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
3184 let first = f.validated(tx.clone());
3185 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3186
3187 let first_in_pool = pool.get(first.id()).unwrap();
3188
3189 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3191
3192 let prev = f.validated(tx.prev());
3193 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3194 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3195
3196 assert!(updates.is_empty());
3198 assert!(replaced_tx.is_none());
3199 assert!(state.contains(TxState::NO_NONCE_GAPS));
3200 assert_eq!(move_to, SubPool::Queued);
3201
3202 let first_in_pool = pool.get(first.id()).unwrap();
3203 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3205 }
3206
3207 #[test]
3209 fn insert_with_updates() {
3210 let on_chain_balance = U256::from(10_000);
3211 let on_chain_nonce = 0;
3212 let mut f = MockTransactionFactory::default();
3213 let mut pool = AllTransactions::default();
3214 let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
3215 let first = f.validated(tx.clone());
3216 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3217
3218 let first_in_pool = pool.get(first.id()).unwrap();
3219 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3221 assert_eq!(SubPool::Queued, first_in_pool.subpool);
3222
3223 let prev = f.validated(tx.prev());
3224 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3225 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3226
3227 assert_eq!(updates.len(), 1);
3229 assert!(replaced_tx.is_none());
3230 assert!(state.contains(TxState::NO_NONCE_GAPS));
3231 assert_eq!(move_to, SubPool::Pending);
3232
3233 let first_in_pool = pool.get(first.id()).unwrap();
3234 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3236 assert_eq!(SubPool::Pending, first_in_pool.subpool);
3237 }
3238
3239 #[test]
3240 fn insert_previous_blocking() {
3241 let on_chain_balance = U256::from(1_000);
3242 let on_chain_nonce = 0;
3243 let mut f = MockTransactionFactory::default();
3244 let mut pool = AllTransactions::default();
3245 pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
3246 let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
3247 let first = f.validated(tx.clone());
3248
3249 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3250
3251 let first_in_pool = pool.get(first.id()).unwrap();
3252
3253 assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
3254 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3256
3257 let prev = f.validated(tx.prev());
3258 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3259 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3260
3261 assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3262 assert!(updates.is_empty());
3264 assert!(replaced_tx.is_none());
3265 assert!(state.contains(TxState::NO_NONCE_GAPS));
3266 assert_eq!(move_to, SubPool::BaseFee);
3267
3268 let first_in_pool = pool.get(first.id()).unwrap();
3269 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3271 }
3272
3273 #[test]
3274 fn rejects_spammer() {
3275 let on_chain_balance = U256::from(1_000);
3276 let on_chain_nonce = 0;
3277 let mut f = MockTransactionFactory::default();
3278 let mut pool = AllTransactions::default();
3279
3280 let mut tx = MockTransaction::eip1559();
3281 let unblocked_tx = tx.clone();
3282 for _ in 0..pool.max_account_slots {
3283 tx = tx.next();
3284 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
3285 }
3286
3287 assert_eq!(
3288 pool.max_account_slots,
3289 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3290 );
3291
3292 let err =
3293 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
3294 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
3295
3296 assert!(pool
3297 .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
3298 .is_ok());
3299 }
3300
3301 #[test]
3302 fn allow_local_spamming() {
3303 let on_chain_balance = U256::from(1_000);
3304 let on_chain_nonce = 0;
3305 let mut f = MockTransactionFactory::default();
3306 let mut pool = AllTransactions::default();
3307
3308 let mut tx = MockTransaction::eip1559();
3309 for _ in 0..pool.max_account_slots {
3310 tx = tx.next();
3311 pool.insert_tx(
3312 f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
3313 on_chain_balance,
3314 on_chain_nonce,
3315 )
3316 .unwrap();
3317 }
3318
3319 assert_eq!(
3320 pool.max_account_slots,
3321 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3322 );
3323
3324 pool.insert_tx(
3325 f.validated_with_origin(TransactionOrigin::Local, tx.next()),
3326 on_chain_balance,
3327 on_chain_nonce,
3328 )
3329 .unwrap();
3330 }
3331
3332 #[test]
3333 fn reject_tx_over_gas_limit() {
3334 let on_chain_balance = U256::from(1_000);
3335 let on_chain_nonce = 0;
3336 let mut f = MockTransactionFactory::default();
3337 let mut pool = AllTransactions::default();
3338
3339 let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
3340
3341 assert!(matches!(
3342 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
3343 Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
3344 ));
3345 }
3346
3347 #[test]
3348 fn test_tx_equal_gas_limit() {
3349 let on_chain_balance = U256::from(1_000);
3350 let on_chain_nonce = 0;
3351 let mut f = MockTransactionFactory::default();
3352 let mut pool = AllTransactions::default();
3353
3354 let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
3355
3356 let InsertOk { state, .. } =
3357 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
3358 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
3359 }
3360
3361 #[test]
3362 fn update_basefee_subpools() {
3363 let mut f = MockTransactionFactory::default();
3364 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3365
3366 let tx = MockTransaction::eip1559().inc_price_by(10);
3367 let validated = f.validated(tx.clone());
3368 let id = *validated.id();
3369 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3370
3371 assert_eq!(pool.pending_pool.len(), 1);
3372
3373 pool.update_basefee((tx.max_fee_per_gas() + 1) as u64, |_| {});
3374
3375 assert!(pool.pending_pool.is_empty());
3376 assert_eq!(pool.basefee_pool.len(), 1);
3377
3378 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3379 }
3380
3381 #[test]
3382 fn update_basefee_subpools_setting_block_info() {
3383 let mut f = MockTransactionFactory::default();
3384 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3385
3386 let tx = MockTransaction::eip1559().inc_price_by(10);
3387 let validated = f.validated(tx.clone());
3388 let id = *validated.id();
3389 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3390
3391 assert_eq!(pool.pending_pool.len(), 1);
3392
3393 let mut block_info = pool.block_info();
3395 block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
3396 pool.set_block_info(block_info);
3397
3398 assert!(pool.pending_pool.is_empty());
3399 assert_eq!(pool.basefee_pool.len(), 1);
3400
3401 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3402 }
3403
3404 #[test]
3405 fn basefee_decrease_promotes_affordable_and_keeps_unaffordable() {
3406 use alloy_primitives::address;
3407 let mut f = MockTransactionFactory::default();
3408 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3409
3410 let sender_a = address!("0x000000000000000000000000000000000000000a");
3413 let sender_b = address!("0x000000000000000000000000000000000000000b");
3414 let sender_c = address!("0x000000000000000000000000000000000000000c");
3415
3416 let tx1 = MockTransaction::eip1559()
3417 .set_sender(sender_a)
3418 .set_nonce(0)
3419 .set_max_fee(500)
3420 .inc_limit();
3421 let tx2 = MockTransaction::eip1559()
3422 .set_sender(sender_b)
3423 .set_nonce(0)
3424 .set_max_fee(600)
3425 .inc_limit();
3426 let tx3 = MockTransaction::eip1559()
3427 .set_sender(sender_c)
3428 .set_nonce(0)
3429 .set_max_fee(400)
3430 .inc_limit();
3431
3432 let mut block_info = pool.block_info();
3434 block_info.pending_basefee = 700;
3435 pool.set_block_info(block_info);
3436
3437 let validated1 = f.validated(tx1);
3438 let validated2 = f.validated(tx2);
3439 let validated3 = f.validated(tx3);
3440 let id1 = *validated1.id();
3441 let id2 = *validated2.id();
3442 let id3 = *validated3.id();
3443
3444 pool.add_transaction(validated1, U256::from(10_000), 0, None).unwrap();
3448 pool.add_transaction(validated2, U256::from(10_000), 0, None).unwrap();
3449 pool.add_transaction(validated3, U256::from(10_000), 0, None).unwrap();
3450
3451 println!("Basefee pool len: {}", pool.basefee_pool.len());
3453 println!("Pending pool len: {}", pool.pending_pool.len());
3454 println!("tx1 subpool: {:?}", pool.all_transactions.txs.get(&id1).unwrap().subpool);
3455 println!("tx2 subpool: {:?}", pool.all_transactions.txs.get(&id2).unwrap().subpool);
3456 println!("tx3 subpool: {:?}", pool.all_transactions.txs.get(&id3).unwrap().subpool);
3457
3458 assert_eq!(pool.basefee_pool.len(), 3);
3460 assert_eq!(pool.pending_pool.len(), 0);
3461 assert_eq!(pool.all_transactions.txs.get(&id1).unwrap().subpool, SubPool::BaseFee);
3462 assert_eq!(pool.all_transactions.txs.get(&id2).unwrap().subpool, SubPool::BaseFee);
3463 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3464
3465 let mut block_info = pool.block_info();
3467 block_info.pending_basefee = 450; pool.set_block_info(block_info);
3469
3470 assert_eq!(pool.basefee_pool.len(), 1);
3475 assert_eq!(pool.pending_pool.len(), 2);
3476
3477 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3479
3480 let tx1_meta = pool.all_transactions.txs.get(&id1).unwrap();
3482 let tx2_meta = pool.all_transactions.txs.get(&id2).unwrap();
3483 assert_eq!(tx1_meta.subpool, SubPool::Pending);
3484 assert_eq!(tx2_meta.subpool, SubPool::Pending);
3485 assert!(tx1_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3486 assert!(tx2_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3487
3488 let best: Vec<_> = pool.best_transactions().take(3).collect();
3490 assert_eq!(best.len(), 2); assert!(best.iter().any(|tx| tx.id() == &id1));
3492 assert!(best.iter().any(|tx| tx.id() == &id2));
3493 }
3494
3495 #[test]
3496 fn apply_fee_updates_records_promotions_after_basefee_drop() {
3497 let mut f = MockTransactionFactory::default();
3498 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3499
3500 let tx = MockTransaction::eip1559()
3501 .with_gas_limit(21_000)
3502 .with_max_fee(500)
3503 .with_priority_fee(1);
3504 let validated = f.validated(tx);
3505 let id = *validated.id();
3506 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3507
3508 assert_eq!(pool.pending_pool.len(), 1);
3509
3510 pool.update_basefee(600, |_| {});
3512 assert!(pool.pending_pool.is_empty());
3513 assert_eq!(pool.basefee_pool.len(), 1);
3514
3515 let prev_base_fee = 600;
3516 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3517
3518 pool.all_transactions.pending_fees.base_fee = 400;
3520
3521 let mut outcome = UpdateOutcome::default();
3522 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3523
3524 assert_eq!(pool.pending_pool.len(), 1);
3525 assert!(pool.basefee_pool.is_empty());
3526 assert_eq!(outcome.promoted.len(), 1);
3527 assert_eq!(outcome.promoted[0].id(), &id);
3528 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3529 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3530
3531 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3532 assert_eq!(tx_meta.subpool, SubPool::Pending);
3533 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3534 }
3535
3536 #[test]
3537 fn apply_fee_updates_records_promotions_after_blob_fee_drop() {
3538 let mut f = MockTransactionFactory::default();
3539 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3540
3541 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3542
3543 let tx = MockTransaction::eip4844().with_blob_fee(initial_blob_fee + 100);
3544 let validated = f.validated(tx.clone());
3545 let id = *validated.id();
3546 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3547
3548 assert_eq!(pool.pending_pool.len(), 1);
3549
3550 let increased_blob_fee = tx.max_fee_per_blob_gas().unwrap() + 200;
3552 pool.update_blob_fee(increased_blob_fee, Ordering::Equal, |_| {});
3553 assert!(pool.pending_pool.is_empty());
3554 assert_eq!(pool.blob_pool.len(), 1);
3555
3556 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3557 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3558
3559 pool.all_transactions.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
3561
3562 let mut outcome = UpdateOutcome::default();
3563 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3564
3565 assert_eq!(pool.pending_pool.len(), 1);
3566 assert!(pool.blob_pool.is_empty());
3567 assert_eq!(outcome.promoted.len(), 1);
3568 assert_eq!(outcome.promoted[0].id(), &id);
3569 assert_eq!(pool.all_transactions.pending_fees.base_fee, prev_base_fee);
3570 assert_eq!(pool.all_transactions.pending_fees.blob_fee, tx.max_fee_per_blob_gas().unwrap());
3571
3572 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3573 assert_eq!(tx_meta.subpool, SubPool::Pending);
3574 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3575 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3576 }
3577
3578 #[test]
3579 fn apply_fee_updates_promotes_blob_after_basefee_drop() {
3580 let mut f = MockTransactionFactory::default();
3581 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3582
3583 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3584
3585 let tx = MockTransaction::eip4844()
3586 .with_max_fee(500)
3587 .with_priority_fee(1)
3588 .with_blob_fee(initial_blob_fee + 100);
3589 let validated = f.validated(tx);
3590 let id = *validated.id();
3591 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3592
3593 assert_eq!(pool.pending_pool.len(), 1);
3594
3595 let high_base_fee = 600;
3597 pool.update_basefee(high_base_fee, |_| {});
3598 assert!(pool.pending_pool.is_empty());
3599 assert_eq!(pool.blob_pool.len(), 1);
3600
3601 let prev_base_fee = high_base_fee;
3602 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3603
3604 pool.all_transactions.pending_fees.base_fee = 400;
3606
3607 let mut outcome = UpdateOutcome::default();
3608 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3609
3610 assert_eq!(pool.pending_pool.len(), 1);
3611 assert!(pool.blob_pool.is_empty());
3612 assert_eq!(outcome.promoted.len(), 1);
3613 assert_eq!(outcome.promoted[0].id(), &id);
3614 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3615 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3616
3617 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3618 assert_eq!(tx_meta.subpool, SubPool::Pending);
3619 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3620 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3621 }
3622
3623 #[test]
3624 fn apply_fee_updates_demotes_after_basefee_rise() {
3625 let mut f = MockTransactionFactory::default();
3626 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3627
3628 let tx = MockTransaction::eip1559()
3629 .with_gas_limit(21_000)
3630 .with_max_fee(400)
3631 .with_priority_fee(1);
3632 let validated = f.validated(tx);
3633 let id = *validated.id();
3634 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3635
3636 assert_eq!(pool.pending_pool.len(), 1);
3637
3638 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3639 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3640
3641 let new_base_fee = prev_base_fee + 1_000;
3643 pool.all_transactions.pending_fees.base_fee = new_base_fee;
3644
3645 let mut outcome = UpdateOutcome::default();
3646 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3647
3648 assert!(pool.pending_pool.is_empty());
3649 assert_eq!(pool.basefee_pool.len(), 1);
3650 assert!(outcome.promoted.is_empty());
3651 assert_eq!(pool.all_transactions.pending_fees.base_fee, new_base_fee);
3652 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3653
3654 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3655 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
3656 assert!(!tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3657 }
3658
3659 #[test]
3660 fn get_highest_transaction_by_sender_and_nonce() {
3661 let mut f = MockTransactionFactory::default();
3663 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3664
3665 let tx = MockTransaction::eip1559();
3667 pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0, None).unwrap();
3668
3669 let tx1 = tx.inc_price().next();
3671
3672 let tx1_validated = f.validated(tx1.clone());
3674 pool.add_transaction(tx1_validated, U256::from(1_000), 0, None).unwrap();
3675
3676 assert_eq!(
3678 pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
3679 Some(1)
3680 );
3681
3682 let highest_tx = pool
3684 .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
3685 .expect("Failed to retrieve highest transaction");
3686
3687 assert_eq!(highest_tx.as_ref().transaction, tx1);
3689 }
3690
3691 #[test]
3692 fn get_highest_consecutive_transaction_by_sender() {
3693 let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
3695 let mut f = MockTransactionFactory::default();
3696
3697 let sender = Address::random();
3699 let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
3700 for nonce in txs {
3701 let mut mock_tx = MockTransaction::eip1559();
3702 mock_tx.set_sender(sender);
3703 mock_tx.set_nonce(nonce);
3704
3705 let validated_tx = f.validated(mock_tx);
3706 pool.add_transaction(validated_tx, U256::from(1000), 0, None).unwrap();
3707 }
3708
3709 let sender_id = f.ids.sender_id(&sender).unwrap();
3711 let next_tx =
3712 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
3713 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
3714
3715 let next_tx =
3716 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
3717 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
3718
3719 let next_tx =
3720 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3721 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
3722
3723 let mut info = SenderInfo::default();
3725 info.update(8, U256::ZERO);
3726 pool.all_transactions.sender_info.insert(sender_id, info);
3727 let next_tx =
3728 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3729 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
3730 }
3731
3732 #[test]
3733 fn discard_nonce_too_low() {
3734 let mut f = MockTransactionFactory::default();
3735 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3736
3737 let tx = MockTransaction::eip1559().inc_price_by(10);
3738 let validated = f.validated(tx.clone());
3739 let id = *validated.id();
3740 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3741
3742 let next = tx.next();
3743 let validated = f.validated(next.clone());
3744 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3745
3746 assert_eq!(pool.pending_pool.len(), 2);
3747
3748 let mut changed_senders = HashMap::default();
3749 changed_senders.insert(
3750 id.sender,
3751 SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
3752 );
3753 let outcome = pool.update_accounts(changed_senders);
3754 assert_eq!(outcome.discarded.len(), 1);
3755 assert_eq!(pool.pending_pool.len(), 1);
3756 }
3757
3758 #[test]
3759 fn discard_with_large_blob_txs() {
3760 reth_tracing::init_test_tracing();
3762
3763 let mut f = MockTransactionFactory::default();
3765 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3766 let default_limits = pool.config.blob_limit;
3767
3768 let a_sender = address!("0x000000000000000000000000000000000000000a");
3771
3772 let mut block_info = pool.block_info();
3774 block_info.pending_blob_fee = Some(100);
3775 block_info.pending_basefee = 100;
3776
3777 pool.set_block_info(block_info);
3779
3780 let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
3782 .into_iter()
3783 .map(|mut tx| {
3784 tx.set_size(default_limits.max_size / 2 + 1);
3785 tx.set_max_fee((block_info.pending_basefee - 1).into());
3786 tx
3787 })
3788 .collect::<Vec<_>>();
3789
3790 for tx in a_txs {
3792 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3793 }
3794
3795 let removed = pool.discard_worst();
3797 assert_eq!(removed.len(), 1);
3798 }
3799
3800 #[test]
3801 fn discard_with_parked_large_txs() {
3802 reth_tracing::init_test_tracing();
3804
3805 let mut f = MockTransactionFactory::default();
3807 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3808 let default_limits = pool.config.queued_limit;
3809
3810 let a_sender = address!("0x000000000000000000000000000000000000000a");
3813
3814 let pool_base_fee = 100;
3816 pool.update_basefee(pool_base_fee, |_| {});
3817
3818 let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
3820 .into_iter()
3821 .map(|mut tx| {
3822 tx.set_size(default_limits.max_size / 2 + 1);
3823 tx.set_max_fee((pool_base_fee - 1).into());
3824 tx
3825 })
3826 .collect::<Vec<_>>();
3827
3828 for tx in a_txs {
3830 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3831 }
3832
3833 let removed = pool.discard_worst();
3835 assert_eq!(removed.len(), 1);
3836 }
3837
3838 #[test]
3839 fn discard_at_capacity() {
3840 let mut f = MockTransactionFactory::default();
3841 let queued_limit = SubPoolLimit::new(1000, usize::MAX);
3842 let mut pool =
3843 TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
3844
3845 for _ in 0..queued_limit.max_txs {
3847 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3848 let validated = f.validated(tx.clone());
3849 let _id = *validated.id();
3850 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3851 }
3852
3853 let size = pool.size();
3854 assert_eq!(size.queued, queued_limit.max_txs);
3855
3856 for _ in 0..queued_limit.max_txs {
3857 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3858 let validated = f.validated(tx.clone());
3859 let _id = *validated.id();
3860 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3861
3862 pool.discard_worst();
3863 pool.assert_invariants();
3864 assert!(pool.size().queued <= queued_limit.max_txs);
3865 }
3866 }
3867
3868 #[test]
3869 fn discard_blobs_at_capacity() {
3870 let mut f = MockTransactionFactory::default();
3871 let blob_limit = SubPoolLimit::new(1000, usize::MAX);
3872 let mut pool =
3873 TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
3874 pool.all_transactions.pending_fees.blob_fee = 10000;
3875 for _ in 0..blob_limit.max_txs {
3877 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3878 let validated = f.validated(tx.clone());
3879 let _id = *validated.id();
3880 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3881 }
3882
3883 let size = pool.size();
3884 assert_eq!(size.blob, blob_limit.max_txs);
3885
3886 for _ in 0..blob_limit.max_txs {
3887 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3888 let validated = f.validated(tx.clone());
3889 let _id = *validated.id();
3890 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3891
3892 pool.discard_worst();
3893 pool.assert_invariants();
3894 assert!(pool.size().blob <= blob_limit.max_txs);
3895 }
3896 }
3897
3898 #[test]
3899 fn account_updates_sender_balance() {
3900 let mut on_chain_balance = U256::from(100);
3901 let on_chain_nonce = 0;
3902 let mut f = MockTransactionFactory::default();
3903 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3904
3905 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3906 let tx_1 = tx_0.next();
3907 let tx_2 = tx_1.next();
3908
3909 let v0 = f.validated(tx_0);
3911 let v1 = f.validated(tx_1);
3912 let v2 = f.validated(tx_2);
3913
3914 let _res =
3915 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3916 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3917 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3918
3919 assert_eq!(1, pool.pending_transactions().len());
3921 assert_eq!(2, pool.queued_transactions().len());
3922
3923 let mut updated_accounts = HashMap::default();
3925 on_chain_balance = U256::from(300);
3926 updated_accounts.insert(
3927 v0.sender_id(),
3928 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3929 );
3930 pool.update_accounts(updated_accounts.clone());
3931
3932 assert_eq!(3, pool.pending_transactions().len());
3933 assert!(pool.queued_transactions().is_empty());
3934
3935 updated_accounts.entry(v0.sender_id()).and_modify(|v| v.balance = U256::from(1));
3937 pool.update_accounts(updated_accounts);
3938
3939 assert!(pool.pending_transactions().is_empty());
3940 assert_eq!(3, pool.queued_transactions().len());
3941 }
3942
3943 #[test]
3944 fn account_updates_nonce_gap() {
3945 let on_chain_balance = U256::from(10_000);
3946 let mut on_chain_nonce = 0;
3947 let mut f = MockTransactionFactory::default();
3948 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3949
3950 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3951 let tx_1 = tx_0.next();
3952 let tx_2 = tx_1.next();
3953
3954 let v0 = f.validated(tx_0);
3956 let v1 = f.validated(tx_1);
3957 let v2 = f.validated(tx_2);
3958
3959 let _res =
3961 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3962 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3963
3964 assert!(pool.queued_transactions().is_empty());
3965 assert_eq!(2, pool.pending_transactions().len());
3966
3967 pool.remove_transaction_by_hash(v0.hash());
3969
3970 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3972
3973 assert_eq!(2, pool.queued_transactions().len());
3975 assert!(pool.pending_transactions().is_empty());
3976
3977 let mut updated_accounts = HashMap::default();
3979 on_chain_nonce += 1;
3980 updated_accounts.insert(
3981 v0.sender_id(),
3982 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3983 );
3984 pool.update_accounts(updated_accounts);
3985
3986 assert!(pool.queued_transactions().is_empty());
3988 assert_eq!(2, pool.pending_transactions().len());
3989 }
3990 #[test]
3991 fn test_transaction_removal() {
3992 let on_chain_balance = U256::from(10_000);
3993 let on_chain_nonce = 0;
3994 let mut f = MockTransactionFactory::default();
3995 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3996
3997 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3998 let tx_1 = tx_0.next();
3999
4000 let v0 = f.validated(tx_0);
4002 let v1 = f.validated(tx_1);
4003
4004 let _res =
4006 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4007 let _res =
4008 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4009
4010 assert_eq!(0, pool.queued_transactions().len());
4011 assert_eq!(2, pool.pending_transactions().len());
4012
4013 pool.remove_transaction(v0.id());
4015 let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
4017 assert_eq!(vec![v1.nonce()], pool_txs);
4018 }
4019 #[test]
4020 fn test_remove_transactions() {
4021 let on_chain_balance = U256::from(10_000);
4022 let on_chain_nonce = 0;
4023 let mut f = MockTransactionFactory::default();
4024 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4025
4026 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4027 let tx_1 = tx_0.next();
4028 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4029 let tx_3 = tx_2.next();
4030
4031 let v0 = f.validated(tx_0);
4033 let v1 = f.validated(tx_1);
4034 let v2 = f.validated(tx_2);
4035 let v3 = f.validated(tx_3);
4036
4037 let _res =
4039 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4040 let _res =
4041 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4042 let _res =
4043 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4044 let _res =
4045 pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4046
4047 assert_eq!(0, pool.queued_transactions().len());
4048 assert_eq!(4, pool.pending_transactions().len());
4049
4050 pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
4051
4052 assert_eq!(2, pool.queued_transactions().len());
4053 assert!(pool.pending_transactions().is_empty());
4054 assert!(pool.contains(v1.hash()));
4055 assert!(pool.contains(v3.hash()));
4056 }
4057
4058 #[test]
4059 fn test_remove_transactions_middle_pending_hash() {
4060 let on_chain_balance = U256::from(10_000);
4061 let on_chain_nonce = 0;
4062 let mut f = MockTransactionFactory::default();
4063 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4064
4065 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4066 let tx_1 = tx_0.next();
4067 let tx_2 = tx_1.next();
4068 let tx_3 = tx_2.next();
4069
4070 let v0 = f.validated(tx_0);
4072 let v1 = f.validated(tx_1);
4073 let v2 = f.validated(tx_2);
4074 let v3 = f.validated(tx_3);
4075
4076 let _res = pool.add_transaction(v0, on_chain_balance, on_chain_nonce, None).unwrap();
4078 let _res =
4079 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4080 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4081 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4082
4083 assert_eq!(0, pool.queued_transactions().len());
4084 assert_eq!(4, pool.pending_transactions().len());
4085
4086 let mut removed_txs = pool.remove_transactions(vec![*v1.hash()]);
4087 assert_eq!(1, removed_txs.len());
4088
4089 assert_eq!(2, pool.queued_transactions().len());
4090 assert_eq!(1, pool.pending_transactions().len());
4091
4092 let removed_tx = removed_txs.pop().unwrap();
4094 let v1 = f.validated(removed_tx.transaction.clone());
4095 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4096 assert_eq!(0, pool.queued_transactions().len());
4097 assert_eq!(4, pool.pending_transactions().len());
4098 }
4099
4100 #[test]
4101 fn test_remove_transactions_and_descendants() {
4102 let on_chain_balance = U256::from(10_000);
4103 let on_chain_nonce = 0;
4104 let mut f = MockTransactionFactory::default();
4105 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4106
4107 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4108 let tx_1 = tx_0.next();
4109 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4110 let tx_3 = tx_2.next();
4111 let tx_4 = tx_3.next();
4112
4113 let v0 = f.validated(tx_0);
4115 let v1 = f.validated(tx_1);
4116 let v2 = f.validated(tx_2);
4117 let v3 = f.validated(tx_3);
4118 let v4 = f.validated(tx_4);
4119
4120 let _res =
4122 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4123 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4124 let _res =
4125 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4126 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4127 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4128
4129 assert_eq!(0, pool.queued_transactions().len());
4130 assert_eq!(5, pool.pending_transactions().len());
4131
4132 pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
4133
4134 assert_eq!(0, pool.queued_transactions().len());
4135 assert_eq!(0, pool.pending_transactions().len());
4136 }
4137 #[test]
4138 fn test_remove_descendants() {
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 =
4157 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4158 let _res = pool.add_transaction(v1, 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 = Vec::new();
4166 pool.remove_transaction(v0.id());
4167 pool.remove_descendants(v0.id(), &mut removed);
4168
4169 assert_eq!(0, pool.queued_transactions().len());
4170 assert_eq!(0, pool.pending_transactions().len());
4171 assert_eq!(3, removed.len());
4172 }
4173 #[test]
4174 fn test_remove_transactions_by_sender() {
4175 let on_chain_balance = U256::from(10_000);
4176 let on_chain_nonce = 0;
4177 let mut f = MockTransactionFactory::default();
4178 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4179
4180 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4181 let tx_1 = tx_0.next();
4182 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4183 let tx_3 = tx_2.next();
4184 let tx_4 = tx_3.next();
4185
4186 let v0 = f.validated(tx_0);
4188 let v1 = f.validated(tx_1);
4189 let v2 = f.validated(tx_2);
4190 let v3 = f.validated(tx_3);
4191 let v4 = f.validated(tx_4);
4192
4193 let _res =
4195 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4196 let _res =
4197 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4198 let _res =
4199 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4200 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4201 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4202
4203 assert_eq!(0, pool.queued_transactions().len());
4204 assert_eq!(5, pool.pending_transactions().len());
4205
4206 pool.remove_transactions_by_sender(v2.sender_id());
4207
4208 assert_eq!(0, pool.queued_transactions().len());
4209 assert_eq!(2, pool.pending_transactions().len());
4210 assert!(pool.contains(v0.hash()));
4211 assert!(pool.contains(v1.hash()));
4212 }
4213 #[test]
4214 fn wrong_best_order_of_transactions() {
4215 let on_chain_balance = U256::from(10_000);
4216 let mut on_chain_nonce = 0;
4217 let mut f = MockTransactionFactory::default();
4218 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4219
4220 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4221 let tx_1 = tx_0.next();
4222 let tx_2 = tx_1.next();
4223 let tx_3 = tx_2.next();
4224
4225 let v0 = f.validated(tx_0);
4227 let v1 = f.validated(tx_1);
4228 let v2 = f.validated(tx_2);
4229 let v3 = f.validated(tx_3);
4230
4231 let _res =
4233 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4234 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4235
4236 assert_eq!(0, pool.queued_transactions().len());
4237 assert_eq!(2, pool.pending_transactions().len());
4238
4239 pool.remove_transaction(v0.id());
4241
4242 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4244
4245 assert_eq!(1, pool.queued_transactions().len());
4247 assert_eq!(1, pool.pending_transactions().len());
4248
4249 let mut updated_accounts = HashMap::default();
4251 on_chain_nonce += 1;
4252 updated_accounts.insert(
4253 v0.sender_id(),
4254 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4255 );
4256 pool.update_accounts(updated_accounts);
4257
4258 assert_eq!(0, pool.queued_transactions().len());
4261 assert_eq!(2, pool.pending_transactions().len());
4262
4263 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4265 assert_eq!(0, pool.queued_transactions().len());
4266 assert_eq!(3, pool.pending_transactions().len());
4267
4268 assert_eq!(
4271 pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
4272 vec![1, 2, 3]
4273 );
4274 }
4275
4276 #[test]
4277 fn test_best_with_attributes() {
4278 let on_chain_balance = U256::MAX;
4279 let on_chain_nonce = 0;
4280 let mut f = MockTransactionFactory::default();
4281 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4282
4283 let base_fee: u128 = 100;
4284 let blob_fee: u128 = 100;
4285
4286 let mut block_info = pool.block_info();
4288 block_info.pending_basefee = base_fee as u64;
4289 block_info.pending_blob_fee = Some(blob_fee);
4290 pool.set_block_info(block_info);
4291
4292 let tx1 = MockTransaction::eip4844()
4294 .with_sender(Address::with_last_byte(1))
4295 .with_max_fee(base_fee + 10)
4296 .with_blob_fee(blob_fee + 10);
4297 let tx2 = MockTransaction::eip4844()
4298 .with_sender(Address::with_last_byte(2))
4299 .with_max_fee(base_fee + 10)
4300 .with_blob_fee(blob_fee);
4301 let tx3 = MockTransaction::eip4844()
4302 .with_sender(Address::with_last_byte(3))
4303 .with_max_fee(base_fee)
4304 .with_blob_fee(blob_fee + 10);
4305 let tx4 = MockTransaction::eip4844()
4306 .with_sender(Address::with_last_byte(4))
4307 .with_max_fee(base_fee)
4308 .with_blob_fee(blob_fee);
4309 let tx5 = MockTransaction::eip4844()
4310 .with_sender(Address::with_last_byte(5))
4311 .with_max_fee(base_fee)
4312 .with_blob_fee(blob_fee - 10);
4313 let tx6 = MockTransaction::eip4844()
4314 .with_sender(Address::with_last_byte(6))
4315 .with_max_fee(base_fee - 10)
4316 .with_blob_fee(blob_fee);
4317 let tx7 = MockTransaction::eip4844()
4318 .with_sender(Address::with_last_byte(7))
4319 .with_max_fee(base_fee - 10)
4320 .with_blob_fee(blob_fee - 10);
4321
4322 for tx in vec![
4323 tx1.clone(),
4324 tx2.clone(),
4325 tx3.clone(),
4326 tx4.clone(),
4327 tx5.clone(),
4328 tx6.clone(),
4329 tx7.clone(),
4330 ] {
4331 pool.add_transaction(f.validated(tx.clone()), on_chain_balance, on_chain_nonce, None)
4332 .unwrap();
4333 }
4334
4335 let base_fee = base_fee as u64;
4336 let blob_fee = blob_fee as u64;
4337
4338 let cases = vec![
4339 (BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee + 5)), vec![tx1.clone()]),
4341 (
4343 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee)),
4344 vec![tx1.clone(), tx2.clone()],
4345 ),
4346 (
4348 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee - 5)),
4349 vec![tx1.clone(), tx2.clone()],
4350 ),
4351 (
4353 BestTransactionsAttributes::new(base_fee, Some(blob_fee + 5)),
4354 vec![tx1.clone(), tx3.clone()],
4355 ),
4356 (
4358 BestTransactionsAttributes::new(base_fee, Some(blob_fee)),
4359 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
4360 ),
4361 (
4363 BestTransactionsAttributes::new(base_fee, Some(blob_fee - 10)),
4364 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx5.clone()],
4365 ),
4366 (
4368 BestTransactionsAttributes::new(base_fee - 5, Some(blob_fee + 5)),
4369 vec![tx1.clone(), tx3.clone()],
4370 ),
4371 (
4373 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee)),
4374 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx6.clone()],
4375 ),
4376 (
4378 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee - 10)),
4379 vec![tx1, tx2, tx5, tx3, tx4, tx6, tx7],
4380 ),
4381 ];
4382
4383 for (idx, (attribute, expected)) in cases.into_iter().enumerate() {
4384 let mut best = pool.best_transactions_with_attributes(attribute);
4385
4386 for (tx_idx, expected_tx) in expected.into_iter().enumerate() {
4387 let tx = best.next().expect("Transaction should be returned");
4388 assert_eq!(
4389 tx.transaction,
4390 expected_tx,
4391 "Failed tx {} in case {}",
4392 tx_idx + 1,
4393 idx + 1
4394 );
4395 }
4396
4397 assert!(best.next().is_none());
4399 }
4400 }
4401
4402 #[test]
4403 fn test_pending_ordering() {
4404 let mut f = MockTransactionFactory::default();
4405 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4406
4407 let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
4408 let tx_1 = tx_0.next();
4409
4410 let v0 = f.validated(tx_0);
4411 let v1 = f.validated(tx_1);
4412
4413 pool.add_transaction(v0.clone(), U256::MAX, 0, None).unwrap();
4415 assert_eq!(1, pool.queued_transactions().len());
4416
4417 pool.add_transaction(v1, U256::MAX, 1, None).unwrap();
4419
4420 assert_eq!(2, pool.pending_transactions().len());
4421 assert_eq!(0, pool.queued_transactions().len());
4422
4423 assert_eq!(
4424 pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
4425 v0.nonce()
4426 );
4427 }
4428
4429 #[test]
4431 fn one_sender_one_independent_transaction() {
4432 let mut on_chain_balance = U256::from(4_999); let mut on_chain_nonce = 40;
4434 let mut f = MockTransactionFactory::default();
4435 let mut pool = TxPool::mock();
4436 let mut submitted_txs = Vec::new();
4437
4438 let template =
4440 MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
4441
4442 for tx_nonce in 40..48 {
4445 let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
4446 submitted_txs.push(*tx.id());
4447 pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap();
4448 }
4449
4450 on_chain_balance = U256::from(999_999);
4453 on_chain_nonce = 42;
4454 pool.remove_transaction(&submitted_txs[0]);
4455 pool.remove_transaction(&submitted_txs[1]);
4456
4457 for tx_nonce in 48..52 {
4459 pool.add_transaction(
4460 f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
4461 on_chain_balance,
4462 on_chain_nonce,
4463 None,
4464 )
4465 .unwrap();
4466 }
4467
4468 let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
4469 assert_eq!(best_txs.len(), 10); assert_eq!(pool.pending_pool.independent().len(), 1);
4472 }
4473
4474 #[test]
4475 fn test_insertion_disorder() {
4476 let mut f = MockTransactionFactory::default();
4477 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4478
4479 let sender = address!("0x1234567890123456789012345678901234567890");
4480 let tx0 = f.validated_arc(
4481 MockTransaction::legacy().with_sender(sender).with_nonce(0).with_gas_price(10),
4482 );
4483 let tx1 = f.validated_arc(
4484 MockTransaction::eip1559()
4485 .with_sender(sender)
4486 .with_nonce(1)
4487 .with_gas_limit(1000)
4488 .with_gas_price(10),
4489 );
4490 let tx2 = f.validated_arc(
4491 MockTransaction::legacy().with_sender(sender).with_nonce(2).with_gas_price(10),
4492 );
4493 let tx3 = f.validated_arc(
4494 MockTransaction::legacy().with_sender(sender).with_nonce(3).with_gas_price(10),
4495 );
4496
4497 pool.add_transaction((*tx0).clone(), U256::from(1000), 0, None).unwrap();
4499 let mut best = pool.best_transactions();
4500 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4501 assert_eq!(t0.id(), tx0.id());
4502 pool.add_transaction((*tx1).clone(), U256::from(1000), 0, None).unwrap();
4504 let mut best = pool.best_transactions();
4505 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4506 assert_eq!(t0.id(), tx0.id());
4507 assert!(best.next().is_none());
4508
4509 pool.add_transaction((*tx2).clone(), U256::MAX, 0, None).unwrap();
4511
4512 let mut best = pool.best_transactions();
4513
4514 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4515 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4516 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4517 assert_eq!(t0.id(), tx0.id());
4518 assert_eq!(t1.id(), tx1.id());
4519 assert_eq!(t2.id(), tx2.id());
4520
4521 pool.add_transaction((*tx3).clone(), U256::MAX, 0, None).unwrap();
4523 let mut best = pool.best_transactions();
4524 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4525 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4526 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4527 let t3 = best.next().expect("tx3 should be put in the pending subpool");
4528 assert_eq!(t0.id(), tx0.id());
4529 assert_eq!(t1.id(), tx1.id());
4530 assert_eq!(t2.id(), tx2.id());
4531 assert_eq!(t3.id(), tx3.id());
4532 }
4533
4534 #[test]
4535 fn test_non_4844_blob_fee_bit_invariant() {
4536 let mut f = MockTransactionFactory::default();
4537 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4538
4539 let non_4844_tx = MockTransaction::eip1559().set_max_fee(200).inc_limit();
4540 let validated = f.validated(non_4844_tx.clone());
4541
4542 assert!(!non_4844_tx.is_eip4844());
4543 pool.add_transaction(validated.clone(), U256::from(10_000), 0, None).unwrap();
4544
4545 let tx_meta = pool.all_transactions.txs.get(validated.id()).unwrap();
4547 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4548 assert_eq!(tx_meta.subpool, SubPool::Pending);
4549 }
4550
4551 #[test]
4552 fn test_blob_fee_enforcement_only_applies_to_eip4844() {
4553 let mut f = MockTransactionFactory::default();
4554 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4555
4556 let mut block_info = pool.block_info();
4558 block_info.pending_blob_fee = Some(160);
4559 block_info.pending_basefee = 100;
4560 pool.set_block_info(block_info);
4561
4562 let eip4844_tx = MockTransaction::eip4844()
4563 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4564 .with_max_fee(200)
4565 .with_blob_fee(150) .inc_limit();
4567
4568 let non_4844_tx = MockTransaction::eip1559()
4569 .with_sender(address!("0x000000000000000000000000000000000000000b"))
4570 .set_max_fee(200)
4571 .inc_limit();
4572
4573 let validated_4844 = f.validated(eip4844_tx);
4574 let validated_non_4844 = f.validated(non_4844_tx);
4575
4576 pool.add_transaction(validated_4844.clone(), U256::from(10_000), 0, None).unwrap();
4577 pool.add_transaction(validated_non_4844.clone(), U256::from(10_000), 0, None).unwrap();
4578
4579 let tx_4844_meta = pool.all_transactions.txs.get(validated_4844.id()).unwrap();
4580 let tx_non_4844_meta = pool.all_transactions.txs.get(validated_non_4844.id()).unwrap();
4581
4582 assert!(!tx_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4584 assert_eq!(tx_4844_meta.subpool, SubPool::Blob);
4585
4586 assert!(tx_non_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4588 assert_eq!(tx_non_4844_meta.subpool, SubPool::Pending);
4589 }
4590
4591 #[test]
4592 fn test_basefee_decrease_preserves_non_4844_blob_fee_bit() {
4593 let mut f = MockTransactionFactory::default();
4594 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4595
4596 let non_4844_tx = MockTransaction::eip1559()
4598 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4599 .set_max_fee(500) .inc_limit();
4601
4602 pool.update_basefee(600, |_| {});
4604
4605 let validated = f.validated(non_4844_tx);
4606 let tx_id = *validated.id();
4607 pool.add_transaction(validated, U256::from(10_000), 0, None).unwrap();
4608
4609 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4611 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
4612 assert!(
4613 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4614 "Non-4844 tx in BaseFee pool must retain ENOUGH_BLOB_FEE_CAP_BLOCK bit"
4615 );
4616
4617 pool.update_basefee(400, |_| {});
4620
4621 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4623 assert_eq!(
4624 tx_meta.subpool,
4625 SubPool::Pending,
4626 "Non-4844 tx should be promoted from BaseFee to Pending after basefee decrease"
4627 );
4628 assert!(
4629 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4630 "Non-4844 tx must NEVER lose ENOUGH_BLOB_FEE_CAP_BLOCK bit during basefee promotion"
4631 );
4632 assert!(
4633 tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK),
4634 "Non-4844 tx should gain ENOUGH_FEE_CAP_BLOCK bit after basefee decrease"
4635 );
4636 }
4637
4638 #[test]
4644 fn best_transactions_nonce_order_on_balance_unlock() {
4645 let mut f = MockTransactionFactory::default();
4646 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4647
4648 let sender = Address::random();
4649 let on_chain_balance = U256::from(10_000);
4650
4651 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
4653 let tx1 = tx0.next().inc_limit().with_value(U256::from(on_chain_balance));
4655 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
4657
4658 let v0 = f.validated(tx0);
4659 let v1 = f.validated(tx1);
4660 let v2 = f.validated(tx2);
4661
4662 pool.add_transaction(v0, on_chain_balance, 0, None).unwrap();
4664
4665 let mut best = pool.best_transactions();
4667
4668 let first = best.next().expect("should yield tx0");
4670 assert_eq!(first.id().nonce, 0);
4671
4672 pool.add_transaction(v1, on_chain_balance, 0, None).unwrap();
4675
4676 assert!(best.next().is_none(), "tx1 should be queued, not pending");
4678
4679 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
4683
4684 let t1 = best.next().expect("should yield a transaction");
4685 let t2 = best.next().expect("should yield a transaction");
4686
4687 assert_eq!(
4689 t1.id().nonce,
4690 1,
4691 "first yielded tx should be nonce 1, got nonce {}",
4692 t1.id().nonce
4693 );
4694 assert_eq!(
4695 t2.id().nonce,
4696 2,
4697 "second yielded tx should be nonce 2, got nonce {}",
4698 t2.id().nonce
4699 );
4700 }
4701
4702 #[test]
4706 fn best_transactions_nonce_order_on_gap_fill() {
4707 let mut f = MockTransactionFactory::default();
4708 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4709
4710 let sender = Address::random();
4711 let balance = U256::MAX;
4712
4713 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
4715 let tx1 = tx0.next().inc_limit();
4717
4718 let v0 = f.validated(tx0);
4719 let v1 = f.validated(tx1);
4720
4721 pool.add_transaction(v1, balance, 0, None).unwrap();
4723
4724 let mut best = pool.best_transactions();
4726 assert!(best.next().is_none(), "pool should have no pending txs yet");
4727
4728 pool.add_transaction(v0, balance, 0, None).unwrap();
4730
4731 let t0 = best.next().expect("should yield a transaction");
4732 let t1 = best.next().expect("should yield a transaction");
4733
4734 assert_eq!(t0.id().nonce, 0, "first yielded tx should be nonce 0, got {}", t0.id().nonce);
4735 assert_eq!(t1.id().nonce, 1, "second yielded tx should be nonce 1, got {}", t1.id().nonce);
4736 }
4737
4738 #[test]
4742 fn best_transactions_nonce_order_mixed_promotions() {
4743 let mut f = MockTransactionFactory::default();
4744 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4745
4746 let sender = Address::random();
4747 let low_balance = U256::from(10_000);
4748
4749 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
4751 let tx1 = tx0.next().inc_limit().with_value(U256::from(low_balance));
4753 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
4755 let tx3 = tx2.next().inc_limit().with_value(U256::ZERO);
4757
4758 let v0 = f.validated(tx0);
4759 let v1 = f.validated(tx1);
4760 let v2 = f.validated(tx2);
4761 let v3 = f.validated(tx3);
4762
4763 pool.add_transaction(v0, low_balance, 0, None).unwrap();
4765
4766 pool.add_transaction(v1, low_balance, 0, None).unwrap();
4768
4769 pool.add_transaction(v3, low_balance, 0, None).unwrap();
4771
4772 let mut best = pool.best_transactions();
4773
4774 let first = best.next().expect("should yield tx0");
4776 assert_eq!(first.id().nonce, 0);
4777 assert!(best.next().is_none(), "only tx0 should be pending");
4778
4779 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
4784
4785 let t1 = best.next().expect("should yield nonce 1");
4786 let t2 = best.next().expect("should yield nonce 2");
4787 let t3 = best.next().expect("should yield nonce 3");
4788
4789 assert_eq!(t1.id().nonce, 1, "expected nonce 1, got {}", t1.id().nonce);
4790 assert_eq!(t2.id().nonce, 2, "expected nonce 2, got {}", t2.id().nonce);
4791 assert_eq!(t3.id().nonce, 3, "expected nonce 3, got {}", t3.id().nonce);
4792 }
4793}