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::{map::AddressSet, TxHash, B256};
36use rustc_hash::FxHashMap;
37use smallvec::SmallVec;
38use std::{
39 cmp::Ordering,
40 collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet},
41 fmt,
42 ops::Bound::{Excluded, Unbounded},
43 sync::Arc,
44};
45use tracing::{trace, warn};
46
47#[cfg_attr(doc, aquamarine::aquamarine)]
48pub struct TxPool<T: TransactionOrdering> {
90 pending_pool: PendingPool<T>,
94 config: PoolConfig,
96 queued_pool: ParkedPool<QueuedOrd<T::Transaction>>,
103 basefee_pool: ParkedPool<BasefeeOrd<T::Transaction>>,
108 blob_pool: BlobTransactions<T::Transaction>,
115 all_transactions: AllTransactions<T::Transaction>,
117 metrics: TxPoolMetrics,
119}
120
121impl<T: TransactionOrdering> TxPool<T> {
124 pub fn new(ordering: T, config: PoolConfig) -> Self {
126 Self {
127 pending_pool: PendingPool::with_buffer(
128 ordering,
129 config.max_new_pending_txs_notifications,
130 ),
131 queued_pool: Default::default(),
132 basefee_pool: Default::default(),
133 blob_pool: Default::default(),
134 all_transactions: AllTransactions::new(&config),
135 config,
136 metrics: Default::default(),
137 }
138 }
139
140 pub fn get_highest_nonce_by_sender(&self, sender: SenderId) -> Option<u64> {
142 self.all().txs_iter(sender).last().map(|(_, tx)| tx.transaction.nonce())
143 }
144
145 pub fn get_highest_transaction_by_sender(
148 &self,
149 sender: SenderId,
150 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
151 self.all().txs_iter(sender).last().map(|(_, tx)| Arc::clone(&tx.transaction))
152 }
153
154 pub(crate) fn get_highest_consecutive_transaction_by_sender(
161 &self,
162 mut on_chain: TransactionId,
163 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
164 let mut last_consecutive_tx = None;
165
166 if let Some(current) = self.all_transactions.sender_info.get(&on_chain.sender) {
168 on_chain.nonce = on_chain.nonce.max(current.state_nonce);
169 }
170
171 let mut next_expected_nonce = on_chain.nonce;
172 for (id, tx) in self.all().descendant_txs_inclusive(&on_chain) {
173 if next_expected_nonce != id.nonce {
174 break
175 }
176 next_expected_nonce = id.next_nonce();
177 last_consecutive_tx = Some(tx);
178 }
179
180 last_consecutive_tx.map(|tx| Arc::clone(&tx.transaction))
181 }
182
183 pub(crate) const fn all(&self) -> &AllTransactions<T::Transaction> {
185 &self.all_transactions
186 }
187
188 pub(crate) fn unique_senders(&self) -> AddressSet {
190 self.all_transactions.txs.values().map(|tx| tx.transaction.sender()).collect()
191 }
192
193 pub fn size(&self) -> PoolSize {
195 PoolSize {
196 pending: self.pending_pool.len(),
197 pending_size: self.pending_pool.size(),
198 basefee: self.basefee_pool.len(),
199 basefee_size: self.basefee_pool.size(),
200 queued: self.queued_pool.len(),
201 queued_size: self.queued_pool.size(),
202 blob: self.blob_pool.len(),
203 blob_size: self.blob_pool.size(),
204 total: self.all_transactions.len(),
205 }
206 }
207
208 pub const fn block_info(&self) -> BlockInfo {
210 BlockInfo {
211 block_gas_limit: self.all_transactions.block_gas_limit,
212 last_seen_block_hash: self.all_transactions.last_seen_block_hash,
213 last_seen_block_number: self.all_transactions.last_seen_block_number,
214 pending_basefee: self.all_transactions.pending_fees.base_fee,
215 pending_blob_fee: Some(self.all_transactions.pending_fees.blob_fee),
216 }
217 }
218
219 fn update_blob_fee<F>(
221 &mut self,
222 mut pending_blob_fee: u128,
223 base_fee_update: Ordering,
224 mut on_promoted: F,
225 ) where
226 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
227 {
228 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut pending_blob_fee);
229 match (self.all_transactions.pending_fees.blob_fee.cmp(&pending_blob_fee), base_fee_update)
230 {
231 (Ordering::Equal, Ordering::Equal | Ordering::Greater) => {
232 }
234 (Ordering::Greater, Ordering::Equal | Ordering::Greater) => {
235 let removed =
237 self.pending_pool.update_blob_fee(self.all_transactions.pending_fees.blob_fee);
238 for tx in removed {
239 let to = {
240 let tx =
241 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
242
243 tx.state.remove(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
245 tx.subpool = tx.state.into();
246 tx.subpool
247 };
248 self.add_transaction_to_subpool(to, tx);
249 }
250 }
251 (Ordering::Less, _) | (_, Ordering::Less) => {
252 let removed =
254 self.blob_pool.enforce_pending_fees(&self.all_transactions.pending_fees);
255 for tx in removed {
256 let subpool = {
257 let tx_meta =
258 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
259 tx_meta.state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
260 tx_meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
261 tx_meta.subpool = tx_meta.state.into();
262 tx_meta.subpool
263 };
264
265 if subpool == SubPool::Pending {
266 on_promoted(&tx);
267 }
268
269 self.add_transaction_to_subpool(subpool, tx);
270 }
271 }
272 }
273 }
274
275 fn update_basefee<F>(&mut self, mut pending_basefee: u64, mut on_promoted: F) -> Ordering
280 where
281 F: FnMut(&Arc<ValidPoolTransaction<T::Transaction>>),
282 {
283 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut pending_basefee);
284 match self.all_transactions.pending_fees.base_fee.cmp(&pending_basefee) {
285 Ordering::Equal => {
286 Ordering::Equal
288 }
289 Ordering::Greater => {
290 let removed =
292 self.pending_pool.update_base_fee(self.all_transactions.pending_fees.base_fee);
293 for tx in removed {
294 let to = {
295 let tx =
296 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
297 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
298 tx.subpool = tx.state.into();
299 tx.subpool
300 };
301 self.add_transaction_to_subpool(to, tx);
302 }
303
304 Ordering::Greater
305 }
306 Ordering::Less => {
307 let current_base_fee = self.all_transactions.pending_fees.base_fee;
316 self.basefee_pool.enforce_basefee_with(current_base_fee, |tx| {
317 let subpool = {
319 let meta =
320 self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set");
321 meta.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
322 meta.subpool = meta.state.into();
323 meta.subpool
324 };
325
326 if subpool == SubPool::Pending {
327 on_promoted(&tx);
328 }
329
330 trace!(target: "txpool", hash=%tx.transaction.hash(), pool=?subpool, "Adding transaction to a subpool");
331 match subpool {
332 SubPool::Queued => self.queued_pool.add_transaction(tx),
333 SubPool::Pending => {
334 self.pending_pool.add_transaction(tx, current_base_fee);
335 }
336 SubPool::Blob => {
337 self.blob_pool.add_transaction(tx);
338 }
339 SubPool::BaseFee => {
340 warn!(target: "txpool", "BaseFee transactions should become Pending after basefee decrease");
343 }
344 }
345 });
346
347 Ordering::Less
348 }
349 }
350 }
351
352 pub fn set_block_info(&mut self, info: BlockInfo) -> UpdateOutcome<T::Transaction> {
358 let mut outcome = UpdateOutcome::default();
359
360 let basefee_ordering = self.update_basefee(info.pending_basefee, |tx| {
362 outcome.promoted.push(tx.clone());
363 });
364 if let Some(blob_fee) = info.pending_blob_fee {
365 self.update_blob_fee(blob_fee, basefee_ordering, |tx| {
366 outcome.promoted.push(tx.clone());
367 })
368 }
369 self.all_transactions.set_block_info(info);
371
372 outcome
373 }
374
375 pub(crate) fn best_transactions(&self) -> BestTransactions<T> {
378 self.pending_pool.best()
379 }
380
381 pub(crate) fn best_transactions_with_attributes(
388 &self,
389 best_transactions_attributes: BestTransactionsAttributes,
390 ) -> Box<dyn crate::traits::BestTransactions<Item = Arc<ValidPoolTransaction<T::Transaction>>>>
391 {
392 match best_transactions_attributes.basefee.cmp(&self.all_transactions.pending_fees.base_fee)
395 {
396 Ordering::Equal => {
397 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
401 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
402 Ordering::Less => {
403 let unlocked =
405 self.blob_pool.satisfy_attributes(best_transactions_attributes);
406 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
407 unlocked,
408 best_transactions_attributes.basefee,
409 new_blob_fee,
410 ))
411 }
412 Ordering::Equal => Box::new(self.pending_pool.best()),
413 Ordering::Greater => {
414 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
416 best_transactions_attributes.basefee,
417 best_transactions_attributes.blob_fee.unwrap_or_default(),
418 ))
419 }
420 }
421 }
422 Ordering::Greater => {
423 let new_blob_fee = best_transactions_attributes.blob_fee.unwrap_or_default();
425 match new_blob_fee.cmp(&(self.all_transactions.pending_fees.blob_fee as u64)) {
426 Ordering::Less => {
427 let unlocked =
429 self.blob_pool.satisfy_attributes(best_transactions_attributes);
430 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
431 unlocked,
432 best_transactions_attributes.basefee,
433 new_blob_fee,
434 ))
435 }
436 Ordering::Equal | Ordering::Greater => {
437 Box::new(self.pending_pool.best_with_basefee_and_blobfee(
439 best_transactions_attributes.basefee,
440 new_blob_fee,
441 ))
442 }
443 }
444 }
445 Ordering::Less => {
446 let mut unlocked = self
449 .basefee_pool
450 .satisfy_base_fee_transactions(best_transactions_attributes.basefee);
451
452 unlocked.extend(self.blob_pool.satisfy_attributes(best_transactions_attributes));
454
455 Box::new(self.pending_pool.best_with_unlocked_and_attributes(
456 unlocked,
457 best_transactions_attributes.basefee,
458 best_transactions_attributes.blob_fee.unwrap_or_default(),
459 ))
460 }
461 }
462 }
463
464 pub(crate) fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
466 self.pending_pool.all().collect()
467 }
468 pub(crate) fn pending_transactions_iter(
470 &self,
471 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
472 self.pending_pool.all()
473 }
474
475 pub(crate) fn pending_transactions_count(&self) -> usize {
477 self.pending_pool.len()
478 }
479
480 pub(crate) fn pending_transactions_with_predicate(
482 &self,
483 mut predicate: impl FnMut(&ValidPoolTransaction<T::Transaction>) -> bool,
484 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
485 self.pending_transactions_iter().filter(|tx| predicate(tx)).collect()
486 }
487
488 pub(crate) fn pending_txs_by_sender(
490 &self,
491 sender: SenderId,
492 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
493 self.pending_pool.txs_by_sender(sender)
494 }
495
496 pub(crate) fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
498 self.basefee_pool.all().chain(self.queued_pool.all()).collect()
499 }
500
501 pub(crate) fn queued_transactions_count(&self) -> usize {
503 self.basefee_pool.len() + self.queued_pool.len()
504 }
505
506 pub fn queued_and_pending_txs_by_sender(
508 &self,
509 sender: SenderId,
510 ) -> (SmallVec<[TransactionId; TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER]>, Vec<TransactionId>) {
511 (self.queued_pool.get_txs_by_sender(sender), self.pending_pool.get_txs_by_sender(sender))
512 }
513
514 pub(crate) fn queued_txs_by_sender(
516 &self,
517 sender: SenderId,
518 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
519 let mut txs = self.basefee_pool.txs_by_sender(sender);
520 txs.extend(self.queued_pool.txs_by_sender(sender));
521 txs
522 }
523
524 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
526 self.all_transactions.contains(tx_hash)
527 }
528
529 #[cfg(test)]
531 pub(crate) fn subpool_contains(&self, subpool: SubPool, id: &TransactionId) -> bool {
532 match subpool {
533 SubPool::Queued => self.queued_pool.contains(id),
534 SubPool::Pending => self.pending_pool.contains(id),
535 SubPool::BaseFee => self.basefee_pool.contains(id),
536 SubPool::Blob => self.blob_pool.contains(id),
537 }
538 }
539
540 #[inline]
542 pub(crate) fn is_exceeded(&self) -> bool {
543 self.config.is_exceeded(self.size())
544 }
545
546 pub(crate) fn get(
548 &self,
549 tx_hash: &TxHash,
550 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
551 self.all_transactions.by_hash.get(tx_hash).cloned()
552 }
553
554 pub(crate) fn get_all(
556 &self,
557 txs: Vec<TxHash>,
558 ) -> impl Iterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
559 txs.into_iter().filter_map(|tx| self.get(&tx))
560 }
561
562 pub(crate) fn get_transactions_by_sender(
564 &self,
565 sender: SenderId,
566 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
567 self.all_transactions.txs_iter(sender).map(|(_, tx)| Arc::clone(&tx.transaction)).collect()
568 }
569
570 pub(crate) fn get_pending_transaction_by_sender_and_nonce(
572 &self,
573 sender: SenderId,
574 nonce: u64,
575 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
576 self.all_transactions
577 .txs_iter(sender)
578 .find(|(id, tx)| id.nonce == nonce && tx.subpool == SubPool::Pending)
579 .map(|(_, tx)| Arc::clone(&tx.transaction))
580 }
581
582 const fn update_pending_fees_only(
585 &mut self,
586 mut new_base_fee: u64,
587 new_blob_fee: Option<u128>,
588 ) -> (u64, u128) {
589 std::mem::swap(&mut self.all_transactions.pending_fees.base_fee, &mut new_base_fee);
590
591 let prev_blob_fee = if let Some(mut blob_fee) = new_blob_fee {
592 std::mem::swap(&mut self.all_transactions.pending_fees.blob_fee, &mut blob_fee);
593 blob_fee
594 } else {
595 self.all_transactions.pending_fees.blob_fee
596 };
597
598 (new_base_fee, prev_blob_fee)
599 }
600
601 fn apply_fee_updates(
608 &mut self,
609 prev_base_fee: u64,
610 prev_blob_fee: u128,
611 outcome: &mut UpdateOutcome<T::Transaction>,
612 ) {
613 let new_base_fee = self.all_transactions.pending_fees.base_fee;
614 let new_blob_fee = self.all_transactions.pending_fees.blob_fee;
615
616 if new_base_fee == prev_base_fee && new_blob_fee == prev_blob_fee {
617 return;
619 }
620
621 self.all_transactions.pending_fees.base_fee = prev_base_fee;
624 self.all_transactions.pending_fees.blob_fee = prev_blob_fee;
625
626 let base_fee_ordering = self.update_basefee(new_base_fee, |tx| {
627 outcome.promoted.push(tx.clone());
628 });
629
630 self.update_blob_fee(new_blob_fee, base_fee_ordering, |tx| {
631 outcome.promoted.push(tx.clone());
632 });
633 }
634
635 pub(crate) fn update_accounts(
637 &mut self,
638 changed_senders: FxHashMap<SenderId, SenderInfo>,
639 ) -> UpdateOutcome<T::Transaction> {
640 let updates = self.all_transactions.update(&changed_senders);
642
643 self.all_transactions.sender_info.extend(changed_senders);
645
646 let update = self.process_updates(updates);
648 self.update_size_metrics();
650 update
651 }
652
653 pub(crate) fn on_canonical_state_change(
658 &mut self,
659 block_info: BlockInfo,
660 mined_transactions: Vec<TxHash>,
661 changed_senders: FxHashMap<SenderId, SenderInfo>,
662 _update_kind: PoolUpdateKind,
663 ) -> OnNewCanonicalStateOutcome<T::Transaction> {
664 let block_hash = block_info.last_seen_block_hash;
666
667 let mut removed_txs_count = 0;
669 for tx_hash in &mined_transactions {
670 if self.prune_transaction_by_hash(tx_hash).is_some() {
671 removed_txs_count += 1;
672 }
673 }
674
675 self.metrics.removed_transactions.increment(removed_txs_count);
677
678 let (prev_base_fee, prev_blob_fee) =
683 self.update_pending_fees_only(block_info.pending_basefee, block_info.pending_blob_fee);
684
685 let mut outcome = self.update_accounts(changed_senders);
687
688 self.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
691
692 self.all_transactions.set_block_info(block_info);
694
695 self.update_transaction_type_metrics();
696 self.metrics.performed_state_updates.increment(1);
697
698 OnNewCanonicalStateOutcome {
699 block_hash,
700 mined: mined_transactions,
701 promoted: outcome.promoted,
702 discarded: outcome.discarded,
703 }
704 }
705
706 pub(crate) fn update_size_metrics(&self) {
708 let stats = self.size();
709 self.metrics.pending_pool_transactions.set(stats.pending as f64);
710 self.metrics.pending_pool_size_bytes.set(stats.pending_size as f64);
711 self.metrics.basefee_pool_transactions.set(stats.basefee as f64);
712 self.metrics.basefee_pool_size_bytes.set(stats.basefee_size as f64);
713 self.metrics.queued_pool_transactions.set(stats.queued as f64);
714 self.metrics.queued_pool_size_bytes.set(stats.queued_size as f64);
715 self.metrics.blob_pool_transactions.set(stats.blob as f64);
716 self.metrics.blob_pool_size_bytes.set(stats.blob_size as f64);
717 self.metrics.total_transactions.set(stats.total as f64);
718 }
719
720 pub(crate) fn update_transaction_type_metrics(&self) {
722 let mut legacy_count = 0;
723 let mut eip2930_count = 0;
724 let mut eip1559_count = 0;
725 let mut eip4844_count = 0;
726 let mut eip7702_count = 0;
727 let mut other_count = 0;
728
729 for tx in self.all_transactions.transactions_iter() {
730 match tx.transaction.ty() {
731 LEGACY_TX_TYPE_ID => legacy_count += 1,
732 EIP2930_TX_TYPE_ID => eip2930_count += 1,
733 EIP1559_TX_TYPE_ID => eip1559_count += 1,
734 EIP4844_TX_TYPE_ID => eip4844_count += 1,
735 EIP7702_TX_TYPE_ID => eip7702_count += 1,
736 _ => other_count += 1,
737 }
738 }
739
740 self.metrics.total_legacy_transactions.set(legacy_count as f64);
741 self.metrics.total_eip2930_transactions.set(eip2930_count as f64);
742 self.metrics.total_eip1559_transactions.set(eip1559_count as f64);
743 self.metrics.total_eip4844_transactions.set(eip4844_count as f64);
744 self.metrics.total_eip7702_transactions.set(eip7702_count as f64);
745 self.metrics.total_other_transactions.set(other_count as f64);
746 }
747
748 pub(crate) fn add_transaction(
749 &mut self,
750 tx: ValidPoolTransaction<T::Transaction>,
751 on_chain_balance: U256,
752 on_chain_nonce: u64,
753 on_chain_code_hash: Option<B256>,
754 ) -> PoolResult<AddedTransaction<T::Transaction>> {
755 if self.contains(tx.hash()) {
756 return Err(PoolError::new(*tx.hash(), PoolErrorKind::AlreadyImported))
757 }
758
759 self.validate_auth(&tx, on_chain_nonce, on_chain_code_hash)?;
760
761 self.all_transactions
763 .sender_info
764 .entry(tx.sender_id())
765 .or_default()
766 .update(on_chain_nonce, on_chain_balance);
767
768 match self.all_transactions.insert_tx(tx, on_chain_balance, on_chain_nonce) {
769 Ok(InsertOk { transaction, move_to, replaced_tx, mut updates, state }) => {
770 let new_nonce = transaction.id().nonce;
778 let split = updates.iter().position(|u| u.id.nonce >= new_nonce);
779 let (promoted, discarded) = match split {
780 None => {
782 let UpdateOutcome { promoted, discarded } = self.process_updates(updates);
783 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
784 (promoted, discarded)
785 }
786 Some(0) => {
788 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
789 let UpdateOutcome { promoted, discarded } = self.process_updates(updates);
790 (promoted, discarded)
791 }
792 Some(i) => {
794 let after = updates.split_off(i);
795 let mut outcome = self.process_updates(updates);
796 self.add_new_transaction(transaction.clone(), replaced_tx.clone(), move_to);
797 let after_outcome = self.process_updates(after);
798 outcome.promoted.extend(after_outcome.promoted);
799 outcome.discarded.extend(after_outcome.discarded);
800 (outcome.promoted, outcome.discarded)
801 }
802 };
803 self.metrics.inserted_transactions.increment(1);
804
805 let replaced = replaced_tx.map(|(tx, _)| tx);
806
807 let res = if move_to.is_pending() {
809 AddedTransaction::Pending(AddedPendingTransaction {
810 transaction,
811 promoted,
812 discarded,
813 replaced,
814 })
815 } else {
816 let queued_reason = state.determine_queued_reason(move_to);
818 AddedTransaction::Parked {
819 transaction,
820 subpool: move_to,
821 replaced,
822 queued_reason,
823 }
824 };
825
826 self.update_size_metrics();
828
829 Ok(res)
830 }
831 Err(err) => {
832 self.metrics.invalid_transactions.increment(1);
834 match err {
835 InsertErr::Underpriced { existing: _, transaction } => Err(PoolError::new(
836 *transaction.hash(),
837 PoolErrorKind::ReplacementUnderpriced,
838 )),
839 InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap } => {
840 Err(PoolError::new(
841 *transaction.hash(),
842 PoolErrorKind::FeeCapBelowMinimumProtocolFeeCap(fee_cap),
843 ))
844 }
845 InsertErr::ExceededSenderTransactionsCapacity { transaction } => {
846 Err(PoolError::new(
847 *transaction.hash(),
848 PoolErrorKind::SpammerExceededCapacity(transaction.sender()),
849 ))
850 }
851 InsertErr::TxGasLimitMoreThanAvailableBlockGas {
852 transaction,
853 block_gas_limit,
854 tx_gas_limit,
855 } => Err(PoolError::new(
856 *transaction.hash(),
857 PoolErrorKind::InvalidTransaction(
858 InvalidPoolTransactionError::ExceedsGasLimit(
859 tx_gas_limit,
860 block_gas_limit,
861 ),
862 ),
863 )),
864 InsertErr::BlobTxHasNonceGap { transaction } => Err(PoolError::new(
865 *transaction.hash(),
866 PoolErrorKind::InvalidTransaction(
867 Eip4844PoolTransactionError::Eip4844NonceGap.into(),
868 ),
869 )),
870 InsertErr::Overdraft { transaction } => Err(PoolError::new(
871 *transaction.hash(),
872 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Overdraft {
873 cost: *transaction.cost(),
874 balance: on_chain_balance,
875 }),
876 )),
877 InsertErr::TxTypeConflict { transaction } => Err(PoolError::new(
878 *transaction.hash(),
879 PoolErrorKind::ExistingConflictingTransactionType(
880 transaction.sender(),
881 transaction.tx_type(),
882 ),
883 )),
884 }
885 }
886 }
887 }
888
889 fn check_delegation_limit(
893 &self,
894 transaction: &ValidPoolTransaction<T::Transaction>,
895 on_chain_nonce: u64,
896 on_chain_code_hash: Option<B256>,
897 ) -> Result<(), PoolError> {
898 if (on_chain_code_hash.is_none() || on_chain_code_hash == Some(KECCAK_EMPTY)) &&
900 !self.all_transactions.auths.contains_key(&transaction.sender_id())
901 {
902 return Ok(())
903 }
904
905 let mut txs_by_sender =
906 self.pending_pool.iter_txs_by_sender(transaction.sender_id()).peekable();
907
908 if txs_by_sender.peek().is_none() {
909 let nonce_gap_distance = transaction.nonce().saturating_sub(on_chain_nonce);
914 if nonce_gap_distance >= self.config.max_inflight_delegated_slot_limit as u64 {
915 return Err(PoolError::new(
916 *transaction.hash(),
917 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
918 Eip7702PoolTransactionError::OutOfOrderTxFromDelegated,
919 )),
920 ))
921 }
922 return Ok(())
923 }
924
925 let mut count = 0;
926 for id in txs_by_sender {
927 if id == &transaction.transaction_id {
928 return Ok(())
930 }
931 count += 1;
932 }
933
934 if count < self.config.max_inflight_delegated_slot_limit {
935 return Ok(())
937 }
938
939 Err(PoolError::new(
940 *transaction.hash(),
941 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
942 Eip7702PoolTransactionError::InflightTxLimitReached,
943 )),
944 ))
945 }
946
947 fn validate_auth(
957 &self,
958 transaction: &ValidPoolTransaction<T::Transaction>,
959 on_chain_nonce: u64,
960 on_chain_code_hash: Option<B256>,
961 ) -> Result<(), PoolError> {
962 self.check_delegation_limit(transaction, on_chain_nonce, on_chain_code_hash)?;
964
965 if let Some(authority_list) = &transaction.authority_ids {
966 for sender_id in authority_list {
967 if self.all_transactions.txs_iter(*sender_id).nth(1).is_some() {
969 return Err(PoolError::new(
970 *transaction.hash(),
971 PoolErrorKind::InvalidTransaction(InvalidPoolTransactionError::Eip7702(
972 Eip7702PoolTransactionError::AuthorityReserved,
973 )),
974 ))
975 }
976 }
977 }
978
979 Ok(())
980 }
981
982 fn process_updates(&mut self, updates: Vec<PoolUpdate>) -> UpdateOutcome<T::Transaction> {
986 let mut outcome = UpdateOutcome::default();
987 let mut removed = 0;
988 for PoolUpdate { id, current, destination } in updates {
989 match destination {
990 Destination::Discard => {
991 if let Some(tx) = self.prune_transaction_by_id(&id) {
993 outcome.discarded.push(tx);
994 }
995 removed += 1;
996 }
997 Destination::Pool(move_to) => {
998 debug_assert_ne!(&move_to, ¤t, "destination must be different");
999 let moved = self.move_transaction(current, move_to, &id);
1000 if matches!(move_to, SubPool::Pending) &&
1001 let Some(tx) = moved
1002 {
1003 trace!(target: "txpool", hash=%tx.transaction.hash(), "Promoted transaction to pending");
1004 outcome.promoted.push(tx);
1005 }
1006 }
1007 }
1008 }
1009
1010 if removed > 0 {
1011 self.metrics.removed_transactions.increment(removed);
1012 }
1013
1014 outcome
1015 }
1016
1017 fn move_transaction(
1022 &mut self,
1023 from: SubPool,
1024 to: SubPool,
1025 id: &TransactionId,
1026 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1027 let tx = self.remove_from_subpool(from, id)?;
1028 self.add_transaction_to_subpool(to, tx.clone());
1029 Some(tx)
1030 }
1031
1032 pub(crate) fn remove_transactions(
1037 &mut self,
1038 hashes: Vec<TxHash>,
1039 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1040 let txs =
1041 hashes.into_iter().filter_map(|hash| self.remove_transaction_by_hash(&hash)).collect();
1042 self.update_size_metrics();
1043 txs
1044 }
1045
1046 pub(crate) fn remove_transactions_and_descendants(
1048 &mut self,
1049 hashes: Vec<TxHash>,
1050 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1051 let mut removed = Vec::new();
1052 for hash in hashes {
1053 if let Some(tx) = self.remove_transaction_by_hash(&hash) {
1054 removed.push(tx.clone());
1055 self.remove_descendants(tx.id(), &mut removed);
1056 }
1057 }
1058 self.update_size_metrics();
1059 removed
1060 }
1061
1062 pub(crate) fn remove_transactions_by_sender(
1064 &mut self,
1065 sender_id: SenderId,
1066 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1067 let mut removed = Vec::new();
1068 let txs = self.get_transactions_by_sender(sender_id);
1069 for tx in txs {
1070 if let Some(tx) = self.remove_transaction(tx.id()) {
1071 removed.push(tx);
1072 }
1073 }
1074 self.update_size_metrics();
1075 removed
1076 }
1077
1078 pub(crate) fn prune_transactions(
1084 &mut self,
1085 hashes: Vec<TxHash>,
1086 ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1087 let txs =
1088 hashes.into_iter().filter_map(|hash| self.prune_transaction_by_hash(&hash)).collect();
1089 self.update_size_metrics();
1090 txs
1091 }
1092
1093 fn remove_transaction(
1097 &mut self,
1098 id: &TransactionId,
1099 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1100 let (tx, pool) = self.all_transactions.remove_transaction(id)?;
1101 self.remove_from_subpool(pool, tx.id())
1102 }
1103
1104 fn remove_transaction_by_hash(
1110 &mut self,
1111 tx_hash: &B256,
1112 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1113 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1114
1115 let updates = self.all_transactions.park_descendant_transactions(tx.id());
1117 self.process_updates(updates);
1118 self.remove_from_subpool(pool, tx.id())
1119 }
1120
1121 fn prune_transaction_by_hash(
1128 &mut self,
1129 tx_hash: &B256,
1130 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1131 let (tx, pool) = self.all_transactions.remove_transaction_by_hash(tx_hash)?;
1132 self.remove_from_subpool(pool, tx.id())
1133 }
1134 fn prune_transaction_by_id(
1139 &mut self,
1140 tx_id: &TransactionId,
1141 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1142 let (tx, pool) = self.all_transactions.remove_transaction_by_id(tx_id)?;
1143 self.remove_from_subpool(pool, tx.id())
1144 }
1145
1146 fn remove_from_subpool(
1150 &mut self,
1151 pool: SubPool,
1152 tx: &TransactionId,
1153 ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
1154 let tx = match pool {
1155 SubPool::Queued => self.queued_pool.remove_transaction(tx),
1156 SubPool::Pending => self.pending_pool.remove_transaction(tx),
1157 SubPool::BaseFee => self.basefee_pool.remove_transaction(tx),
1158 SubPool::Blob => self.blob_pool.remove_transaction(tx),
1159 };
1160
1161 if let Some(ref tx) = tx {
1162 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Removed transaction from a subpool");
1166 }
1167
1168 tx
1169 }
1170
1171 fn remove_descendants(
1175 &mut self,
1176 tx: &TransactionId,
1177 removed: &mut Vec<Arc<ValidPoolTransaction<T::Transaction>>>,
1178 ) {
1179 let mut id = *tx;
1180
1181 loop {
1183 let descendant =
1184 self.all_transactions.descendant_txs_exclusive(&id).map(|(id, _)| *id).next();
1185 if let Some(descendant) = descendant {
1186 if let Some(tx) = self.remove_transaction(&descendant) {
1187 removed.push(tx)
1188 }
1189 id = descendant;
1190 } else {
1191 return
1192 }
1193 }
1194 }
1195
1196 fn add_transaction_to_subpool(
1198 &mut self,
1199 pool: SubPool,
1200 tx: Arc<ValidPoolTransaction<T::Transaction>>,
1201 ) {
1202 trace!(target: "txpool", hash=%tx.transaction.hash(), ?pool, "Adding transaction to a subpool");
1206 match pool {
1207 SubPool::Queued => self.queued_pool.add_transaction(tx),
1208 SubPool::Pending => {
1209 self.pending_pool.add_transaction(tx, self.all_transactions.pending_fees.base_fee);
1210 }
1211 SubPool::BaseFee => {
1212 self.basefee_pool.add_transaction(tx);
1213 }
1214 SubPool::Blob => {
1215 self.blob_pool.add_transaction(tx);
1216 }
1217 }
1218 }
1219
1220 fn add_new_transaction(
1223 &mut self,
1224 transaction: Arc<ValidPoolTransaction<T::Transaction>>,
1225 replaced: Option<(Arc<ValidPoolTransaction<T::Transaction>>, SubPool)>,
1226 pool: SubPool,
1227 ) {
1228 if let Some((replaced, replaced_pool)) = replaced {
1229 self.remove_from_subpool(replaced_pool, replaced.id());
1231 }
1232
1233 self.add_transaction_to_subpool(pool, transaction)
1234 }
1235
1236 pub(crate) fn discard_worst(&mut self) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
1243 let mut removed = Vec::new();
1244
1245 macro_rules! discard_worst {
1247 ($this:ident, $removed:ident, [$($limit:ident => ($pool:ident, $metric:ident)),* $(,)*]) => {
1248 $ (
1249 while $this.$pool.exceeds(&$this.config.$limit)
1250 {
1251 trace!(
1252 target: "txpool",
1253 "discarding transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1254 stringify!($pool),
1255 $this.config.$limit,
1256 $this.$pool.size(),
1257 $this.$pool.len(),
1258 );
1259
1260 let removed_from_subpool = $this.$pool.truncate_pool($this.config.$limit.clone());
1262
1263 trace!(
1264 target: "txpool",
1265 "removed {} transactions from {}, limit: {:?}, curr size: {}, curr len: {}",
1266 removed_from_subpool.len(),
1267 stringify!($pool),
1268 $this.config.$limit,
1269 $this.$pool.size(),
1270 $this.$pool.len()
1271 );
1272 $this.metrics.$metric.increment(removed_from_subpool.len() as u64);
1273
1274 for tx in removed_from_subpool {
1276 $this.all_transactions.remove_transaction(tx.id());
1277
1278 let id = *tx.id();
1279
1280 removed.push(tx);
1282
1283 $this.remove_descendants(&id, &mut $removed);
1285 }
1286 }
1287
1288 )*
1289 };
1290 }
1291
1292 discard_worst!(
1293 self, removed, [
1294 pending_limit => (pending_pool, pending_transactions_evicted),
1295 basefee_limit => (basefee_pool, basefee_transactions_evicted),
1296 blob_limit => (blob_pool, blob_transactions_evicted),
1297 queued_limit => (queued_pool, queued_transactions_evicted),
1298 ]
1299 );
1300
1301 removed
1302 }
1303
1304 pub(crate) fn len(&self) -> usize {
1306 self.all_transactions.len()
1307 }
1308
1309 pub(crate) fn is_empty(&self) -> bool {
1311 self.all_transactions.is_empty()
1312 }
1313
1314 #[cfg(any(test, feature = "test-utils"))]
1322 pub fn assert_invariants(&self) {
1323 let size = self.size();
1324 let actual = size.basefee + size.pending + size.queued + size.blob;
1325 assert_eq!(
1326 size.total, actual,
1327 "total size must be equal to the sum of all sub-pools, basefee:{}, pending:{}, queued:{}, blob:{}",
1328 size.basefee, size.pending, size.queued, size.blob
1329 );
1330 self.all_transactions.assert_invariants();
1331 self.pending_pool.assert_invariants();
1332 self.basefee_pool.assert_invariants();
1333 self.queued_pool.assert_invariants();
1334 self.blob_pool.assert_invariants();
1335 }
1336}
1337
1338#[cfg(any(test, feature = "test-utils"))]
1339impl TxPool<crate::test_utils::MockOrdering> {
1340 pub fn mock() -> Self {
1342 Self::new(crate::test_utils::MockOrdering::default(), PoolConfig::default())
1343 }
1344}
1345
1346#[cfg(test)]
1347impl<T: TransactionOrdering> Drop for TxPool<T> {
1348 fn drop(&mut self) {
1349 self.assert_invariants();
1350 }
1351}
1352
1353impl<T: TransactionOrdering> TxPool<T> {
1354 pub const fn pending(&self) -> &PendingPool<T> {
1356 &self.pending_pool
1357 }
1358
1359 pub const fn base_fee(&self) -> &ParkedPool<BasefeeOrd<T::Transaction>> {
1361 &self.basefee_pool
1362 }
1363
1364 pub const fn queued(&self) -> &ParkedPool<QueuedOrd<T::Transaction>> {
1366 &self.queued_pool
1367 }
1368}
1369
1370impl<T: TransactionOrdering> fmt::Debug for TxPool<T> {
1371 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1372 f.debug_struct("TxPool").field("config", &self.config).finish_non_exhaustive()
1373 }
1374}
1375
1376pub(crate) struct AllTransactions<T: PoolTransaction> {
1381 minimal_protocol_basefee: u64,
1385 block_gas_limit: u64,
1387 max_account_slots: usize,
1389 by_hash: HashMap<TxHash, Arc<ValidPoolTransaction<T>>>,
1391 txs: BTreeMap<TransactionId, PoolInternalTransaction<T>>,
1393 sender_info: FxHashMap<SenderId, SenderInfo>,
1395 tx_counter: FxHashMap<SenderId, usize>,
1397 last_seen_block_number: u64,
1399 last_seen_block_hash: B256,
1401 pending_fees: PendingFees,
1403 price_bumps: PriceBumpConfig,
1405 local_transactions_config: LocalTransactionConfig,
1407 auths: FxHashMap<SenderId, HashSet<TxHash>>,
1409 metrics: AllTransactionsMetrics,
1411}
1412
1413impl<T: PoolTransaction> AllTransactions<T> {
1414 fn new(config: &PoolConfig) -> Self {
1416 Self {
1417 max_account_slots: config.max_account_slots,
1418 price_bumps: config.price_bumps,
1419 local_transactions_config: config.local_transactions_config.clone(),
1420 minimal_protocol_basefee: config.minimal_protocol_basefee,
1421 block_gas_limit: config.gas_limit,
1422 ..Default::default()
1423 }
1424 }
1425
1426 #[expect(dead_code)]
1428 pub(crate) fn hashes_iter(&self) -> impl Iterator<Item = TxHash> + '_ {
1429 self.by_hash.keys().copied()
1430 }
1431
1432 pub(crate) fn transactions_iter(
1434 &self,
1435 ) -> impl Iterator<Item = &Arc<ValidPoolTransaction<T>>> + '_ {
1436 self.by_hash.values()
1437 }
1438
1439 pub(crate) fn contains(&self, tx_hash: &TxHash) -> bool {
1441 self.by_hash.contains_key(tx_hash)
1442 }
1443
1444 pub(crate) fn get(&self, id: &TransactionId) -> Option<&PoolInternalTransaction<T>> {
1446 self.txs.get(id)
1447 }
1448
1449 pub(crate) fn tx_inc(&mut self, sender: SenderId) {
1451 let count = self.tx_counter.entry(sender).or_default();
1452 *count += 1;
1453 self.metrics.all_transactions_by_all_senders.increment(1.0);
1454 }
1455
1456 pub(crate) fn tx_decr(&mut self, sender: SenderId) {
1458 if let hash_map::Entry::Occupied(mut entry) = self.tx_counter.entry(sender) {
1459 let count = entry.get_mut();
1460 if *count == 1 {
1461 entry.remove();
1462 self.sender_info.remove(&sender);
1463 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1464 return
1465 }
1466 *count -= 1;
1467 self.metrics.all_transactions_by_all_senders.decrement(1.0);
1468 }
1469 }
1470
1471 fn set_block_info(&mut self, block_info: BlockInfo) {
1473 let BlockInfo {
1474 block_gas_limit,
1475 last_seen_block_hash,
1476 last_seen_block_number,
1477 pending_basefee,
1478 pending_blob_fee,
1479 } = block_info;
1480 self.last_seen_block_number = last_seen_block_number;
1481 self.last_seen_block_hash = last_seen_block_hash;
1482
1483 self.pending_fees.base_fee = pending_basefee;
1484 self.metrics.base_fee.set(pending_basefee as f64);
1485
1486 self.block_gas_limit = block_gas_limit;
1487
1488 if let Some(pending_blob_fee) = pending_blob_fee {
1489 self.pending_fees.blob_fee = pending_blob_fee;
1490 self.metrics.blob_base_fee.set(pending_blob_fee as f64);
1491 }
1492 }
1493
1494 pub(crate) fn update_size_metrics(&self) {
1496 self.metrics.all_transactions_by_hash.set(self.by_hash.len() as f64);
1497 self.metrics.all_transactions_by_id.set(self.txs.len() as f64);
1498 }
1499
1500 pub(crate) fn update(
1517 &mut self,
1518 changed_accounts: &FxHashMap<SenderId, SenderInfo>,
1519 ) -> Vec<PoolUpdate> {
1520 let mut updates = Vec::with_capacity(64);
1522
1523 let mut iter = self.txs.iter_mut().peekable();
1524
1525 'transactions: while let Some((id, tx)) = iter.next() {
1535 macro_rules! next_sender {
1536 ($iter:ident) => {
1537 'this: while let Some((peek, _)) = iter.peek() {
1538 if peek.sender != id.sender {
1539 break 'this
1540 }
1541 iter.next();
1542 }
1543 };
1544 }
1545
1546 let changed_balance = if let Some(info) = changed_accounts.get(&id.sender) {
1549 if id.nonce < info.state_nonce {
1551 updates.push(PoolUpdate {
1552 id: *tx.transaction.id(),
1553 current: tx.subpool,
1554 destination: Destination::Discard,
1555 });
1556 continue 'transactions
1557 }
1558
1559 let ancestor = TransactionId::ancestor(id.nonce, info.state_nonce, id.sender);
1560 if ancestor.is_none() {
1562 tx.state.insert(TxState::NO_NONCE_GAPS);
1563 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1564 tx.cumulative_cost = U256::ZERO;
1565 if tx.transaction.cost() > &info.balance {
1566 tx.state.remove(TxState::ENOUGH_BALANCE);
1568 } else {
1569 tx.state.insert(TxState::ENOUGH_BALANCE);
1570 }
1571 }
1572
1573 Some(&info.balance)
1574 } else {
1575 None
1576 };
1577
1578 if tx.state.has_nonce_gap() {
1580 next_sender!(iter);
1581 continue 'transactions
1582 }
1583
1584 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1586
1587 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1589 Self::record_subpool_update(&mut updates, tx);
1591
1592 let mut has_parked_ancestor = !tx.state.is_pending();
1594
1595 let mut cumulative_cost = tx.next_cumulative_cost();
1596
1597 let mut next_nonce_in_line = tx.transaction.nonce().saturating_add(1);
1599
1600 while let Some((peek, tx)) = iter.peek_mut() {
1602 if peek.sender != id.sender {
1603 continue 'transactions
1605 }
1606
1607 if tx.transaction.nonce() == next_nonce_in_line {
1608 tx.state.insert(TxState::NO_NONCE_GAPS);
1610 } else {
1611 next_sender!(iter);
1613 continue 'transactions
1614 }
1615
1616 next_nonce_in_line = next_nonce_in_line.saturating_add(1);
1618
1619 tx.cumulative_cost = cumulative_cost;
1621 cumulative_cost = tx.next_cumulative_cost();
1623
1624 if let Some(changed_balance) = changed_balance {
1626 if &cumulative_cost > changed_balance {
1627 tx.state.remove(TxState::ENOUGH_BALANCE);
1629 } else {
1630 tx.state.insert(TxState::ENOUGH_BALANCE);
1631 }
1632 }
1633
1634 if has_parked_ancestor {
1636 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
1637 } else {
1638 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
1639 }
1640 has_parked_ancestor = !tx.state.is_pending();
1641
1642 Self::update_tx_base_fee(self.pending_fees.base_fee, tx);
1644 Self::record_subpool_update(&mut updates, tx);
1645
1646 iter.next();
1648 }
1649 }
1650
1651 updates
1652 }
1653
1654 fn record_subpool_update(updates: &mut Vec<PoolUpdate>, tx: &mut PoolInternalTransaction<T>) {
1659 let current_pool = tx.subpool;
1660 tx.subpool = tx.state.into();
1661 if current_pool != tx.subpool {
1662 updates.push(PoolUpdate {
1663 id: *tx.transaction.id(),
1664 current: current_pool,
1665 destination: tx.subpool.into(),
1666 })
1667 }
1668 }
1669
1670 fn update_tx_base_fee(pending_block_base_fee: u64, tx: &mut PoolInternalTransaction<T>) {
1672 match tx.transaction.max_fee_per_gas().cmp(&(pending_block_base_fee as u128)) {
1674 Ordering::Greater | Ordering::Equal => {
1675 tx.state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
1676 }
1677 Ordering::Less => {
1678 tx.state.remove(TxState::ENOUGH_FEE_CAP_BLOCK);
1679 }
1680 }
1681 }
1682
1683 pub(crate) fn txs_iter(
1686 &self,
1687 sender: SenderId,
1688 ) -> impl Iterator<Item = (&TransactionId, &PoolInternalTransaction<T>)> + '_ {
1689 self.txs
1690 .range((sender.start_bound(), Unbounded))
1691 .take_while(move |(other, _)| sender == other.sender)
1692 }
1693
1694 #[cfg(test)]
1697 #[expect(dead_code)]
1698 pub(crate) fn txs_iter_mut(
1699 &mut self,
1700 sender: SenderId,
1701 ) -> impl Iterator<Item = (&TransactionId, &mut PoolInternalTransaction<T>)> + '_ {
1702 self.txs
1703 .range_mut((sender.start_bound(), Unbounded))
1704 .take_while(move |(other, _)| sender == other.sender)
1705 }
1706
1707 pub(crate) fn descendant_txs_exclusive<'a, 'b: 'a>(
1711 &'a self,
1712 id: &'b TransactionId,
1713 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1714 self.txs.range((Excluded(id), Unbounded)).take_while(|(other, _)| id.sender == other.sender)
1715 }
1716
1717 pub(crate) fn descendant_txs_inclusive<'a, 'b: 'a>(
1722 &'a self,
1723 id: &'b TransactionId,
1724 ) -> impl Iterator<Item = (&'a TransactionId, &'a PoolInternalTransaction<T>)> + 'a {
1725 self.txs.range(id..).take_while(|(other, _)| id.sender == other.sender)
1726 }
1727
1728 pub(crate) fn descendant_txs_mut<'a, 'b: 'a>(
1733 &'a mut self,
1734 id: &'b TransactionId,
1735 ) -> impl Iterator<Item = (&'a TransactionId, &'a mut PoolInternalTransaction<T>)> + 'a {
1736 self.txs.range_mut(id..).take_while(|(other, _)| id.sender == other.sender)
1737 }
1738
1739 pub(crate) fn remove_transaction_by_hash(
1741 &mut self,
1742 tx_hash: &B256,
1743 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1744 let tx = self.by_hash.remove(tx_hash)?;
1745 let internal = self.txs.remove(&tx.transaction_id)?;
1746 self.remove_auths(&internal);
1747 self.tx_decr(tx.sender_id());
1749 Some((tx, internal.subpool))
1750 }
1751
1752 pub(crate) fn remove_transaction_by_id(
1756 &mut self,
1757 tx_id: &TransactionId,
1758 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1759 let internal = self.txs.remove(tx_id)?;
1760 let tx = self.by_hash.remove(internal.transaction.hash())?;
1761 self.remove_auths(&internal);
1762 self.tx_decr(tx.sender_id());
1764 Some((tx, internal.subpool))
1765 }
1766
1767 pub(crate) fn park_descendant_transactions(
1769 &mut self,
1770 tx_id: &TransactionId,
1771 ) -> Vec<PoolUpdate> {
1772 let mut updates = Vec::new();
1773
1774 for (id, tx) in self.descendant_txs_mut(tx_id) {
1775 let current_pool = tx.subpool;
1776
1777 tx.state.remove(TxState::NO_NONCE_GAPS);
1778
1779 tx.subpool = tx.state.into();
1781
1782 if current_pool != tx.subpool {
1784 updates.push(PoolUpdate {
1785 id: *id,
1786 current: current_pool,
1787 destination: tx.subpool.into(),
1788 })
1789 }
1790 }
1791
1792 updates
1793 }
1794
1795 pub(crate) fn remove_transaction(
1801 &mut self,
1802 id: &TransactionId,
1803 ) -> Option<(Arc<ValidPoolTransaction<T>>, SubPool)> {
1804 let internal = self.txs.remove(id)?;
1805
1806 self.tx_decr(internal.transaction.sender_id());
1808
1809 let result =
1810 self.by_hash.remove(internal.transaction.hash()).map(|tx| (tx, internal.subpool));
1811
1812 self.remove_auths(&internal);
1813
1814 result
1815 }
1816
1817 fn remove_auths(&mut self, tx: &PoolInternalTransaction<T>) {
1821 let Some(auths) = &tx.transaction.authority_ids else { return };
1822
1823 let tx_hash = tx.transaction.hash();
1824 for auth in auths {
1825 if let Some(list) = self.auths.get_mut(auth) {
1826 list.remove(tx_hash);
1827 if list.is_empty() {
1828 self.auths.remove(auth);
1829 }
1830 }
1831 }
1832 }
1833
1834 #[inline]
1840 fn contains_conflicting_transaction(&self, tx: &ValidPoolTransaction<T>) -> bool {
1841 self.txs_iter(tx.transaction_id.sender)
1842 .next()
1843 .is_some_and(|(_, existing)| tx.tx_type_conflicts_with(&existing.transaction))
1844 }
1845
1846 fn ensure_valid(
1855 &self,
1856 transaction: ValidPoolTransaction<T>,
1857 on_chain_nonce: u64,
1858 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1859 if !self.local_transactions_config.is_local(transaction.origin, transaction.sender_ref()) {
1860 let current_txs =
1861 self.tx_counter.get(&transaction.sender_id()).copied().unwrap_or_default();
1862
1863 if current_txs >= self.max_account_slots && transaction.nonce() > on_chain_nonce {
1866 return Err(InsertErr::ExceededSenderTransactionsCapacity {
1867 transaction: Arc::new(transaction),
1868 })
1869 }
1870 }
1871 if transaction.gas_limit() > self.block_gas_limit {
1872 return Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas {
1873 block_gas_limit: self.block_gas_limit,
1874 tx_gas_limit: transaction.gas_limit(),
1875 transaction: Arc::new(transaction),
1876 })
1877 }
1878
1879 if self.contains_conflicting_transaction(&transaction) {
1880 return Err(InsertErr::TxTypeConflict { transaction: Arc::new(transaction) })
1882 }
1883
1884 Ok(transaction)
1885 }
1886
1887 fn ensure_valid_blob_transaction(
1893 &self,
1894 new_blob_tx: ValidPoolTransaction<T>,
1895 on_chain_balance: U256,
1896 ancestor: Option<TransactionId>,
1897 ) -> Result<ValidPoolTransaction<T>, InsertErr<T>> {
1898 if let Some(ancestor) = ancestor {
1899 let Some(ancestor_tx) = self.txs.get(&ancestor) else {
1900 self.metrics.blob_transactions_nonce_gaps.increment(1);
1902 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1903 };
1904 if ancestor_tx.state.has_nonce_gap() {
1905 self.metrics.blob_transactions_nonce_gaps.increment(1);
1908 return Err(InsertErr::BlobTxHasNonceGap { transaction: Arc::new(new_blob_tx) })
1909 }
1910
1911 let mut cumulative_cost = ancestor_tx.next_cumulative_cost() + new_blob_tx.cost();
1913
1914 if cumulative_cost > on_chain_balance {
1916 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1918 }
1919
1920 let id = new_blob_tx.transaction_id;
1923 let mut descendants = self.descendant_txs_inclusive(&id).peekable();
1924 if let Some((maybe_replacement, _)) = descendants.peek() &&
1925 **maybe_replacement == new_blob_tx.transaction_id
1926 {
1927 descendants.next();
1929
1930 for (_, tx) in descendants {
1932 cumulative_cost += tx.transaction.cost();
1933 if tx.transaction.is_eip4844() && cumulative_cost > on_chain_balance {
1934 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1936 }
1937 }
1938 }
1939 } else if new_blob_tx.cost() > &on_chain_balance {
1940 return Err(InsertErr::Overdraft { transaction: Arc::new(new_blob_tx) })
1942 }
1943
1944 Ok(new_blob_tx)
1945 }
1946
1947 pub(crate) fn insert_tx(
1979 &mut self,
1980 transaction: ValidPoolTransaction<T>,
1981 on_chain_balance: U256,
1982 on_chain_nonce: u64,
1983 ) -> InsertResult<T> {
1984 assert!(on_chain_nonce <= transaction.nonce(), "Invalid transaction");
1985
1986 let mut transaction = self.ensure_valid(transaction, on_chain_nonce)?;
1987
1988 let inserted_tx_id = *transaction.id();
1989 let mut state = TxState::default();
1990 let mut cumulative_cost = U256::ZERO;
1991 let mut updates = Vec::new();
1992
1993 state.insert(TxState::NOT_TOO_MUCH_GAS);
1995
1996 let ancestor = TransactionId::ancestor(
1999 transaction.transaction.nonce(),
2000 on_chain_nonce,
2001 inserted_tx_id.sender,
2002 );
2003
2004 if transaction.is_eip4844() {
2007 state.insert(TxState::BLOB_TRANSACTION);
2008
2009 transaction =
2010 self.ensure_valid_blob_transaction(transaction, on_chain_balance, ancestor)?;
2011 let blob_fee_cap = transaction.transaction.max_fee_per_blob_gas().unwrap_or_default();
2012 if blob_fee_cap >= self.pending_fees.blob_fee {
2013 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
2014 }
2015 } else {
2016 state.insert(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK);
2018 }
2019
2020 let transaction = Arc::new(transaction);
2021
2022 if ancestor.is_none() {
2024 state.insert(TxState::NO_NONCE_GAPS);
2025 state.insert(TxState::NO_PARKED_ANCESTORS);
2026 }
2027
2028 let fee_cap = transaction.max_fee_per_gas();
2030
2031 if fee_cap < self.minimal_protocol_basefee as u128 {
2032 return Err(InsertErr::FeeCapBelowMinimumProtocolFeeCap { transaction, fee_cap })
2033 }
2034 if fee_cap >= self.pending_fees.base_fee as u128 {
2035 state.insert(TxState::ENOUGH_FEE_CAP_BLOCK);
2036 }
2037
2038 let mut replaced_tx = None;
2040
2041 let pool_tx = PoolInternalTransaction {
2042 transaction: Arc::clone(&transaction),
2043 subpool: state.into(),
2044 state,
2045 cumulative_cost,
2046 };
2047
2048 match self.txs.entry(*transaction.id()) {
2050 Entry::Vacant(entry) => {
2051 self.by_hash.insert(*pool_tx.transaction.hash(), pool_tx.transaction.clone());
2053 entry.insert(pool_tx);
2054 }
2055 Entry::Occupied(mut entry) => {
2056 let existing_transaction = entry.get().transaction.as_ref();
2058 let maybe_replacement = transaction.as_ref();
2059
2060 if existing_transaction.is_underpriced(maybe_replacement, &self.price_bumps) {
2062 return Err(InsertErr::Underpriced {
2063 transaction: pool_tx.transaction,
2064 existing: *entry.get().transaction.hash(),
2065 })
2066 }
2067 let new_hash = *pool_tx.transaction.hash();
2068 let new_transaction = pool_tx.transaction.clone();
2069 let replaced = entry.insert(pool_tx);
2070 self.by_hash.remove(replaced.transaction.hash());
2071 self.by_hash.insert(new_hash, new_transaction);
2072
2073 self.remove_auths(&replaced);
2074
2075 replaced_tx = Some((replaced.transaction, replaced.subpool));
2077 }
2078 }
2079
2080 if let Some(auths) = &transaction.authority_ids {
2081 let tx_hash = transaction.hash();
2082 for auth in auths {
2083 self.auths.entry(*auth).or_default().insert(*tx_hash);
2084 }
2085 }
2086
2087 let on_chain_id = TransactionId::new(transaction.sender_id(), on_chain_nonce);
2089 {
2090 let mut next_nonce = on_chain_id.nonce;
2092
2093 let mut has_parked_ancestor = false;
2097
2098 for (id, tx) in self.descendant_txs_mut(&on_chain_id) {
2101 let current_pool = tx.subpool;
2102
2103 if next_nonce != id.nonce {
2105 break
2106 }
2107
2108 tx.state.insert(TxState::NO_NONCE_GAPS);
2110
2111 tx.cumulative_cost = cumulative_cost;
2113
2114 cumulative_cost = tx.next_cumulative_cost();
2116
2117 if cumulative_cost > on_chain_balance {
2118 tx.state.remove(TxState::ENOUGH_BALANCE);
2120 } else {
2121 tx.state.insert(TxState::ENOUGH_BALANCE);
2122 }
2123
2124 if has_parked_ancestor {
2126 tx.state.remove(TxState::NO_PARKED_ANCESTORS);
2127 } else {
2128 tx.state.insert(TxState::NO_PARKED_ANCESTORS);
2129 }
2130 has_parked_ancestor = !tx.state.is_pending();
2131
2132 tx.subpool = tx.state.into();
2134
2135 if inserted_tx_id.eq(id) {
2136 state = tx.state;
2138 } else {
2139 if current_pool != tx.subpool {
2141 updates.push(PoolUpdate {
2142 id: *id,
2143 current: current_pool,
2144 destination: tx.subpool.into(),
2145 })
2146 }
2147 }
2148
2149 next_nonce = id.next_nonce();
2151 }
2152 }
2153
2154 if replaced_tx.is_none() {
2156 self.tx_inc(inserted_tx_id.sender);
2157 }
2158
2159 self.update_size_metrics();
2160
2161 Ok(InsertOk { transaction, move_to: state.into(), state, replaced_tx, updates })
2162 }
2163
2164 pub(crate) fn len(&self) -> usize {
2166 self.txs.len()
2167 }
2168
2169 pub(crate) fn is_empty(&self) -> bool {
2171 self.txs.is_empty()
2172 }
2173
2174 #[cfg(any(test, feature = "test-utils"))]
2176 pub(crate) fn assert_invariants(&self) {
2177 assert_eq!(self.by_hash.len(), self.txs.len(), "by_hash.len() != txs.len()");
2178 assert!(self.auths.len() <= self.txs.len(), "auths.len() > txs.len()");
2179 }
2180}
2181
2182#[cfg(test)]
2183impl<T: PoolTransaction> AllTransactions<T> {
2184 pub(crate) fn tx_count(&self, sender: SenderId) -> usize {
2188 self.tx_counter.get(&sender).copied().unwrap_or_default()
2189 }
2190}
2191
2192impl<T: PoolTransaction> Default for AllTransactions<T> {
2193 fn default() -> Self {
2194 Self {
2195 max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
2196 minimal_protocol_basefee: MIN_PROTOCOL_BASE_FEE,
2197 block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
2198 by_hash: Default::default(),
2199 txs: Default::default(),
2200 sender_info: Default::default(),
2201 tx_counter: Default::default(),
2202 last_seen_block_number: Default::default(),
2203 last_seen_block_hash: Default::default(),
2204 pending_fees: Default::default(),
2205 price_bumps: Default::default(),
2206 local_transactions_config: Default::default(),
2207 auths: Default::default(),
2208 metrics: Default::default(),
2209 }
2210 }
2211}
2212
2213#[derive(Debug, Clone)]
2215pub(crate) struct PendingFees {
2216 pub(crate) base_fee: u64,
2218 pub(crate) blob_fee: u128,
2220}
2221
2222impl Default for PendingFees {
2223 fn default() -> Self {
2224 Self { base_fee: Default::default(), blob_fee: BLOB_TX_MIN_BLOB_GASPRICE }
2225 }
2226}
2227
2228pub(crate) type InsertResult<T> = Result<InsertOk<T>, InsertErr<T>>;
2230
2231#[derive(Debug)]
2233pub(crate) enum InsertErr<T: PoolTransaction> {
2234 Underpriced {
2236 transaction: Arc<ValidPoolTransaction<T>>,
2237 #[expect(dead_code)]
2238 existing: TxHash,
2239 },
2240 BlobTxHasNonceGap { transaction: Arc<ValidPoolTransaction<T>> },
2242 Overdraft { transaction: Arc<ValidPoolTransaction<T>> },
2245 FeeCapBelowMinimumProtocolFeeCap { transaction: Arc<ValidPoolTransaction<T>>, fee_cap: u128 },
2249 ExceededSenderTransactionsCapacity { transaction: Arc<ValidPoolTransaction<T>> },
2253 TxGasLimitMoreThanAvailableBlockGas {
2255 transaction: Arc<ValidPoolTransaction<T>>,
2256 block_gas_limit: u64,
2257 tx_gas_limit: u64,
2258 },
2259 TxTypeConflict { transaction: Arc<ValidPoolTransaction<T>> },
2261}
2262
2263#[derive(Debug)]
2265pub(crate) struct InsertOk<T: PoolTransaction> {
2266 transaction: Arc<ValidPoolTransaction<T>>,
2268 move_to: SubPool,
2270 state: TxState,
2272 replaced_tx: Option<(Arc<ValidPoolTransaction<T>>, SubPool)>,
2274 updates: Vec<PoolUpdate>,
2276}
2277
2278#[derive(Debug)]
2281pub(crate) struct PoolInternalTransaction<T: PoolTransaction> {
2282 pub(crate) transaction: Arc<ValidPoolTransaction<T>>,
2284 pub(crate) subpool: SubPool,
2286 pub(crate) state: TxState,
2289 pub(crate) cumulative_cost: U256,
2294}
2295
2296impl<T: PoolTransaction> PoolInternalTransaction<T> {
2299 fn next_cumulative_cost(&self) -> U256 {
2300 self.cumulative_cost + self.transaction.cost()
2301 }
2302}
2303
2304#[derive(Debug, Clone, Default)]
2306pub(crate) struct SenderInfo {
2307 pub(crate) state_nonce: u64,
2309 pub(crate) balance: U256,
2311}
2312
2313impl SenderInfo {
2316 const fn update(&mut self, state_nonce: u64, balance: U256) {
2318 *self = Self { state_nonce, balance };
2319 }
2320}
2321
2322#[cfg(test)]
2323mod tests {
2324 use super::*;
2325 use crate::{
2326 test_utils::{MockOrdering, MockTransaction, MockTransactionFactory, MockTransactionSet},
2327 traits::TransactionOrigin,
2328 SubPoolLimit,
2329 };
2330 use alloy_consensus::{Transaction, TxType};
2331 use alloy_primitives::address;
2332
2333 #[test]
2334 fn test_insert_blob() {
2335 let on_chain_balance = U256::MAX;
2336 let on_chain_nonce = 0;
2337 let mut f = MockTransactionFactory::default();
2338 let mut pool = AllTransactions::default();
2339 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2340 let valid_tx = f.validated(tx);
2341 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2342 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2343 assert!(updates.is_empty());
2344 assert!(replaced_tx.is_none());
2345 assert!(state.contains(TxState::NO_NONCE_GAPS));
2346 assert!(state.contains(TxState::ENOUGH_BALANCE));
2347 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2348 assert_eq!(move_to, SubPool::Pending);
2349
2350 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2351 assert_eq!(inserted.subpool, SubPool::Pending);
2352 }
2353
2354 #[test]
2355 fn test_insert_blob_not_enough_blob_fee() {
2356 let on_chain_balance = U256::MAX;
2357 let on_chain_nonce = 0;
2358 let mut f = MockTransactionFactory::default();
2359 let mut pool = AllTransactions {
2360 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2361 ..Default::default()
2362 };
2363 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2364 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2365 let valid_tx = f.validated(tx);
2366 let InsertOk { state, .. } =
2367 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2368 assert!(state.contains(TxState::NO_NONCE_GAPS));
2369 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2370
2371 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2372 }
2373
2374 #[test]
2375 fn test_valid_tx_with_decreasing_blob_fee() {
2376 let on_chain_balance = U256::MAX;
2377 let on_chain_nonce = 0;
2378 let mut f = MockTransactionFactory::default();
2379 let mut pool = AllTransactions {
2380 pending_fees: PendingFees { blob_fee: 10_000_000, ..Default::default() },
2381 ..Default::default()
2382 };
2383 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2384
2385 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap() + 1;
2386 let valid_tx = f.validated(tx.clone());
2387 let InsertOk { state, .. } =
2388 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2389 assert!(state.contains(TxState::NO_NONCE_GAPS));
2390 assert!(!state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2391
2392 let _ = pool.txs.get(&valid_tx.transaction_id).unwrap();
2393 pool.remove_transaction(&valid_tx.transaction_id);
2394
2395 pool.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
2396 let InsertOk { state, .. } =
2397 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2398 assert!(state.contains(TxState::NO_NONCE_GAPS));
2399 assert!(state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2400 }
2401
2402 #[test]
2403 fn test_demote_valid_tx_with_increasing_blob_fee() {
2404 let on_chain_balance = U256::MAX;
2405 let on_chain_nonce = 0;
2406 let mut f = MockTransactionFactory::default();
2407 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2408 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2409
2410 let mut block_info = pool.block_info();
2412 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2413 pool.set_block_info(block_info);
2414
2415 let validated = f.validated(tx.clone());
2416 let id = *validated.id();
2417 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2418
2419 assert!(pool.blob_pool.is_empty());
2421 assert_eq!(pool.pending_pool.len(), 1);
2422
2423 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2425 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2426 assert_eq!(internal_tx.subpool, SubPool::Pending);
2427
2428 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2430 pool.set_block_info(block_info);
2431
2432 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2434 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2435 assert_eq!(internal_tx.subpool, SubPool::Blob);
2436
2437 assert_eq!(pool.blob_pool.len(), 1);
2439 assert!(pool.pending_pool.is_empty());
2440 }
2441
2442 #[test]
2443 fn test_promote_valid_tx_with_decreasing_blob_fee() {
2444 let on_chain_balance = U256::MAX;
2445 let on_chain_nonce = 0;
2446 let mut f = MockTransactionFactory::default();
2447 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2448 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2449
2450 let mut block_info = pool.block_info();
2452 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap() + 1);
2453 pool.set_block_info(block_info);
2454
2455 let validated = f.validated(tx.clone());
2456 let id = *validated.id();
2457 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2458
2459 assert!(pool.pending_pool.is_empty());
2461 assert_eq!(pool.blob_pool.len(), 1);
2462
2463 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2465 assert!(!internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2466 assert_eq!(internal_tx.subpool, SubPool::Blob);
2467
2468 block_info.pending_blob_fee = Some(tx.max_fee_per_blob_gas().unwrap());
2470 pool.set_block_info(block_info);
2471
2472 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2474 assert!(internal_tx.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
2475 assert_eq!(internal_tx.subpool, SubPool::Pending);
2476
2477 assert_eq!(pool.pending_pool.len(), 1);
2479 assert!(pool.blob_pool.is_empty());
2480 }
2481
2482 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
2484 struct PromotionTest {
2485 basefee: u64,
2487 blobfee: u128,
2489 subpool: SubPool,
2491 basefee_update: u64,
2493 blobfee_update: u128,
2495 new_subpool: SubPool,
2497 }
2498
2499 impl PromotionTest {
2500 const fn opposite(&self) -> Self {
2502 Self {
2503 basefee: self.basefee_update,
2504 blobfee: self.blobfee_update,
2505 subpool: self.new_subpool,
2506 blobfee_update: self.blobfee,
2507 basefee_update: self.basefee,
2508 new_subpool: self.subpool,
2509 }
2510 }
2511
2512 fn assert_subpool_lengths<T: TransactionOrdering>(
2513 &self,
2514 pool: &TxPool<T>,
2515 failure_message: String,
2516 check_subpool: SubPool,
2517 ) {
2518 match check_subpool {
2519 SubPool::Blob => {
2520 assert_eq!(pool.blob_pool.len(), 1, "{failure_message}");
2521 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2522 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2523 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2524 }
2525 SubPool::Pending => {
2526 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2527 assert_eq!(pool.pending_pool.len(), 1, "{failure_message}");
2528 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2529 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2530 }
2531 SubPool::BaseFee => {
2532 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2533 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2534 assert_eq!(pool.basefee_pool.len(), 1, "{failure_message}");
2535 assert!(pool.queued_pool.is_empty(), "{failure_message}");
2536 }
2537 SubPool::Queued => {
2538 assert!(pool.blob_pool.is_empty(), "{failure_message}");
2539 assert!(pool.pending_pool.is_empty(), "{failure_message}");
2540 assert!(pool.basefee_pool.is_empty(), "{failure_message}");
2541 assert_eq!(pool.queued_pool.len(), 1, "{failure_message}");
2542 }
2543 }
2544 }
2545
2546 fn assert_single_tx_starting_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2550 self.assert_subpool_lengths(
2551 pool,
2552 format!("pool length check failed at start of test: {self:?}"),
2553 self.subpool,
2554 );
2555 }
2556
2557 fn assert_single_tx_ending_subpool<T: TransactionOrdering>(&self, pool: &TxPool<T>) {
2561 self.assert_subpool_lengths(
2562 pool,
2563 format!("pool length check failed at end of test: {self:?}"),
2564 self.new_subpool,
2565 );
2566 }
2567 }
2568
2569 #[test]
2570 fn test_promote_blob_tx_with_both_pending_fee_updates() {
2571 let on_chain_balance = U256::MAX;
2574 let on_chain_nonce = 0;
2575 let mut f = MockTransactionFactory::default();
2576 let tx = MockTransaction::eip4844().inc_price().inc_limit();
2577
2578 let max_fee_per_blob_gas = tx.max_fee_per_blob_gas().unwrap();
2579 let max_fee_per_gas = tx.max_fee_per_gas() as u64;
2580
2581 let mut expected_promotions = vec![
2583 PromotionTest {
2584 blobfee: max_fee_per_blob_gas + 1,
2585 basefee: max_fee_per_gas + 1,
2586 subpool: SubPool::Blob,
2587 blobfee_update: max_fee_per_blob_gas + 1,
2588 basefee_update: max_fee_per_gas + 1,
2589 new_subpool: SubPool::Blob,
2590 },
2591 PromotionTest {
2592 blobfee: max_fee_per_blob_gas + 1,
2593 basefee: max_fee_per_gas + 1,
2594 subpool: SubPool::Blob,
2595 blobfee_update: max_fee_per_blob_gas,
2596 basefee_update: max_fee_per_gas + 1,
2597 new_subpool: SubPool::Blob,
2598 },
2599 PromotionTest {
2600 blobfee: max_fee_per_blob_gas + 1,
2601 basefee: max_fee_per_gas + 1,
2602 subpool: SubPool::Blob,
2603 blobfee_update: max_fee_per_blob_gas + 1,
2604 basefee_update: max_fee_per_gas,
2605 new_subpool: SubPool::Blob,
2606 },
2607 PromotionTest {
2608 blobfee: max_fee_per_blob_gas + 1,
2609 basefee: max_fee_per_gas + 1,
2610 subpool: SubPool::Blob,
2611 blobfee_update: max_fee_per_blob_gas,
2612 basefee_update: max_fee_per_gas,
2613 new_subpool: SubPool::Pending,
2614 },
2615 PromotionTest {
2616 blobfee: max_fee_per_blob_gas,
2617 basefee: max_fee_per_gas + 1,
2618 subpool: SubPool::Blob,
2619 blobfee_update: max_fee_per_blob_gas,
2620 basefee_update: max_fee_per_gas,
2621 new_subpool: SubPool::Pending,
2622 },
2623 PromotionTest {
2624 blobfee: max_fee_per_blob_gas + 1,
2625 basefee: max_fee_per_gas,
2626 subpool: SubPool::Blob,
2627 blobfee_update: max_fee_per_blob_gas,
2628 basefee_update: max_fee_per_gas,
2629 new_subpool: SubPool::Pending,
2630 },
2631 PromotionTest {
2632 blobfee: max_fee_per_blob_gas,
2633 basefee: max_fee_per_gas,
2634 subpool: SubPool::Pending,
2635 blobfee_update: max_fee_per_blob_gas,
2636 basefee_update: max_fee_per_gas,
2637 new_subpool: SubPool::Pending,
2638 },
2639 ];
2640
2641 let reversed = expected_promotions.iter().map(|test| test.opposite()).collect::<Vec<_>>();
2643 expected_promotions.extend(reversed);
2644
2645 let expected_promotions = expected_promotions.into_iter().collect::<HashSet<_>>();
2647
2648 for promotion_test in &expected_promotions {
2649 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2650
2651 let mut block_info = pool.block_info();
2653
2654 block_info.pending_blob_fee = Some(promotion_test.blobfee);
2655 block_info.pending_basefee = promotion_test.basefee;
2656 pool.set_block_info(block_info);
2657
2658 let validated = f.validated(tx.clone());
2659 let id = *validated.id();
2660 pool.add_transaction(validated, on_chain_balance, on_chain_nonce, None).unwrap();
2661
2662 promotion_test.assert_single_tx_starting_subpool(&pool);
2664
2665 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2667 assert_eq!(
2668 internal_tx.subpool, promotion_test.subpool,
2669 "Subpools do not match at start of test: {promotion_test:?}"
2670 );
2671
2672 block_info.pending_basefee = promotion_test.basefee_update;
2674 block_info.pending_blob_fee = Some(promotion_test.blobfee_update);
2675 pool.set_block_info(block_info);
2676
2677 let internal_tx = pool.all_transactions.txs.get(&id).unwrap();
2679 assert_eq!(
2680 internal_tx.subpool, promotion_test.new_subpool,
2681 "Subpools do not match at end of test: {promotion_test:?}"
2682 );
2683
2684 promotion_test.assert_single_tx_ending_subpool(&pool);
2686 }
2687 }
2688
2689 #[test]
2690 fn test_insert_pending() {
2691 let on_chain_balance = U256::MAX;
2692 let on_chain_nonce = 0;
2693 let mut f = MockTransactionFactory::default();
2694 let mut pool = AllTransactions::default();
2695 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2696 let valid_tx = f.validated(tx);
2697 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2698 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2699 assert!(updates.is_empty());
2700 assert!(replaced_tx.is_none());
2701 assert!(state.contains(TxState::NO_NONCE_GAPS));
2702 assert!(state.contains(TxState::ENOUGH_BALANCE));
2703 assert_eq!(move_to, SubPool::Pending);
2704
2705 let inserted = pool.txs.get(&valid_tx.transaction_id).unwrap();
2706 assert_eq!(inserted.subpool, SubPool::Pending);
2707 }
2708
2709 #[test]
2710 fn test_simple_insert() {
2711 let on_chain_balance = U256::ZERO;
2712 let on_chain_nonce = 0;
2713 let mut f = MockTransactionFactory::default();
2714 let mut pool = AllTransactions::default();
2715 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
2716 tx.set_priority_fee(100);
2717 tx.set_max_fee(100);
2718 let valid_tx = f.validated(tx.clone());
2719 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2720 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2721 assert!(updates.is_empty());
2722 assert!(replaced_tx.is_none());
2723 assert!(state.contains(TxState::NO_NONCE_GAPS));
2724 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2725 assert_eq!(move_to, SubPool::Queued);
2726
2727 assert_eq!(pool.len(), 1);
2728 assert!(pool.contains(valid_tx.hash()));
2729 let expected_state = TxState::ENOUGH_FEE_CAP_BLOCK | TxState::NO_NONCE_GAPS;
2730 let inserted = pool.get(valid_tx.id()).unwrap();
2731 assert!(inserted.state.intersects(expected_state));
2732
2733 let res = pool.insert_tx(valid_tx, on_chain_balance, on_chain_nonce);
2735 res.unwrap_err();
2736 assert_eq!(pool.len(), 1);
2737
2738 let valid_tx = f.validated(tx.next());
2739 let InsertOk { updates, replaced_tx, move_to, state, .. } =
2740 pool.insert_tx(valid_tx.clone(), on_chain_balance, on_chain_nonce).unwrap();
2741
2742 assert!(updates.is_empty());
2743 assert!(replaced_tx.is_none());
2744 assert!(state.contains(TxState::NO_NONCE_GAPS));
2745 assert!(!state.contains(TxState::ENOUGH_BALANCE));
2746 assert_eq!(move_to, SubPool::Queued);
2747
2748 assert!(pool.contains(valid_tx.hash()));
2749 assert_eq!(pool.len(), 2);
2750 let inserted = pool.get(valid_tx.id()).unwrap();
2751 assert!(inserted.state.intersects(expected_state));
2752 }
2753
2754 #[test]
2755 fn test_on_canonical_state_change_no_double_processing() {
2758 let mut tx_factory = MockTransactionFactory::default();
2759 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2760
2761 let tx = MockTransaction::eip1559().with_gas_price(50).with_gas_limit(30_000);
2763 let sender = tx.sender();
2764
2765 let mut block_info = pool.block_info();
2767 block_info.pending_basefee = 100;
2768 pool.set_block_info(block_info);
2769
2770 let validated = tx_factory.validated(tx);
2771 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2772
2773 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2775
2776 assert_eq!(pool.basefee_pool.len(), 1);
2777 assert_eq!(pool.pending_pool.len(), 0);
2778
2779 block_info.pending_basefee = 40;
2783
2784 let mut changed_senders = FxHashMap::default();
2785 changed_senders.insert(
2786 sender_id,
2787 SenderInfo {
2788 state_nonce: 0,
2789 balance: U256::from(20_000_000), },
2791 );
2792
2793 let outcome = pool.on_canonical_state_change(
2794 block_info,
2795 vec![], changed_senders,
2797 PoolUpdateKind::Commit,
2798 );
2799
2800 assert_eq!(pool.pending_pool.len(), 1, "Transaction should be in pending pool");
2802 assert_eq!(pool.basefee_pool.len(), 0, "Transaction should not be in basefee pool");
2803 assert_eq!(outcome.promoted.len(), 1, "Should report exactly one promotion");
2804 }
2805
2806 #[test]
2807 fn test_canonical_state_change_with_basefee_update_regression() {
2810 let mut tx_factory = MockTransactionFactory::default();
2811 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2812
2813 let sender_balance = U256::from(100_000_000);
2815
2816 let tx1 =
2818 MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000).with_nonce(0);
2819 let sender1 = tx1.sender();
2820
2821 let tx2 =
2823 MockTransaction::eip1559().with_gas_price(55).with_gas_limit(21_000).with_nonce(0);
2824 let sender2 = tx2.sender();
2825
2826 let tx3 =
2828 MockTransaction::eip1559().with_gas_price(45).with_gas_limit(21_000).with_nonce(0);
2829 let sender3 = tx3.sender();
2830
2831 let mut block_info = pool.block_info();
2833 block_info.pending_basefee = 70;
2834 pool.set_block_info(block_info);
2835
2836 let validated1 = tx_factory.validated(tx1);
2838 let validated2 = tx_factory.validated(tx2);
2839 let validated3 = tx_factory.validated(tx3);
2840
2841 pool.add_transaction(validated1, sender_balance, 0, None).unwrap();
2842 pool.add_transaction(validated2, sender_balance, 0, None).unwrap();
2843 pool.add_transaction(validated3, sender_balance, 0, None).unwrap();
2844
2845 let sender1_id = tx_factory.ids.sender_id(&sender1).unwrap();
2846 let sender2_id = tx_factory.ids.sender_id(&sender2).unwrap();
2847 let sender3_id = tx_factory.ids.sender_id(&sender3).unwrap();
2848
2849 assert_eq!(pool.basefee_pool.len(), 3, "All txs should be in basefee pool");
2851 assert_eq!(pool.pending_pool.len(), 0, "No txs should be in pending pool");
2852
2853 block_info.pending_basefee = 50;
2856
2857 let mut changed_senders = FxHashMap::default();
2859 changed_senders.insert(
2860 sender1_id,
2861 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2862 );
2863 changed_senders.insert(
2864 sender2_id,
2865 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2866 );
2867 changed_senders.insert(
2868 sender3_id,
2869 SenderInfo { state_nonce: 0, balance: sender_balance + U256::from(1000) },
2870 );
2871
2872 let outcome = pool.on_canonical_state_change(
2873 block_info,
2874 vec![],
2875 changed_senders,
2876 PoolUpdateKind::Commit,
2877 );
2878
2879 assert_eq!(pool.pending_pool.len(), 2, "tx1 and tx2 should be promoted");
2881 assert_eq!(pool.basefee_pool.len(), 1, "tx3 should remain in basefee");
2882
2883 assert_eq!(
2886 outcome.promoted.len(),
2887 2,
2888 "Should report exactly 2 promotions, not double-counted"
2889 );
2890
2891 let promoted_prices: Vec<u128> =
2893 outcome.promoted.iter().map(|tx| tx.max_fee_per_gas()).collect();
2894 assert!(promoted_prices.contains(&60));
2895 assert!(promoted_prices.contains(&55));
2896 }
2897
2898 #[test]
2899 fn test_basefee_decrease_with_empty_senders() {
2900 let mut tx_factory = MockTransactionFactory::default();
2903 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2904
2905 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2907
2908 let mut block_info = pool.block_info();
2910 block_info.pending_basefee = 100;
2911 pool.set_block_info(block_info);
2912
2913 let validated = tx_factory.validated(tx);
2915 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2916
2917 assert_eq!(pool.basefee_pool.len(), 1);
2918 assert_eq!(pool.pending_pool.len(), 0);
2919
2920 block_info.pending_basefee = 50;
2922 let outcome = pool.on_canonical_state_change(
2923 block_info,
2924 vec![],
2925 FxHashMap::default(), PoolUpdateKind::Commit,
2927 );
2928
2929 assert_eq!(pool.pending_pool.len(), 1, "Fee decrease should promote tx");
2931 assert_eq!(pool.basefee_pool.len(), 0);
2932 assert_eq!(outcome.promoted.len(), 1, "Should report promotion from fee update");
2933 }
2934
2935 #[test]
2936 fn test_basefee_decrease_account_makes_unfundable() {
2937 let mut tx_factory = MockTransactionFactory::default();
2940 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2941
2942 let tx = MockTransaction::eip1559().with_gas_price(60).with_gas_limit(21_000);
2943 let sender = tx.sender();
2944
2945 let mut block_info = pool.block_info();
2947 block_info.pending_basefee = 100;
2948 pool.set_block_info(block_info);
2949
2950 let validated = tx_factory.validated(tx);
2951 pool.add_transaction(validated, U256::from(10_000_000), 0, None).unwrap();
2952 let sender_id = tx_factory.ids.sender_id(&sender).unwrap();
2953
2954 assert_eq!(pool.basefee_pool.len(), 1);
2955
2956 block_info.pending_basefee = 50;
2958 let mut changed_senders = FxHashMap::default();
2959 changed_senders.insert(
2960 sender_id,
2961 SenderInfo {
2962 state_nonce: 0,
2963 balance: U256::from(100), },
2965 );
2966
2967 let outcome = pool.on_canonical_state_change(
2968 block_info,
2969 vec![],
2970 changed_senders,
2971 PoolUpdateKind::Commit,
2972 );
2973
2974 assert_eq!(pool.pending_pool.len(), 0, "Unfunded tx should not be in pending");
2976 assert_eq!(pool.basefee_pool.len(), 0, "Tx no longer in basefee pool");
2977 assert_eq!(pool.queued_pool.len(), 1, "Unfunded tx should be in queued pool");
2978
2979 let tx_count = pool.all_transactions.txs.len();
2981 assert_eq!(tx_count, 1, "Transaction should still be in pool (in queued)");
2982
2983 assert_eq!(outcome.promoted.len(), 0, "Should not report promotion");
2984 assert_eq!(outcome.discarded.len(), 0, "Queued tx is not reported as discarded");
2985 }
2986
2987 #[test]
2988 fn insert_already_imported() {
2989 let on_chain_balance = U256::ZERO;
2990 let on_chain_nonce = 0;
2991 let mut f = MockTransactionFactory::default();
2992 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
2993 let tx = MockTransaction::eip1559().inc_price().inc_limit();
2994 let tx = f.validated(tx);
2995 pool.add_transaction(tx.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
2996 match pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap_err().kind {
2997 PoolErrorKind::AlreadyImported => {}
2998 _ => unreachable!(),
2999 }
3000 }
3001
3002 #[test]
3003 fn insert_replace() {
3004 let on_chain_balance = U256::ZERO;
3005 let on_chain_nonce = 0;
3006 let mut f = MockTransactionFactory::default();
3007 let mut pool = AllTransactions::default();
3008 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3009 let first = f.validated(tx.clone());
3010 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3011 let replacement = f.validated(tx.rng_hash().inc_price());
3012 let InsertOk { updates, replaced_tx, .. } =
3013 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap();
3014 assert!(updates.is_empty());
3015 let replaced = replaced_tx.unwrap();
3016 assert_eq!(replaced.0.hash(), first.hash());
3017
3018 assert!(!pool.contains(first.hash()));
3020 assert!(pool.contains(replacement.hash()));
3021 assert_eq!(pool.len(), 1);
3022 }
3023
3024 #[test]
3025 fn insert_replace_txpool() {
3026 let on_chain_balance = U256::ZERO;
3027 let on_chain_nonce = 0;
3028 let mut f = MockTransactionFactory::default();
3029 let mut pool = TxPool::mock();
3030
3031 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3032 let first = f.validated(tx.clone());
3033 let first_added =
3034 pool.add_transaction(first, on_chain_balance, on_chain_nonce, None).unwrap();
3035 let replacement = f.validated(tx.rng_hash().inc_price());
3036 let replacement_added = pool
3037 .add_transaction(replacement.clone(), on_chain_balance, on_chain_nonce, None)
3038 .unwrap();
3039
3040 assert!(!pool.contains(first_added.hash()));
3042 assert!(pool.subpool_contains(replacement_added.subpool(), replacement_added.id()));
3044
3045 assert!(pool.contains(replacement.hash()));
3046 let size = pool.size();
3047 assert_eq!(size.total, 1);
3048 size.assert_invariants();
3049 }
3050
3051 #[test]
3052 fn insert_replace_underpriced() {
3053 let on_chain_balance = U256::ZERO;
3054 let on_chain_nonce = 0;
3055 let mut f = MockTransactionFactory::default();
3056 let mut pool = AllTransactions::default();
3057 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3058 let first = f.validated(tx.clone());
3059 let _res = pool.insert_tx(first, on_chain_balance, on_chain_nonce);
3060 let mut replacement = f.validated(tx.rng_hash());
3061 replacement.transaction = replacement.transaction.decr_price();
3062 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3063 assert!(matches!(err, InsertErr::Underpriced { .. }));
3064 }
3065
3066 #[test]
3067 fn insert_replace_underpriced_not_enough_bump() {
3068 let on_chain_balance = U256::ZERO;
3069 let on_chain_nonce = 0;
3070 let mut f = MockTransactionFactory::default();
3071 let mut pool = AllTransactions::default();
3072 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3073 tx.set_priority_fee(100);
3074 tx.set_max_fee(100);
3075 let first = f.validated(tx.clone());
3076 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3077 let mut replacement = f.validated(tx.rng_hash().inc_price());
3078
3079 replacement.transaction.set_priority_fee(109);
3081 replacement.transaction.set_max_fee(109);
3082 let err =
3083 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3084 assert!(matches!(err, InsertErr::Underpriced { .. }));
3085 assert!(pool.contains(first.hash()));
3087 assert_eq!(pool.len(), 1);
3088
3089 replacement.transaction.set_priority_fee(110);
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()));
3096 assert_eq!(pool.len(), 1);
3097
3098 replacement.transaction.set_priority_fee(109);
3100 replacement.transaction.set_max_fee(110);
3101 let err = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap_err();
3102 assert!(matches!(err, InsertErr::Underpriced { .. }));
3103 assert!(pool.contains(first.hash()));
3104 assert_eq!(pool.len(), 1);
3105 }
3106
3107 #[test]
3108 fn insert_conflicting_type_normal_to_blob() {
3109 let on_chain_balance = U256::from(10_000);
3110 let on_chain_nonce = 0;
3111 let mut f = MockTransactionFactory::default();
3112 let mut pool = AllTransactions::default();
3113 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3114 let first = f.validated(tx.clone());
3115 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3116 let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3117 let blob = f.validated(tx);
3118 let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
3119 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3120 }
3121
3122 #[test]
3123 fn insert_conflicting_type_blob_to_normal() {
3124 let on_chain_balance = U256::from(10_000);
3125 let on_chain_nonce = 0;
3126 let mut f = MockTransactionFactory::default();
3127 let mut pool = AllTransactions::default();
3128 let tx = MockTransaction::eip4844().inc_price().inc_limit();
3129 let first = f.validated(tx.clone());
3130 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3131 let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3132 let tx = f.validated(tx);
3133 let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
3134 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3135 }
3136
3137 #[test]
3139 fn insert_previous() {
3140 let on_chain_balance = U256::ZERO;
3141 let on_chain_nonce = 0;
3142 let mut f = MockTransactionFactory::default();
3143 let mut pool = AllTransactions::default();
3144 let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
3145 let first = f.validated(tx.clone());
3146 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3147
3148 let first_in_pool = pool.get(first.id()).unwrap();
3149
3150 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3152
3153 let prev = f.validated(tx.prev());
3154 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3155 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3156
3157 assert!(updates.is_empty());
3159 assert!(replaced_tx.is_none());
3160 assert!(state.contains(TxState::NO_NONCE_GAPS));
3161 assert_eq!(move_to, SubPool::Queued);
3162
3163 let first_in_pool = pool.get(first.id()).unwrap();
3164 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3166 }
3167
3168 #[test]
3170 fn insert_with_updates() {
3171 let on_chain_balance = U256::from(10_000);
3172 let on_chain_nonce = 0;
3173 let mut f = MockTransactionFactory::default();
3174 let mut pool = AllTransactions::default();
3175 let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
3176 let first = f.validated(tx.clone());
3177 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3178
3179 let first_in_pool = pool.get(first.id()).unwrap();
3180 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3182 assert_eq!(SubPool::Queued, first_in_pool.subpool);
3183
3184 let prev = f.validated(tx.prev());
3185 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3186 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3187
3188 assert_eq!(updates.len(), 1);
3190 assert!(replaced_tx.is_none());
3191 assert!(state.contains(TxState::NO_NONCE_GAPS));
3192 assert_eq!(move_to, SubPool::Pending);
3193
3194 let first_in_pool = pool.get(first.id()).unwrap();
3195 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3197 assert_eq!(SubPool::Pending, first_in_pool.subpool);
3198 }
3199
3200 #[test]
3201 fn insert_previous_blocking() {
3202 let on_chain_balance = U256::from(1_000);
3203 let on_chain_nonce = 0;
3204 let mut f = MockTransactionFactory::default();
3205 let mut pool = AllTransactions::default();
3206 pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
3207 let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
3208 let first = f.validated(tx.clone());
3209
3210 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3211
3212 let first_in_pool = pool.get(first.id()).unwrap();
3213
3214 assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
3215 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3217
3218 let prev = f.validated(tx.prev());
3219 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3220 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3221
3222 assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3223 assert!(updates.is_empty());
3225 assert!(replaced_tx.is_none());
3226 assert!(state.contains(TxState::NO_NONCE_GAPS));
3227 assert_eq!(move_to, SubPool::BaseFee);
3228
3229 let first_in_pool = pool.get(first.id()).unwrap();
3230 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3232 }
3233
3234 #[test]
3235 fn rejects_spammer() {
3236 let on_chain_balance = U256::from(1_000);
3237 let on_chain_nonce = 0;
3238 let mut f = MockTransactionFactory::default();
3239 let mut pool = AllTransactions::default();
3240
3241 let mut tx = MockTransaction::eip1559();
3242 let unblocked_tx = tx.clone();
3243 for _ in 0..pool.max_account_slots {
3244 tx = tx.next();
3245 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
3246 }
3247
3248 assert_eq!(
3249 pool.max_account_slots,
3250 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3251 );
3252
3253 let err =
3254 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
3255 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
3256
3257 assert!(pool
3258 .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
3259 .is_ok());
3260 }
3261
3262 #[test]
3263 fn allow_local_spamming() {
3264 let on_chain_balance = U256::from(1_000);
3265 let on_chain_nonce = 0;
3266 let mut f = MockTransactionFactory::default();
3267 let mut pool = AllTransactions::default();
3268
3269 let mut tx = MockTransaction::eip1559();
3270 for _ in 0..pool.max_account_slots {
3271 tx = tx.next();
3272 pool.insert_tx(
3273 f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
3274 on_chain_balance,
3275 on_chain_nonce,
3276 )
3277 .unwrap();
3278 }
3279
3280 assert_eq!(
3281 pool.max_account_slots,
3282 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3283 );
3284
3285 pool.insert_tx(
3286 f.validated_with_origin(TransactionOrigin::Local, tx.next()),
3287 on_chain_balance,
3288 on_chain_nonce,
3289 )
3290 .unwrap();
3291 }
3292
3293 #[test]
3294 fn reject_tx_over_gas_limit() {
3295 let on_chain_balance = U256::from(1_000);
3296 let on_chain_nonce = 0;
3297 let mut f = MockTransactionFactory::default();
3298 let mut pool = AllTransactions::default();
3299
3300 let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
3301
3302 assert!(matches!(
3303 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
3304 Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
3305 ));
3306 }
3307
3308 #[test]
3309 fn test_tx_equal_gas_limit() {
3310 let on_chain_balance = U256::from(1_000);
3311 let on_chain_nonce = 0;
3312 let mut f = MockTransactionFactory::default();
3313 let mut pool = AllTransactions::default();
3314
3315 let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
3316
3317 let InsertOk { state, .. } =
3318 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
3319 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
3320 }
3321
3322 #[test]
3323 fn update_basefee_subpools() {
3324 let mut f = MockTransactionFactory::default();
3325 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3326
3327 let tx = MockTransaction::eip1559().inc_price_by(10);
3328 let validated = f.validated(tx.clone());
3329 let id = *validated.id();
3330 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3331
3332 assert_eq!(pool.pending_pool.len(), 1);
3333
3334 pool.update_basefee((tx.max_fee_per_gas() + 1) as u64, |_| {});
3335
3336 assert!(pool.pending_pool.is_empty());
3337 assert_eq!(pool.basefee_pool.len(), 1);
3338
3339 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3340 }
3341
3342 #[test]
3343 fn update_basefee_subpools_setting_block_info() {
3344 let mut f = MockTransactionFactory::default();
3345 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3346
3347 let tx = MockTransaction::eip1559().inc_price_by(10);
3348 let validated = f.validated(tx.clone());
3349 let id = *validated.id();
3350 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3351
3352 assert_eq!(pool.pending_pool.len(), 1);
3353
3354 let mut block_info = pool.block_info();
3356 block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
3357 pool.set_block_info(block_info);
3358
3359 assert!(pool.pending_pool.is_empty());
3360 assert_eq!(pool.basefee_pool.len(), 1);
3361
3362 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3363 }
3364
3365 #[test]
3366 fn basefee_decrease_promotes_affordable_and_keeps_unaffordable() {
3367 use alloy_primitives::address;
3368 let mut f = MockTransactionFactory::default();
3369 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3370
3371 let sender_a = address!("0x000000000000000000000000000000000000000a");
3374 let sender_b = address!("0x000000000000000000000000000000000000000b");
3375 let sender_c = address!("0x000000000000000000000000000000000000000c");
3376
3377 let tx1 = MockTransaction::eip1559()
3378 .set_sender(sender_a)
3379 .set_nonce(0)
3380 .set_max_fee(500)
3381 .inc_limit();
3382 let tx2 = MockTransaction::eip1559()
3383 .set_sender(sender_b)
3384 .set_nonce(0)
3385 .set_max_fee(600)
3386 .inc_limit();
3387 let tx3 = MockTransaction::eip1559()
3388 .set_sender(sender_c)
3389 .set_nonce(0)
3390 .set_max_fee(400)
3391 .inc_limit();
3392
3393 let mut block_info = pool.block_info();
3395 block_info.pending_basefee = 700;
3396 pool.set_block_info(block_info);
3397
3398 let validated1 = f.validated(tx1);
3399 let validated2 = f.validated(tx2);
3400 let validated3 = f.validated(tx3);
3401 let id1 = *validated1.id();
3402 let id2 = *validated2.id();
3403 let id3 = *validated3.id();
3404
3405 pool.add_transaction(validated1, U256::from(10_000), 0, None).unwrap();
3409 pool.add_transaction(validated2, U256::from(10_000), 0, None).unwrap();
3410 pool.add_transaction(validated3, U256::from(10_000), 0, None).unwrap();
3411
3412 println!("Basefee pool len: {}", pool.basefee_pool.len());
3414 println!("Pending pool len: {}", pool.pending_pool.len());
3415 println!("tx1 subpool: {:?}", pool.all_transactions.txs.get(&id1).unwrap().subpool);
3416 println!("tx2 subpool: {:?}", pool.all_transactions.txs.get(&id2).unwrap().subpool);
3417 println!("tx3 subpool: {:?}", pool.all_transactions.txs.get(&id3).unwrap().subpool);
3418
3419 assert_eq!(pool.basefee_pool.len(), 3);
3421 assert_eq!(pool.pending_pool.len(), 0);
3422 assert_eq!(pool.all_transactions.txs.get(&id1).unwrap().subpool, SubPool::BaseFee);
3423 assert_eq!(pool.all_transactions.txs.get(&id2).unwrap().subpool, SubPool::BaseFee);
3424 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3425
3426 let mut block_info = pool.block_info();
3428 block_info.pending_basefee = 450; pool.set_block_info(block_info);
3430
3431 assert_eq!(pool.basefee_pool.len(), 1);
3436 assert_eq!(pool.pending_pool.len(), 2);
3437
3438 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3440
3441 let tx1_meta = pool.all_transactions.txs.get(&id1).unwrap();
3443 let tx2_meta = pool.all_transactions.txs.get(&id2).unwrap();
3444 assert_eq!(tx1_meta.subpool, SubPool::Pending);
3445 assert_eq!(tx2_meta.subpool, SubPool::Pending);
3446 assert!(tx1_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3447 assert!(tx2_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3448
3449 let best: Vec<_> = pool.best_transactions().take(3).collect();
3451 assert_eq!(best.len(), 2); assert!(best.iter().any(|tx| tx.id() == &id1));
3453 assert!(best.iter().any(|tx| tx.id() == &id2));
3454 }
3455
3456 #[test]
3457 fn apply_fee_updates_records_promotions_after_basefee_drop() {
3458 let mut f = MockTransactionFactory::default();
3459 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3460
3461 let tx = MockTransaction::eip1559()
3462 .with_gas_limit(21_000)
3463 .with_max_fee(500)
3464 .with_priority_fee(1);
3465 let validated = f.validated(tx);
3466 let id = *validated.id();
3467 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3468
3469 assert_eq!(pool.pending_pool.len(), 1);
3470
3471 pool.update_basefee(600, |_| {});
3473 assert!(pool.pending_pool.is_empty());
3474 assert_eq!(pool.basefee_pool.len(), 1);
3475
3476 let prev_base_fee = 600;
3477 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3478
3479 pool.all_transactions.pending_fees.base_fee = 400;
3481
3482 let mut outcome = UpdateOutcome::default();
3483 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3484
3485 assert_eq!(pool.pending_pool.len(), 1);
3486 assert!(pool.basefee_pool.is_empty());
3487 assert_eq!(outcome.promoted.len(), 1);
3488 assert_eq!(outcome.promoted[0].id(), &id);
3489 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3490 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3491
3492 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3493 assert_eq!(tx_meta.subpool, SubPool::Pending);
3494 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3495 }
3496
3497 #[test]
3498 fn apply_fee_updates_records_promotions_after_blob_fee_drop() {
3499 let mut f = MockTransactionFactory::default();
3500 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3501
3502 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3503
3504 let tx = MockTransaction::eip4844().with_blob_fee(initial_blob_fee + 100);
3505 let validated = f.validated(tx.clone());
3506 let id = *validated.id();
3507 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3508
3509 assert_eq!(pool.pending_pool.len(), 1);
3510
3511 let increased_blob_fee = tx.max_fee_per_blob_gas().unwrap() + 200;
3513 pool.update_blob_fee(increased_blob_fee, Ordering::Equal, |_| {});
3514 assert!(pool.pending_pool.is_empty());
3515 assert_eq!(pool.blob_pool.len(), 1);
3516
3517 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3518 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3519
3520 pool.all_transactions.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
3522
3523 let mut outcome = UpdateOutcome::default();
3524 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3525
3526 assert_eq!(pool.pending_pool.len(), 1);
3527 assert!(pool.blob_pool.is_empty());
3528 assert_eq!(outcome.promoted.len(), 1);
3529 assert_eq!(outcome.promoted[0].id(), &id);
3530 assert_eq!(pool.all_transactions.pending_fees.base_fee, prev_base_fee);
3531 assert_eq!(pool.all_transactions.pending_fees.blob_fee, tx.max_fee_per_blob_gas().unwrap());
3532
3533 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3534 assert_eq!(tx_meta.subpool, SubPool::Pending);
3535 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3536 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3537 }
3538
3539 #[test]
3540 fn apply_fee_updates_promotes_blob_after_basefee_drop() {
3541 let mut f = MockTransactionFactory::default();
3542 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3543
3544 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3545
3546 let tx = MockTransaction::eip4844()
3547 .with_max_fee(500)
3548 .with_priority_fee(1)
3549 .with_blob_fee(initial_blob_fee + 100);
3550 let validated = f.validated(tx);
3551 let id = *validated.id();
3552 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3553
3554 assert_eq!(pool.pending_pool.len(), 1);
3555
3556 let high_base_fee = 600;
3558 pool.update_basefee(high_base_fee, |_| {});
3559 assert!(pool.pending_pool.is_empty());
3560 assert_eq!(pool.blob_pool.len(), 1);
3561
3562 let prev_base_fee = high_base_fee;
3563 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3564
3565 pool.all_transactions.pending_fees.base_fee = 400;
3567
3568 let mut outcome = UpdateOutcome::default();
3569 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3570
3571 assert_eq!(pool.pending_pool.len(), 1);
3572 assert!(pool.blob_pool.is_empty());
3573 assert_eq!(outcome.promoted.len(), 1);
3574 assert_eq!(outcome.promoted[0].id(), &id);
3575 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3576 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3577
3578 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3579 assert_eq!(tx_meta.subpool, SubPool::Pending);
3580 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3581 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3582 }
3583
3584 #[test]
3585 fn apply_fee_updates_demotes_after_basefee_rise() {
3586 let mut f = MockTransactionFactory::default();
3587 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3588
3589 let tx = MockTransaction::eip1559()
3590 .with_gas_limit(21_000)
3591 .with_max_fee(400)
3592 .with_priority_fee(1);
3593 let validated = f.validated(tx);
3594 let id = *validated.id();
3595 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3596
3597 assert_eq!(pool.pending_pool.len(), 1);
3598
3599 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3600 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3601
3602 let new_base_fee = prev_base_fee + 1_000;
3604 pool.all_transactions.pending_fees.base_fee = new_base_fee;
3605
3606 let mut outcome = UpdateOutcome::default();
3607 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3608
3609 assert!(pool.pending_pool.is_empty());
3610 assert_eq!(pool.basefee_pool.len(), 1);
3611 assert!(outcome.promoted.is_empty());
3612 assert_eq!(pool.all_transactions.pending_fees.base_fee, new_base_fee);
3613 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3614
3615 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3616 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
3617 assert!(!tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3618 }
3619
3620 #[test]
3621 fn get_highest_transaction_by_sender_and_nonce() {
3622 let mut f = MockTransactionFactory::default();
3624 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3625
3626 let tx = MockTransaction::eip1559();
3628 pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0, None).unwrap();
3629
3630 let tx1 = tx.inc_price().next();
3632
3633 let tx1_validated = f.validated(tx1.clone());
3635 pool.add_transaction(tx1_validated, U256::from(1_000), 0, None).unwrap();
3636
3637 assert_eq!(
3639 pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
3640 Some(1)
3641 );
3642
3643 let highest_tx = pool
3645 .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
3646 .expect("Failed to retrieve highest transaction");
3647
3648 assert_eq!(highest_tx.as_ref().transaction, tx1);
3650 }
3651
3652 #[test]
3653 fn get_highest_consecutive_transaction_by_sender() {
3654 let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
3656 let mut f = MockTransactionFactory::default();
3657
3658 let sender = Address::random();
3660 let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
3661 for nonce in txs {
3662 let mut mock_tx = MockTransaction::eip1559();
3663 mock_tx.set_sender(sender);
3664 mock_tx.set_nonce(nonce);
3665
3666 let validated_tx = f.validated(mock_tx);
3667 pool.add_transaction(validated_tx, U256::from(1000), 0, None).unwrap();
3668 }
3669
3670 let sender_id = f.ids.sender_id(&sender).unwrap();
3672 let next_tx =
3673 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
3674 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
3675
3676 let next_tx =
3677 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
3678 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
3679
3680 let next_tx =
3681 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3682 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
3683
3684 let mut info = SenderInfo::default();
3686 info.update(8, U256::ZERO);
3687 pool.all_transactions.sender_info.insert(sender_id, info);
3688 let next_tx =
3689 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3690 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
3691 }
3692
3693 #[test]
3694 fn discard_nonce_too_low() {
3695 let mut f = MockTransactionFactory::default();
3696 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3697
3698 let tx = MockTransaction::eip1559().inc_price_by(10);
3699 let validated = f.validated(tx.clone());
3700 let id = *validated.id();
3701 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3702
3703 let next = tx.next();
3704 let validated = f.validated(next.clone());
3705 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3706
3707 assert_eq!(pool.pending_pool.len(), 2);
3708
3709 let mut changed_senders = HashMap::default();
3710 changed_senders.insert(
3711 id.sender,
3712 SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
3713 );
3714 let outcome = pool.update_accounts(changed_senders);
3715 assert_eq!(outcome.discarded.len(), 1);
3716 assert_eq!(pool.pending_pool.len(), 1);
3717 }
3718
3719 #[test]
3720 fn discard_with_large_blob_txs() {
3721 reth_tracing::init_test_tracing();
3723
3724 let mut f = MockTransactionFactory::default();
3726 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3727 let default_limits = pool.config.blob_limit;
3728
3729 let a_sender = address!("0x000000000000000000000000000000000000000a");
3732
3733 let mut block_info = pool.block_info();
3735 block_info.pending_blob_fee = Some(100);
3736 block_info.pending_basefee = 100;
3737
3738 pool.set_block_info(block_info);
3740
3741 let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
3743 .into_iter()
3744 .map(|mut tx| {
3745 tx.set_size(default_limits.max_size / 2 + 1);
3746 tx.set_max_fee((block_info.pending_basefee - 1).into());
3747 tx
3748 })
3749 .collect::<Vec<_>>();
3750
3751 for tx in a_txs {
3753 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3754 }
3755
3756 let removed = pool.discard_worst();
3758 assert_eq!(removed.len(), 1);
3759 }
3760
3761 #[test]
3762 fn discard_with_parked_large_txs() {
3763 reth_tracing::init_test_tracing();
3765
3766 let mut f = MockTransactionFactory::default();
3768 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3769 let default_limits = pool.config.queued_limit;
3770
3771 let a_sender = address!("0x000000000000000000000000000000000000000a");
3774
3775 let pool_base_fee = 100;
3777 pool.update_basefee(pool_base_fee, |_| {});
3778
3779 let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
3781 .into_iter()
3782 .map(|mut tx| {
3783 tx.set_size(default_limits.max_size / 2 + 1);
3784 tx.set_max_fee((pool_base_fee - 1).into());
3785 tx
3786 })
3787 .collect::<Vec<_>>();
3788
3789 for tx in a_txs {
3791 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3792 }
3793
3794 let removed = pool.discard_worst();
3796 assert_eq!(removed.len(), 1);
3797 }
3798
3799 #[test]
3800 fn discard_at_capacity() {
3801 let mut f = MockTransactionFactory::default();
3802 let queued_limit = SubPoolLimit::new(1000, usize::MAX);
3803 let mut pool =
3804 TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
3805
3806 for _ in 0..queued_limit.max_txs {
3808 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3809 let validated = f.validated(tx.clone());
3810 let _id = *validated.id();
3811 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3812 }
3813
3814 let size = pool.size();
3815 assert_eq!(size.queued, queued_limit.max_txs);
3816
3817 for _ in 0..queued_limit.max_txs {
3818 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3819 let validated = f.validated(tx.clone());
3820 let _id = *validated.id();
3821 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3822
3823 pool.discard_worst();
3824 pool.assert_invariants();
3825 assert!(pool.size().queued <= queued_limit.max_txs);
3826 }
3827 }
3828
3829 #[test]
3830 fn discard_blobs_at_capacity() {
3831 let mut f = MockTransactionFactory::default();
3832 let blob_limit = SubPoolLimit::new(1000, usize::MAX);
3833 let mut pool =
3834 TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
3835 pool.all_transactions.pending_fees.blob_fee = 10000;
3836 for _ in 0..blob_limit.max_txs {
3838 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3839 let validated = f.validated(tx.clone());
3840 let _id = *validated.id();
3841 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3842 }
3843
3844 let size = pool.size();
3845 assert_eq!(size.blob, blob_limit.max_txs);
3846
3847 for _ in 0..blob_limit.max_txs {
3848 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3849 let validated = f.validated(tx.clone());
3850 let _id = *validated.id();
3851 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3852
3853 pool.discard_worst();
3854 pool.assert_invariants();
3855 assert!(pool.size().blob <= blob_limit.max_txs);
3856 }
3857 }
3858
3859 #[test]
3860 fn account_updates_sender_balance() {
3861 let mut on_chain_balance = U256::from(100);
3862 let on_chain_nonce = 0;
3863 let mut f = MockTransactionFactory::default();
3864 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3865
3866 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3867 let tx_1 = tx_0.next();
3868 let tx_2 = tx_1.next();
3869
3870 let v0 = f.validated(tx_0);
3872 let v1 = f.validated(tx_1);
3873 let v2 = f.validated(tx_2);
3874
3875 let _res =
3876 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3877 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3878 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3879
3880 assert_eq!(1, pool.pending_transactions().len());
3882 assert_eq!(2, pool.queued_transactions().len());
3883
3884 let mut updated_accounts = HashMap::default();
3886 on_chain_balance = U256::from(300);
3887 updated_accounts.insert(
3888 v0.sender_id(),
3889 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3890 );
3891 pool.update_accounts(updated_accounts.clone());
3892
3893 assert_eq!(3, pool.pending_transactions().len());
3894 assert!(pool.queued_transactions().is_empty());
3895
3896 updated_accounts.entry(v0.sender_id()).and_modify(|v| v.balance = U256::from(1));
3898 pool.update_accounts(updated_accounts);
3899
3900 assert!(pool.pending_transactions().is_empty());
3901 assert_eq!(3, pool.queued_transactions().len());
3902 }
3903
3904 #[test]
3905 fn account_updates_nonce_gap() {
3906 let on_chain_balance = U256::from(10_000);
3907 let mut on_chain_nonce = 0;
3908 let mut f = MockTransactionFactory::default();
3909 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3910
3911 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3912 let tx_1 = tx_0.next();
3913 let tx_2 = tx_1.next();
3914
3915 let v0 = f.validated(tx_0);
3917 let v1 = f.validated(tx_1);
3918 let v2 = f.validated(tx_2);
3919
3920 let _res =
3922 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3923 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3924
3925 assert!(pool.queued_transactions().is_empty());
3926 assert_eq!(2, pool.pending_transactions().len());
3927
3928 pool.remove_transaction_by_hash(v0.hash());
3930
3931 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3933
3934 assert_eq!(2, pool.queued_transactions().len());
3936 assert!(pool.pending_transactions().is_empty());
3937
3938 let mut updated_accounts = HashMap::default();
3940 on_chain_nonce += 1;
3941 updated_accounts.insert(
3942 v0.sender_id(),
3943 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3944 );
3945 pool.update_accounts(updated_accounts);
3946
3947 assert!(pool.queued_transactions().is_empty());
3949 assert_eq!(2, pool.pending_transactions().len());
3950 }
3951 #[test]
3952 fn test_transaction_removal() {
3953 let on_chain_balance = U256::from(10_000);
3954 let on_chain_nonce = 0;
3955 let mut f = MockTransactionFactory::default();
3956 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3957
3958 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3959 let tx_1 = tx_0.next();
3960
3961 let v0 = f.validated(tx_0);
3963 let v1 = f.validated(tx_1);
3964
3965 let _res =
3967 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3968 let _res =
3969 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3970
3971 assert_eq!(0, pool.queued_transactions().len());
3972 assert_eq!(2, pool.pending_transactions().len());
3973
3974 pool.remove_transaction(v0.id());
3976 let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
3978 assert_eq!(vec![v1.nonce()], pool_txs);
3979 }
3980 #[test]
3981 fn test_remove_transactions() {
3982 let on_chain_balance = U256::from(10_000);
3983 let on_chain_nonce = 0;
3984 let mut f = MockTransactionFactory::default();
3985 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3986
3987 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3988 let tx_1 = tx_0.next();
3989 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3990 let tx_3 = tx_2.next();
3991
3992 let v0 = f.validated(tx_0);
3994 let v1 = f.validated(tx_1);
3995 let v2 = f.validated(tx_2);
3996 let v3 = f.validated(tx_3);
3997
3998 let _res =
4000 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4001 let _res =
4002 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4003 let _res =
4004 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4005 let _res =
4006 pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4007
4008 assert_eq!(0, pool.queued_transactions().len());
4009 assert_eq!(4, pool.pending_transactions().len());
4010
4011 pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
4012
4013 assert_eq!(2, pool.queued_transactions().len());
4014 assert!(pool.pending_transactions().is_empty());
4015 assert!(pool.contains(v1.hash()));
4016 assert!(pool.contains(v3.hash()));
4017 }
4018
4019 #[test]
4020 fn test_remove_transactions_middle_pending_hash() {
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 = tx_1.next();
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 = pool.add_transaction(v0, on_chain_balance, on_chain_nonce, None).unwrap();
4039 let _res =
4040 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4041 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4042 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4043
4044 assert_eq!(0, pool.queued_transactions().len());
4045 assert_eq!(4, pool.pending_transactions().len());
4046
4047 let mut removed_txs = pool.remove_transactions(vec![*v1.hash()]);
4048 assert_eq!(1, removed_txs.len());
4049
4050 assert_eq!(2, pool.queued_transactions().len());
4051 assert_eq!(1, pool.pending_transactions().len());
4052
4053 let removed_tx = removed_txs.pop().unwrap();
4055 let v1 = f.validated(removed_tx.transaction.clone());
4056 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4057 assert_eq!(0, pool.queued_transactions().len());
4058 assert_eq!(4, pool.pending_transactions().len());
4059 }
4060
4061 #[test]
4062 fn test_remove_transactions_and_descendants() {
4063 let on_chain_balance = U256::from(10_000);
4064 let on_chain_nonce = 0;
4065 let mut f = MockTransactionFactory::default();
4066 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4067
4068 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4069 let tx_1 = tx_0.next();
4070 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4071 let tx_3 = tx_2.next();
4072 let tx_4 = tx_3.next();
4073
4074 let v0 = f.validated(tx_0);
4076 let v1 = f.validated(tx_1);
4077 let v2 = f.validated(tx_2);
4078 let v3 = f.validated(tx_3);
4079 let v4 = f.validated(tx_4);
4080
4081 let _res =
4083 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4084 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4085 let _res =
4086 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4087 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4088 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4089
4090 assert_eq!(0, pool.queued_transactions().len());
4091 assert_eq!(5, pool.pending_transactions().len());
4092
4093 pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
4094
4095 assert_eq!(0, pool.queued_transactions().len());
4096 assert_eq!(0, pool.pending_transactions().len());
4097 }
4098 #[test]
4099 fn test_remove_descendants() {
4100 let on_chain_balance = U256::from(10_000);
4101 let on_chain_nonce = 0;
4102 let mut f = MockTransactionFactory::default();
4103 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4104
4105 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4106 let tx_1 = tx_0.next();
4107 let tx_2 = tx_1.next();
4108 let tx_3 = tx_2.next();
4109
4110 let v0 = f.validated(tx_0);
4112 let v1 = f.validated(tx_1);
4113 let v2 = f.validated(tx_2);
4114 let v3 = f.validated(tx_3);
4115
4116 let _res =
4118 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4119 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4120 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4121 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4122
4123 assert_eq!(0, pool.queued_transactions().len());
4124 assert_eq!(4, pool.pending_transactions().len());
4125
4126 let mut removed = Vec::new();
4127 pool.remove_transaction(v0.id());
4128 pool.remove_descendants(v0.id(), &mut removed);
4129
4130 assert_eq!(0, pool.queued_transactions().len());
4131 assert_eq!(0, pool.pending_transactions().len());
4132 assert_eq!(3, removed.len());
4133 }
4134 #[test]
4135 fn test_remove_transactions_by_sender() {
4136 let on_chain_balance = U256::from(10_000);
4137 let on_chain_nonce = 0;
4138 let mut f = MockTransactionFactory::default();
4139 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4140
4141 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4142 let tx_1 = tx_0.next();
4143 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4144 let tx_3 = tx_2.next();
4145 let tx_4 = tx_3.next();
4146
4147 let v0 = f.validated(tx_0);
4149 let v1 = f.validated(tx_1);
4150 let v2 = f.validated(tx_2);
4151 let v3 = f.validated(tx_3);
4152 let v4 = f.validated(tx_4);
4153
4154 let _res =
4156 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4157 let _res =
4158 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4159 let _res =
4160 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4161 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4162 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4163
4164 assert_eq!(0, pool.queued_transactions().len());
4165 assert_eq!(5, pool.pending_transactions().len());
4166
4167 pool.remove_transactions_by_sender(v2.sender_id());
4168
4169 assert_eq!(0, pool.queued_transactions().len());
4170 assert_eq!(2, pool.pending_transactions().len());
4171 assert!(pool.contains(v0.hash()));
4172 assert!(pool.contains(v1.hash()));
4173 }
4174 #[test]
4175 fn wrong_best_order_of_transactions() {
4176 let on_chain_balance = U256::from(10_000);
4177 let mut on_chain_nonce = 0;
4178 let mut f = MockTransactionFactory::default();
4179 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4180
4181 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4182 let tx_1 = tx_0.next();
4183 let tx_2 = tx_1.next();
4184 let tx_3 = tx_2.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
4192 let _res =
4194 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4195 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4196
4197 assert_eq!(0, pool.queued_transactions().len());
4198 assert_eq!(2, pool.pending_transactions().len());
4199
4200 pool.remove_transaction(v0.id());
4202
4203 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4205
4206 assert_eq!(1, pool.queued_transactions().len());
4208 assert_eq!(1, pool.pending_transactions().len());
4209
4210 let mut updated_accounts = HashMap::default();
4212 on_chain_nonce += 1;
4213 updated_accounts.insert(
4214 v0.sender_id(),
4215 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4216 );
4217 pool.update_accounts(updated_accounts);
4218
4219 assert_eq!(0, pool.queued_transactions().len());
4222 assert_eq!(2, pool.pending_transactions().len());
4223
4224 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4226 assert_eq!(0, pool.queued_transactions().len());
4227 assert_eq!(3, pool.pending_transactions().len());
4228
4229 assert_eq!(
4232 pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
4233 vec![1, 2, 3]
4234 );
4235 }
4236
4237 #[test]
4238 fn test_best_with_attributes() {
4239 let on_chain_balance = U256::MAX;
4240 let on_chain_nonce = 0;
4241 let mut f = MockTransactionFactory::default();
4242 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4243
4244 let base_fee: u128 = 100;
4245 let blob_fee: u128 = 100;
4246
4247 let mut block_info = pool.block_info();
4249 block_info.pending_basefee = base_fee as u64;
4250 block_info.pending_blob_fee = Some(blob_fee);
4251 pool.set_block_info(block_info);
4252
4253 let tx1 = MockTransaction::eip4844()
4255 .with_sender(Address::with_last_byte(1))
4256 .with_max_fee(base_fee + 10)
4257 .with_blob_fee(blob_fee + 10);
4258 let tx2 = MockTransaction::eip4844()
4259 .with_sender(Address::with_last_byte(2))
4260 .with_max_fee(base_fee + 10)
4261 .with_blob_fee(blob_fee);
4262 let tx3 = MockTransaction::eip4844()
4263 .with_sender(Address::with_last_byte(3))
4264 .with_max_fee(base_fee)
4265 .with_blob_fee(blob_fee + 10);
4266 let tx4 = MockTransaction::eip4844()
4267 .with_sender(Address::with_last_byte(4))
4268 .with_max_fee(base_fee)
4269 .with_blob_fee(blob_fee);
4270 let tx5 = MockTransaction::eip4844()
4271 .with_sender(Address::with_last_byte(5))
4272 .with_max_fee(base_fee)
4273 .with_blob_fee(blob_fee - 10);
4274 let tx6 = MockTransaction::eip4844()
4275 .with_sender(Address::with_last_byte(6))
4276 .with_max_fee(base_fee - 10)
4277 .with_blob_fee(blob_fee);
4278 let tx7 = MockTransaction::eip4844()
4279 .with_sender(Address::with_last_byte(7))
4280 .with_max_fee(base_fee - 10)
4281 .with_blob_fee(blob_fee - 10);
4282
4283 for tx in vec![
4284 tx1.clone(),
4285 tx2.clone(),
4286 tx3.clone(),
4287 tx4.clone(),
4288 tx5.clone(),
4289 tx6.clone(),
4290 tx7.clone(),
4291 ] {
4292 pool.add_transaction(f.validated(tx.clone()), on_chain_balance, on_chain_nonce, None)
4293 .unwrap();
4294 }
4295
4296 let base_fee = base_fee as u64;
4297 let blob_fee = blob_fee as u64;
4298
4299 let cases = vec![
4300 (BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee + 5)), vec![tx1.clone()]),
4302 (
4304 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee)),
4305 vec![tx1.clone(), tx2.clone()],
4306 ),
4307 (
4309 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee - 5)),
4310 vec![tx1.clone(), tx2.clone()],
4311 ),
4312 (
4314 BestTransactionsAttributes::new(base_fee, Some(blob_fee + 5)),
4315 vec![tx1.clone(), tx3.clone()],
4316 ),
4317 (
4319 BestTransactionsAttributes::new(base_fee, Some(blob_fee)),
4320 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
4321 ),
4322 (
4324 BestTransactionsAttributes::new(base_fee, Some(blob_fee - 10)),
4325 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx5.clone()],
4326 ),
4327 (
4329 BestTransactionsAttributes::new(base_fee - 5, Some(blob_fee + 5)),
4330 vec![tx1.clone(), tx3.clone()],
4331 ),
4332 (
4334 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee)),
4335 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx6.clone()],
4336 ),
4337 (
4339 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee - 10)),
4340 vec![tx1, tx2, tx5, tx3, tx4, tx6, tx7],
4341 ),
4342 ];
4343
4344 for (idx, (attribute, expected)) in cases.into_iter().enumerate() {
4345 let mut best = pool.best_transactions_with_attributes(attribute);
4346
4347 for (tx_idx, expected_tx) in expected.into_iter().enumerate() {
4348 let tx = best.next().expect("Transaction should be returned");
4349 assert_eq!(
4350 tx.transaction,
4351 expected_tx,
4352 "Failed tx {} in case {}",
4353 tx_idx + 1,
4354 idx + 1
4355 );
4356 }
4357
4358 assert!(best.next().is_none());
4360 }
4361 }
4362
4363 #[test]
4364 fn test_pending_ordering() {
4365 let mut f = MockTransactionFactory::default();
4366 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4367
4368 let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
4369 let tx_1 = tx_0.next();
4370
4371 let v0 = f.validated(tx_0);
4372 let v1 = f.validated(tx_1);
4373
4374 pool.add_transaction(v0.clone(), U256::MAX, 0, None).unwrap();
4376 assert_eq!(1, pool.queued_transactions().len());
4377
4378 pool.add_transaction(v1, U256::MAX, 1, None).unwrap();
4380
4381 assert_eq!(2, pool.pending_transactions().len());
4382 assert_eq!(0, pool.queued_transactions().len());
4383
4384 assert_eq!(
4385 pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
4386 v0.nonce()
4387 );
4388 }
4389
4390 #[test]
4392 fn one_sender_one_independent_transaction() {
4393 let mut on_chain_balance = U256::from(4_999); let mut on_chain_nonce = 40;
4395 let mut f = MockTransactionFactory::default();
4396 let mut pool = TxPool::mock();
4397 let mut submitted_txs = Vec::new();
4398
4399 let template =
4401 MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
4402
4403 for tx_nonce in 40..48 {
4406 let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
4407 submitted_txs.push(*tx.id());
4408 pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap();
4409 }
4410
4411 on_chain_balance = U256::from(999_999);
4414 on_chain_nonce = 42;
4415 pool.remove_transaction(&submitted_txs[0]);
4416 pool.remove_transaction(&submitted_txs[1]);
4417
4418 for tx_nonce in 48..52 {
4420 pool.add_transaction(
4421 f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
4422 on_chain_balance,
4423 on_chain_nonce,
4424 None,
4425 )
4426 .unwrap();
4427 }
4428
4429 let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
4430 assert_eq!(best_txs.len(), 10); assert_eq!(pool.pending_pool.independent().len(), 1);
4433 }
4434
4435 #[test]
4436 fn test_insertion_disorder() {
4437 let mut f = MockTransactionFactory::default();
4438 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4439
4440 let sender = address!("0x1234567890123456789012345678901234567890");
4441 let tx0 = f.validated_arc(
4442 MockTransaction::legacy().with_sender(sender).with_nonce(0).with_gas_price(10),
4443 );
4444 let tx1 = f.validated_arc(
4445 MockTransaction::eip1559()
4446 .with_sender(sender)
4447 .with_nonce(1)
4448 .with_gas_limit(1000)
4449 .with_gas_price(10),
4450 );
4451 let tx2 = f.validated_arc(
4452 MockTransaction::legacy().with_sender(sender).with_nonce(2).with_gas_price(10),
4453 );
4454 let tx3 = f.validated_arc(
4455 MockTransaction::legacy().with_sender(sender).with_nonce(3).with_gas_price(10),
4456 );
4457
4458 pool.add_transaction((*tx0).clone(), U256::from(1000), 0, None).unwrap();
4460 let mut best = pool.best_transactions();
4461 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4462 assert_eq!(t0.id(), tx0.id());
4463 pool.add_transaction((*tx1).clone(), U256::from(1000), 0, None).unwrap();
4465 let mut best = pool.best_transactions();
4466 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4467 assert_eq!(t0.id(), tx0.id());
4468 assert!(best.next().is_none());
4469
4470 pool.add_transaction((*tx2).clone(), U256::MAX, 0, None).unwrap();
4472
4473 let mut best = pool.best_transactions();
4474
4475 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4476 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4477 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4478 assert_eq!(t0.id(), tx0.id());
4479 assert_eq!(t1.id(), tx1.id());
4480 assert_eq!(t2.id(), tx2.id());
4481
4482 pool.add_transaction((*tx3).clone(), U256::MAX, 0, None).unwrap();
4484 let mut best = pool.best_transactions();
4485 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4486 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4487 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4488 let t3 = best.next().expect("tx3 should be put in the pending subpool");
4489 assert_eq!(t0.id(), tx0.id());
4490 assert_eq!(t1.id(), tx1.id());
4491 assert_eq!(t2.id(), tx2.id());
4492 assert_eq!(t3.id(), tx3.id());
4493 }
4494
4495 #[test]
4496 fn test_non_4844_blob_fee_bit_invariant() {
4497 let mut f = MockTransactionFactory::default();
4498 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4499
4500 let non_4844_tx = MockTransaction::eip1559().set_max_fee(200).inc_limit();
4501 let validated = f.validated(non_4844_tx.clone());
4502
4503 assert!(!non_4844_tx.is_eip4844());
4504 pool.add_transaction(validated.clone(), U256::from(10_000), 0, None).unwrap();
4505
4506 let tx_meta = pool.all_transactions.txs.get(validated.id()).unwrap();
4508 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4509 assert_eq!(tx_meta.subpool, SubPool::Pending);
4510 }
4511
4512 #[test]
4513 fn test_blob_fee_enforcement_only_applies_to_eip4844() {
4514 let mut f = MockTransactionFactory::default();
4515 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4516
4517 let mut block_info = pool.block_info();
4519 block_info.pending_blob_fee = Some(160);
4520 block_info.pending_basefee = 100;
4521 pool.set_block_info(block_info);
4522
4523 let eip4844_tx = MockTransaction::eip4844()
4524 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4525 .with_max_fee(200)
4526 .with_blob_fee(150) .inc_limit();
4528
4529 let non_4844_tx = MockTransaction::eip1559()
4530 .with_sender(address!("0x000000000000000000000000000000000000000b"))
4531 .set_max_fee(200)
4532 .inc_limit();
4533
4534 let validated_4844 = f.validated(eip4844_tx);
4535 let validated_non_4844 = f.validated(non_4844_tx);
4536
4537 pool.add_transaction(validated_4844.clone(), U256::from(10_000), 0, None).unwrap();
4538 pool.add_transaction(validated_non_4844.clone(), U256::from(10_000), 0, None).unwrap();
4539
4540 let tx_4844_meta = pool.all_transactions.txs.get(validated_4844.id()).unwrap();
4541 let tx_non_4844_meta = pool.all_transactions.txs.get(validated_non_4844.id()).unwrap();
4542
4543 assert!(!tx_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4545 assert_eq!(tx_4844_meta.subpool, SubPool::Blob);
4546
4547 assert!(tx_non_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4549 assert_eq!(tx_non_4844_meta.subpool, SubPool::Pending);
4550 }
4551
4552 #[test]
4553 fn test_basefee_decrease_preserves_non_4844_blob_fee_bit() {
4554 let mut f = MockTransactionFactory::default();
4555 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4556
4557 let non_4844_tx = MockTransaction::eip1559()
4559 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4560 .set_max_fee(500) .inc_limit();
4562
4563 pool.update_basefee(600, |_| {});
4565
4566 let validated = f.validated(non_4844_tx);
4567 let tx_id = *validated.id();
4568 pool.add_transaction(validated, U256::from(10_000), 0, None).unwrap();
4569
4570 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4572 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
4573 assert!(
4574 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4575 "Non-4844 tx in BaseFee pool must retain ENOUGH_BLOB_FEE_CAP_BLOCK bit"
4576 );
4577
4578 pool.update_basefee(400, |_| {});
4581
4582 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4584 assert_eq!(
4585 tx_meta.subpool,
4586 SubPool::Pending,
4587 "Non-4844 tx should be promoted from BaseFee to Pending after basefee decrease"
4588 );
4589 assert!(
4590 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4591 "Non-4844 tx must NEVER lose ENOUGH_BLOB_FEE_CAP_BLOCK bit during basefee promotion"
4592 );
4593 assert!(
4594 tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK),
4595 "Non-4844 tx should gain ENOUGH_FEE_CAP_BLOCK bit after basefee decrease"
4596 );
4597 }
4598
4599 #[test]
4605 fn best_transactions_nonce_order_on_balance_unlock() {
4606 let mut f = MockTransactionFactory::default();
4607 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4608
4609 let sender = Address::random();
4610 let on_chain_balance = U256::from(10_000);
4611
4612 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
4614 let tx1 = tx0.next().inc_limit().with_value(U256::from(on_chain_balance));
4616 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
4618
4619 let v0 = f.validated(tx0);
4620 let v1 = f.validated(tx1);
4621 let v2 = f.validated(tx2);
4622
4623 pool.add_transaction(v0, on_chain_balance, 0, None).unwrap();
4625
4626 let mut best = pool.best_transactions();
4628
4629 let first = best.next().expect("should yield tx0");
4631 assert_eq!(first.id().nonce, 0);
4632
4633 pool.add_transaction(v1, on_chain_balance, 0, None).unwrap();
4636
4637 assert!(best.next().is_none(), "tx1 should be queued, not pending");
4639
4640 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
4644
4645 let t1 = best.next().expect("should yield a transaction");
4646 let t2 = best.next().expect("should yield a transaction");
4647
4648 assert_eq!(
4650 t1.id().nonce,
4651 1,
4652 "first yielded tx should be nonce 1, got nonce {}",
4653 t1.id().nonce
4654 );
4655 assert_eq!(
4656 t2.id().nonce,
4657 2,
4658 "second yielded tx should be nonce 2, got nonce {}",
4659 t2.id().nonce
4660 );
4661 }
4662
4663 #[test]
4667 fn best_transactions_nonce_order_on_gap_fill() {
4668 let mut f = MockTransactionFactory::default();
4669 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4670
4671 let sender = Address::random();
4672 let balance = U256::MAX;
4673
4674 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
4676 let tx1 = tx0.next().inc_limit();
4678
4679 let v0 = f.validated(tx0);
4680 let v1 = f.validated(tx1);
4681
4682 pool.add_transaction(v1, balance, 0, None).unwrap();
4684
4685 let mut best = pool.best_transactions();
4687 assert!(best.next().is_none(), "pool should have no pending txs yet");
4688
4689 pool.add_transaction(v0, balance, 0, None).unwrap();
4691
4692 let t0 = best.next().expect("should yield a transaction");
4693 let t1 = best.next().expect("should yield a transaction");
4694
4695 assert_eq!(t0.id().nonce, 0, "first yielded tx should be nonce 0, got {}", t0.id().nonce);
4696 assert_eq!(t1.id().nonce, 1, "second yielded tx should be nonce 1, got {}", t1.id().nonce);
4697 }
4698
4699 #[test]
4703 fn best_transactions_nonce_order_mixed_promotions() {
4704 let mut f = MockTransactionFactory::default();
4705 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4706
4707 let sender = Address::random();
4708 let low_balance = U256::from(10_000);
4709
4710 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
4712 let tx1 = tx0.next().inc_limit().with_value(U256::from(low_balance));
4714 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
4716 let tx3 = tx2.next().inc_limit().with_value(U256::ZERO);
4718
4719 let v0 = f.validated(tx0);
4720 let v1 = f.validated(tx1);
4721 let v2 = f.validated(tx2);
4722 let v3 = f.validated(tx3);
4723
4724 pool.add_transaction(v0, low_balance, 0, None).unwrap();
4726
4727 pool.add_transaction(v1, low_balance, 0, None).unwrap();
4729
4730 pool.add_transaction(v3, low_balance, 0, None).unwrap();
4732
4733 let mut best = pool.best_transactions();
4734
4735 let first = best.next().expect("should yield tx0");
4737 assert_eq!(first.id().nonce, 0);
4738 assert!(best.next().is_none(), "only tx0 should be pending");
4739
4740 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
4745
4746 let t1 = best.next().expect("should yield nonce 1");
4747 let t2 = best.next().expect("should yield nonce 2");
4748 let t3 = best.next().expect("should yield nonce 3");
4749
4750 assert_eq!(t1.id().nonce, 1, "expected nonce 1, got {}", t1.id().nonce);
4751 assert_eq!(t2.id().nonce, 2, "expected nonce 2, got {}", t2.id().nonce);
4752 assert_eq!(t3.id().nonce, 3, "expected nonce 3, got {}", t3.id().nonce);
4753 }
4754}