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_replace_underpriced_rounds_up_minimum_bump() {
3109 let on_chain_balance = U256::ZERO;
3110 let on_chain_nonce = 0;
3111 let mut f = MockTransactionFactory::default();
3112 let mut pool = AllTransactions { minimal_protocol_basefee: 0, ..Default::default() };
3113 let mut tx = MockTransaction::eip1559().inc_price().inc_limit();
3114 tx.set_priority_fee(1);
3115 tx.set_max_fee(1);
3116
3117 let first = f.validated(tx.clone());
3118 let _ = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3119
3120 let mut replacement = f.validated(tx.rng_hash().inc_price());
3121 replacement.transaction.set_priority_fee(1);
3122 replacement.transaction.set_max_fee(2);
3123 let err =
3124 pool.insert_tx(replacement.clone(), on_chain_balance, on_chain_nonce).unwrap_err();
3125 assert!(matches!(err, InsertErr::Underpriced { .. }));
3126 assert!(pool.contains(first.hash()));
3127 assert_eq!(pool.len(), 1);
3128
3129 replacement.transaction.set_priority_fee(2);
3130 replacement.transaction.set_max_fee(2);
3131 let replaced = pool.insert_tx(replacement, on_chain_balance, on_chain_nonce).unwrap();
3132 assert!(replaced.replaced_tx.is_some());
3133 assert_eq!(pool.len(), 1);
3134 }
3135
3136 #[test]
3137 fn insert_conflicting_type_normal_to_blob() {
3138 let on_chain_balance = U256::from(10_000);
3139 let on_chain_nonce = 0;
3140 let mut f = MockTransactionFactory::default();
3141 let mut pool = AllTransactions::default();
3142 let tx = MockTransaction::eip1559().inc_price().inc_limit();
3143 let first = f.validated(tx.clone());
3144 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3145 let tx = MockTransaction::eip4844().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3146 let blob = f.validated(tx);
3147 let err = pool.insert_tx(blob, on_chain_balance, on_chain_nonce).unwrap_err();
3148 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3149 }
3150
3151 #[test]
3152 fn insert_conflicting_type_blob_to_normal() {
3153 let on_chain_balance = U256::from(10_000);
3154 let on_chain_nonce = 0;
3155 let mut f = MockTransactionFactory::default();
3156 let mut pool = AllTransactions::default();
3157 let tx = MockTransaction::eip4844().inc_price().inc_limit();
3158 let first = f.validated(tx.clone());
3159 pool.insert_tx(first, on_chain_balance, on_chain_nonce).unwrap();
3160 let tx = MockTransaction::eip1559().set_sender(tx.sender()).inc_price_by(100).inc_limit();
3161 let tx = f.validated(tx);
3162 let err = pool.insert_tx(tx, on_chain_balance, on_chain_nonce).unwrap_err();
3163 assert!(matches!(err, InsertErr::TxTypeConflict { .. }), "{err:?}");
3164 }
3165
3166 #[test]
3168 fn insert_previous() {
3169 let on_chain_balance = U256::ZERO;
3170 let on_chain_nonce = 0;
3171 let mut f = MockTransactionFactory::default();
3172 let mut pool = AllTransactions::default();
3173 let tx = MockTransaction::eip1559().inc_nonce().inc_price().inc_limit();
3174 let first = f.validated(tx.clone());
3175 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3176
3177 let first_in_pool = pool.get(first.id()).unwrap();
3178
3179 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3181
3182 let prev = f.validated(tx.prev());
3183 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3184 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3185
3186 assert!(updates.is_empty());
3188 assert!(replaced_tx.is_none());
3189 assert!(state.contains(TxState::NO_NONCE_GAPS));
3190 assert_eq!(move_to, SubPool::Queued);
3191
3192 let first_in_pool = pool.get(first.id()).unwrap();
3193 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3195 }
3196
3197 #[test]
3199 fn insert_with_updates() {
3200 let on_chain_balance = U256::from(10_000);
3201 let on_chain_nonce = 0;
3202 let mut f = MockTransactionFactory::default();
3203 let mut pool = AllTransactions::default();
3204 let tx = MockTransaction::eip1559().inc_nonce().set_gas_price(100).inc_limit();
3205 let first = f.validated(tx.clone());
3206 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce).unwrap();
3207
3208 let first_in_pool = pool.get(first.id()).unwrap();
3209 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3211 assert_eq!(SubPool::Queued, first_in_pool.subpool);
3212
3213 let prev = f.validated(tx.prev());
3214 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3215 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3216
3217 assert_eq!(updates.len(), 1);
3219 assert!(replaced_tx.is_none());
3220 assert!(state.contains(TxState::NO_NONCE_GAPS));
3221 assert_eq!(move_to, SubPool::Pending);
3222
3223 let first_in_pool = pool.get(first.id()).unwrap();
3224 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3226 assert_eq!(SubPool::Pending, first_in_pool.subpool);
3227 }
3228
3229 #[test]
3230 fn insert_previous_blocking() {
3231 let on_chain_balance = U256::from(1_000);
3232 let on_chain_nonce = 0;
3233 let mut f = MockTransactionFactory::default();
3234 let mut pool = AllTransactions::default();
3235 pool.pending_fees.base_fee = pool.minimal_protocol_basefee.checked_add(1).unwrap();
3236 let tx = MockTransaction::eip1559().inc_nonce().inc_limit();
3237 let first = f.validated(tx.clone());
3238
3239 let _res = pool.insert_tx(first.clone(), on_chain_balance, on_chain_nonce);
3240
3241 let first_in_pool = pool.get(first.id()).unwrap();
3242
3243 assert!(tx.get_gas_price() < pool.pending_fees.base_fee as u128);
3244 assert!(!first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3246
3247 let prev = f.validated(tx.prev());
3248 let InsertOk { updates, replaced_tx, state, move_to, .. } =
3249 pool.insert_tx(prev, on_chain_balance, on_chain_nonce).unwrap();
3250
3251 assert!(!state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3252 assert!(updates.is_empty());
3254 assert!(replaced_tx.is_none());
3255 assert!(state.contains(TxState::NO_NONCE_GAPS));
3256 assert_eq!(move_to, SubPool::BaseFee);
3257
3258 let first_in_pool = pool.get(first.id()).unwrap();
3259 assert!(first_in_pool.state.contains(TxState::NO_NONCE_GAPS));
3261 }
3262
3263 #[test]
3264 fn rejects_spammer() {
3265 let on_chain_balance = U256::from(1_000);
3266 let on_chain_nonce = 0;
3267 let mut f = MockTransactionFactory::default();
3268 let mut pool = AllTransactions::default();
3269
3270 let mut tx = MockTransaction::eip1559();
3271 let unblocked_tx = tx.clone();
3272 for _ in 0..pool.max_account_slots {
3273 tx = tx.next();
3274 pool.insert_tx(f.validated(tx.clone()), on_chain_balance, on_chain_nonce).unwrap();
3275 }
3276
3277 assert_eq!(
3278 pool.max_account_slots,
3279 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3280 );
3281
3282 let err =
3283 pool.insert_tx(f.validated(tx.next()), on_chain_balance, on_chain_nonce).unwrap_err();
3284 assert!(matches!(err, InsertErr::ExceededSenderTransactionsCapacity { .. }));
3285
3286 assert!(pool
3287 .insert_tx(f.validated(unblocked_tx), on_chain_balance, on_chain_nonce)
3288 .is_ok());
3289 }
3290
3291 #[test]
3292 fn allow_local_spamming() {
3293 let on_chain_balance = U256::from(1_000);
3294 let on_chain_nonce = 0;
3295 let mut f = MockTransactionFactory::default();
3296 let mut pool = AllTransactions::default();
3297
3298 let mut tx = MockTransaction::eip1559();
3299 for _ in 0..pool.max_account_slots {
3300 tx = tx.next();
3301 pool.insert_tx(
3302 f.validated_with_origin(TransactionOrigin::Local, tx.clone()),
3303 on_chain_balance,
3304 on_chain_nonce,
3305 )
3306 .unwrap();
3307 }
3308
3309 assert_eq!(
3310 pool.max_account_slots,
3311 pool.tx_count(f.ids.sender_id(tx.get_sender()).unwrap())
3312 );
3313
3314 pool.insert_tx(
3315 f.validated_with_origin(TransactionOrigin::Local, tx.next()),
3316 on_chain_balance,
3317 on_chain_nonce,
3318 )
3319 .unwrap();
3320 }
3321
3322 #[test]
3323 fn reject_tx_over_gas_limit() {
3324 let on_chain_balance = U256::from(1_000);
3325 let on_chain_nonce = 0;
3326 let mut f = MockTransactionFactory::default();
3327 let mut pool = AllTransactions::default();
3328
3329 let tx = MockTransaction::eip1559().with_gas_limit(30_000_001);
3330
3331 assert!(matches!(
3332 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce),
3333 Err(InsertErr::TxGasLimitMoreThanAvailableBlockGas { .. })
3334 ));
3335 }
3336
3337 #[test]
3338 fn test_tx_equal_gas_limit() {
3339 let on_chain_balance = U256::from(1_000);
3340 let on_chain_nonce = 0;
3341 let mut f = MockTransactionFactory::default();
3342 let mut pool = AllTransactions::default();
3343
3344 let tx = MockTransaction::eip1559().with_gas_limit(30_000_000);
3345
3346 let InsertOk { state, .. } =
3347 pool.insert_tx(f.validated(tx), on_chain_balance, on_chain_nonce).unwrap();
3348 assert!(state.contains(TxState::NOT_TOO_MUCH_GAS));
3349 }
3350
3351 #[test]
3352 fn update_basefee_subpools() {
3353 let mut f = MockTransactionFactory::default();
3354 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3355
3356 let tx = MockTransaction::eip1559().inc_price_by(10);
3357 let validated = f.validated(tx.clone());
3358 let id = *validated.id();
3359 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3360
3361 assert_eq!(pool.pending_pool.len(), 1);
3362
3363 pool.update_basefee((tx.max_fee_per_gas() + 1) as u64, |_| {});
3364
3365 assert!(pool.pending_pool.is_empty());
3366 assert_eq!(pool.basefee_pool.len(), 1);
3367
3368 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3369 }
3370
3371 #[test]
3372 fn update_basefee_subpools_setting_block_info() {
3373 let mut f = MockTransactionFactory::default();
3374 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3375
3376 let tx = MockTransaction::eip1559().inc_price_by(10);
3377 let validated = f.validated(tx.clone());
3378 let id = *validated.id();
3379 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3380
3381 assert_eq!(pool.pending_pool.len(), 1);
3382
3383 let mut block_info = pool.block_info();
3385 block_info.pending_basefee = (tx.max_fee_per_gas() + 1) as u64;
3386 pool.set_block_info(block_info);
3387
3388 assert!(pool.pending_pool.is_empty());
3389 assert_eq!(pool.basefee_pool.len(), 1);
3390
3391 assert_eq!(pool.all_transactions.txs.get(&id).unwrap().subpool, SubPool::BaseFee)
3392 }
3393
3394 #[test]
3395 fn basefee_decrease_promotes_affordable_and_keeps_unaffordable() {
3396 use alloy_primitives::address;
3397 let mut f = MockTransactionFactory::default();
3398 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3399
3400 let sender_a = address!("0x000000000000000000000000000000000000000a");
3403 let sender_b = address!("0x000000000000000000000000000000000000000b");
3404 let sender_c = address!("0x000000000000000000000000000000000000000c");
3405
3406 let tx1 = MockTransaction::eip1559()
3407 .set_sender(sender_a)
3408 .set_nonce(0)
3409 .set_max_fee(500)
3410 .inc_limit();
3411 let tx2 = MockTransaction::eip1559()
3412 .set_sender(sender_b)
3413 .set_nonce(0)
3414 .set_max_fee(600)
3415 .inc_limit();
3416 let tx3 = MockTransaction::eip1559()
3417 .set_sender(sender_c)
3418 .set_nonce(0)
3419 .set_max_fee(400)
3420 .inc_limit();
3421
3422 let mut block_info = pool.block_info();
3424 block_info.pending_basefee = 700;
3425 pool.set_block_info(block_info);
3426
3427 let validated1 = f.validated(tx1);
3428 let validated2 = f.validated(tx2);
3429 let validated3 = f.validated(tx3);
3430 let id1 = *validated1.id();
3431 let id2 = *validated2.id();
3432 let id3 = *validated3.id();
3433
3434 pool.add_transaction(validated1, U256::from(10_000), 0, None).unwrap();
3438 pool.add_transaction(validated2, U256::from(10_000), 0, None).unwrap();
3439 pool.add_transaction(validated3, U256::from(10_000), 0, None).unwrap();
3440
3441 println!("Basefee pool len: {}", pool.basefee_pool.len());
3443 println!("Pending pool len: {}", pool.pending_pool.len());
3444 println!("tx1 subpool: {:?}", pool.all_transactions.txs.get(&id1).unwrap().subpool);
3445 println!("tx2 subpool: {:?}", pool.all_transactions.txs.get(&id2).unwrap().subpool);
3446 println!("tx3 subpool: {:?}", pool.all_transactions.txs.get(&id3).unwrap().subpool);
3447
3448 assert_eq!(pool.basefee_pool.len(), 3);
3450 assert_eq!(pool.pending_pool.len(), 0);
3451 assert_eq!(pool.all_transactions.txs.get(&id1).unwrap().subpool, SubPool::BaseFee);
3452 assert_eq!(pool.all_transactions.txs.get(&id2).unwrap().subpool, SubPool::BaseFee);
3453 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3454
3455 let mut block_info = pool.block_info();
3457 block_info.pending_basefee = 450; pool.set_block_info(block_info);
3459
3460 assert_eq!(pool.basefee_pool.len(), 1);
3465 assert_eq!(pool.pending_pool.len(), 2);
3466
3467 assert_eq!(pool.all_transactions.txs.get(&id3).unwrap().subpool, SubPool::BaseFee);
3469
3470 let tx1_meta = pool.all_transactions.txs.get(&id1).unwrap();
3472 let tx2_meta = pool.all_transactions.txs.get(&id2).unwrap();
3473 assert_eq!(tx1_meta.subpool, SubPool::Pending);
3474 assert_eq!(tx2_meta.subpool, SubPool::Pending);
3475 assert!(tx1_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3476 assert!(tx2_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3477
3478 let best: Vec<_> = pool.best_transactions().take(3).collect();
3480 assert_eq!(best.len(), 2); assert!(best.iter().any(|tx| tx.id() == &id1));
3482 assert!(best.iter().any(|tx| tx.id() == &id2));
3483 }
3484
3485 #[test]
3486 fn apply_fee_updates_records_promotions_after_basefee_drop() {
3487 let mut f = MockTransactionFactory::default();
3488 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3489
3490 let tx = MockTransaction::eip1559()
3491 .with_gas_limit(21_000)
3492 .with_max_fee(500)
3493 .with_priority_fee(1);
3494 let validated = f.validated(tx);
3495 let id = *validated.id();
3496 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3497
3498 assert_eq!(pool.pending_pool.len(), 1);
3499
3500 pool.update_basefee(600, |_| {});
3502 assert!(pool.pending_pool.is_empty());
3503 assert_eq!(pool.basefee_pool.len(), 1);
3504
3505 let prev_base_fee = 600;
3506 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3507
3508 pool.all_transactions.pending_fees.base_fee = 400;
3510
3511 let mut outcome = UpdateOutcome::default();
3512 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3513
3514 assert_eq!(pool.pending_pool.len(), 1);
3515 assert!(pool.basefee_pool.is_empty());
3516 assert_eq!(outcome.promoted.len(), 1);
3517 assert_eq!(outcome.promoted[0].id(), &id);
3518 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3519 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3520
3521 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3522 assert_eq!(tx_meta.subpool, SubPool::Pending);
3523 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3524 }
3525
3526 #[test]
3527 fn apply_fee_updates_records_promotions_after_blob_fee_drop() {
3528 let mut f = MockTransactionFactory::default();
3529 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3530
3531 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3532
3533 let tx = MockTransaction::eip4844().with_blob_fee(initial_blob_fee + 100);
3534 let validated = f.validated(tx.clone());
3535 let id = *validated.id();
3536 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3537
3538 assert_eq!(pool.pending_pool.len(), 1);
3539
3540 let increased_blob_fee = tx.max_fee_per_blob_gas().unwrap() + 200;
3542 pool.update_blob_fee(increased_blob_fee, Ordering::Equal, |_| {});
3543 assert!(pool.pending_pool.is_empty());
3544 assert_eq!(pool.blob_pool.len(), 1);
3545
3546 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3547 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3548
3549 pool.all_transactions.pending_fees.blob_fee = tx.max_fee_per_blob_gas().unwrap();
3551
3552 let mut outcome = UpdateOutcome::default();
3553 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3554
3555 assert_eq!(pool.pending_pool.len(), 1);
3556 assert!(pool.blob_pool.is_empty());
3557 assert_eq!(outcome.promoted.len(), 1);
3558 assert_eq!(outcome.promoted[0].id(), &id);
3559 assert_eq!(pool.all_transactions.pending_fees.base_fee, prev_base_fee);
3560 assert_eq!(pool.all_transactions.pending_fees.blob_fee, tx.max_fee_per_blob_gas().unwrap());
3561
3562 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3563 assert_eq!(tx_meta.subpool, SubPool::Pending);
3564 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3565 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3566 }
3567
3568 #[test]
3569 fn apply_fee_updates_promotes_blob_after_basefee_drop() {
3570 let mut f = MockTransactionFactory::default();
3571 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3572
3573 let initial_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3574
3575 let tx = MockTransaction::eip4844()
3576 .with_max_fee(500)
3577 .with_priority_fee(1)
3578 .with_blob_fee(initial_blob_fee + 100);
3579 let validated = f.validated(tx);
3580 let id = *validated.id();
3581 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3582
3583 assert_eq!(pool.pending_pool.len(), 1);
3584
3585 let high_base_fee = 600;
3587 pool.update_basefee(high_base_fee, |_| {});
3588 assert!(pool.pending_pool.is_empty());
3589 assert_eq!(pool.blob_pool.len(), 1);
3590
3591 let prev_base_fee = high_base_fee;
3592 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3593
3594 pool.all_transactions.pending_fees.base_fee = 400;
3596
3597 let mut outcome = UpdateOutcome::default();
3598 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3599
3600 assert_eq!(pool.pending_pool.len(), 1);
3601 assert!(pool.blob_pool.is_empty());
3602 assert_eq!(outcome.promoted.len(), 1);
3603 assert_eq!(outcome.promoted[0].id(), &id);
3604 assert_eq!(pool.all_transactions.pending_fees.base_fee, 400);
3605 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3606
3607 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3608 assert_eq!(tx_meta.subpool, SubPool::Pending);
3609 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
3610 assert!(tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3611 }
3612
3613 #[test]
3614 fn apply_fee_updates_demotes_after_basefee_rise() {
3615 let mut f = MockTransactionFactory::default();
3616 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3617
3618 let tx = MockTransaction::eip1559()
3619 .with_gas_limit(21_000)
3620 .with_max_fee(400)
3621 .with_priority_fee(1);
3622 let validated = f.validated(tx);
3623 let id = *validated.id();
3624 pool.add_transaction(validated, U256::from(1_000_000), 0, None).unwrap();
3625
3626 assert_eq!(pool.pending_pool.len(), 1);
3627
3628 let prev_base_fee = pool.all_transactions.pending_fees.base_fee;
3629 let prev_blob_fee = pool.all_transactions.pending_fees.blob_fee;
3630
3631 let new_base_fee = prev_base_fee + 1_000;
3633 pool.all_transactions.pending_fees.base_fee = new_base_fee;
3634
3635 let mut outcome = UpdateOutcome::default();
3636 pool.apply_fee_updates(prev_base_fee, prev_blob_fee, &mut outcome);
3637
3638 assert!(pool.pending_pool.is_empty());
3639 assert_eq!(pool.basefee_pool.len(), 1);
3640 assert!(outcome.promoted.is_empty());
3641 assert_eq!(pool.all_transactions.pending_fees.base_fee, new_base_fee);
3642 assert_eq!(pool.all_transactions.pending_fees.blob_fee, prev_blob_fee);
3643
3644 let tx_meta = pool.all_transactions.txs.get(&id).unwrap();
3645 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
3646 assert!(!tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK));
3647 }
3648
3649 #[test]
3650 fn get_highest_transaction_by_sender_and_nonce() {
3651 let mut f = MockTransactionFactory::default();
3653 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3654
3655 let tx = MockTransaction::eip1559();
3657 pool.add_transaction(f.validated(tx.clone()), U256::from(1_000), 0, None).unwrap();
3658
3659 let tx1 = tx.inc_price().next();
3661
3662 let tx1_validated = f.validated(tx1.clone());
3664 pool.add_transaction(tx1_validated, U256::from(1_000), 0, None).unwrap();
3665
3666 assert_eq!(
3668 pool.get_highest_nonce_by_sender(f.ids.sender_id(&tx.sender()).unwrap()),
3669 Some(1)
3670 );
3671
3672 let highest_tx = pool
3674 .get_highest_transaction_by_sender(f.ids.sender_id(&tx.sender()).unwrap())
3675 .expect("Failed to retrieve highest transaction");
3676
3677 assert_eq!(highest_tx.as_ref().transaction, tx1);
3679 }
3680
3681 #[test]
3682 fn get_highest_consecutive_transaction_by_sender() {
3683 let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default());
3685 let mut f = MockTransactionFactory::default();
3686
3687 let sender = Address::random();
3689 let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9];
3690 for nonce in txs {
3691 let mut mock_tx = MockTransaction::eip1559();
3692 mock_tx.set_sender(sender);
3693 mock_tx.set_nonce(nonce);
3694
3695 let validated_tx = f.validated(mock_tx);
3696 pool.add_transaction(validated_tx, U256::from(1000), 0, None).unwrap();
3697 }
3698
3699 let sender_id = f.ids.sender_id(&sender).unwrap();
3701 let next_tx =
3702 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0));
3703 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0");
3704
3705 let next_tx =
3706 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4));
3707 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4");
3708
3709 let next_tx =
3710 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3711 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5");
3712
3713 let mut info = SenderInfo::default();
3715 info.update(8, U256::ZERO);
3716 pool.all_transactions.sender_info.insert(sender_id, info);
3717 let next_tx =
3718 pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5));
3719 assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8");
3720 }
3721
3722 #[test]
3723 fn discard_nonce_too_low() {
3724 let mut f = MockTransactionFactory::default();
3725 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3726
3727 let tx = MockTransaction::eip1559().inc_price_by(10);
3728 let validated = f.validated(tx.clone());
3729 let id = *validated.id();
3730 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3731
3732 let next = tx.next();
3733 let validated = f.validated(next.clone());
3734 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3735
3736 assert_eq!(pool.pending_pool.len(), 2);
3737
3738 let mut changed_senders = HashMap::default();
3739 changed_senders.insert(
3740 id.sender,
3741 SenderInfo { state_nonce: next.nonce(), balance: U256::from(1_000) },
3742 );
3743 let outcome = pool.update_accounts(changed_senders);
3744 assert_eq!(outcome.discarded.len(), 1);
3745 assert_eq!(pool.pending_pool.len(), 1);
3746 }
3747
3748 #[test]
3749 fn discard_with_large_blob_txs() {
3750 reth_tracing::init_test_tracing();
3752
3753 let mut f = MockTransactionFactory::default();
3755 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3756 let default_limits = pool.config.blob_limit;
3757
3758 let a_sender = address!("0x000000000000000000000000000000000000000a");
3761
3762 let mut block_info = pool.block_info();
3764 block_info.pending_blob_fee = Some(100);
3765 block_info.pending_basefee = 100;
3766
3767 pool.set_block_info(block_info);
3769
3770 let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip4844)
3772 .into_iter()
3773 .map(|mut tx| {
3774 tx.set_size(default_limits.max_size / 2 + 1);
3775 tx.set_max_fee((block_info.pending_basefee - 1).into());
3776 tx
3777 })
3778 .collect::<Vec<_>>();
3779
3780 for tx in a_txs {
3782 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3783 }
3784
3785 let removed = pool.discard_worst();
3787 assert_eq!(removed.len(), 1);
3788 }
3789
3790 #[test]
3791 fn discard_with_parked_large_txs() {
3792 reth_tracing::init_test_tracing();
3794
3795 let mut f = MockTransactionFactory::default();
3797 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3798 let default_limits = pool.config.queued_limit;
3799
3800 let a_sender = address!("0x000000000000000000000000000000000000000a");
3803
3804 let pool_base_fee = 100;
3806 pool.update_basefee(pool_base_fee, |_| {});
3807
3808 let a_txs = MockTransactionSet::dependent(a_sender, 0, 3, TxType::Eip1559)
3810 .into_iter()
3811 .map(|mut tx| {
3812 tx.set_size(default_limits.max_size / 2 + 1);
3813 tx.set_max_fee((pool_base_fee - 1).into());
3814 tx
3815 })
3816 .collect::<Vec<_>>();
3817
3818 for tx in a_txs {
3820 pool.add_transaction(f.validated(tx), U256::from(1_000), 0, None).unwrap();
3821 }
3822
3823 let removed = pool.discard_worst();
3825 assert_eq!(removed.len(), 1);
3826 }
3827
3828 #[test]
3829 fn discard_at_capacity() {
3830 let mut f = MockTransactionFactory::default();
3831 let queued_limit = SubPoolLimit::new(1000, usize::MAX);
3832 let mut pool =
3833 TxPool::new(MockOrdering::default(), PoolConfig { queued_limit, ..Default::default() });
3834
3835 for _ in 0..queued_limit.max_txs {
3837 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3838 let validated = f.validated(tx.clone());
3839 let _id = *validated.id();
3840 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3841 }
3842
3843 let size = pool.size();
3844 assert_eq!(size.queued, queued_limit.max_txs);
3845
3846 for _ in 0..queued_limit.max_txs {
3847 let tx = MockTransaction::eip1559().inc_price_by(10).inc_nonce();
3848 let validated = f.validated(tx.clone());
3849 let _id = *validated.id();
3850 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3851
3852 pool.discard_worst();
3853 pool.assert_invariants();
3854 assert!(pool.size().queued <= queued_limit.max_txs);
3855 }
3856 }
3857
3858 #[test]
3859 fn discard_blobs_at_capacity() {
3860 let mut f = MockTransactionFactory::default();
3861 let blob_limit = SubPoolLimit::new(1000, usize::MAX);
3862 let mut pool =
3863 TxPool::new(MockOrdering::default(), PoolConfig { blob_limit, ..Default::default() });
3864 pool.all_transactions.pending_fees.blob_fee = 10000;
3865 for _ in 0..blob_limit.max_txs {
3867 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3868 let validated = f.validated(tx.clone());
3869 let _id = *validated.id();
3870 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3871 }
3872
3873 let size = pool.size();
3874 assert_eq!(size.blob, blob_limit.max_txs);
3875
3876 for _ in 0..blob_limit.max_txs {
3877 let tx = MockTransaction::eip4844().inc_price_by(100).with_blob_fee(100);
3878 let validated = f.validated(tx.clone());
3879 let _id = *validated.id();
3880 pool.add_transaction(validated, U256::from(1_000), 0, None).unwrap();
3881
3882 pool.discard_worst();
3883 pool.assert_invariants();
3884 assert!(pool.size().blob <= blob_limit.max_txs);
3885 }
3886 }
3887
3888 #[test]
3889 fn account_updates_sender_balance() {
3890 let mut on_chain_balance = U256::from(100);
3891 let on_chain_nonce = 0;
3892 let mut f = MockTransactionFactory::default();
3893 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3894
3895 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3896 let tx_1 = tx_0.next();
3897 let tx_2 = tx_1.next();
3898
3899 let v0 = f.validated(tx_0);
3901 let v1 = f.validated(tx_1);
3902 let v2 = f.validated(tx_2);
3903
3904 let _res =
3905 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3906 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3907 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3908
3909 assert_eq!(1, pool.pending_transactions().len());
3911 assert_eq!(2, pool.queued_transactions().len());
3912
3913 let mut updated_accounts = HashMap::default();
3915 on_chain_balance = U256::from(300);
3916 updated_accounts.insert(
3917 v0.sender_id(),
3918 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3919 );
3920 pool.update_accounts(updated_accounts.clone());
3921
3922 assert_eq!(3, pool.pending_transactions().len());
3923 assert!(pool.queued_transactions().is_empty());
3924
3925 updated_accounts.entry(v0.sender_id()).and_modify(|v| v.balance = U256::from(1));
3927 pool.update_accounts(updated_accounts);
3928
3929 assert!(pool.pending_transactions().is_empty());
3930 assert_eq!(3, pool.queued_transactions().len());
3931 }
3932
3933 #[test]
3934 fn account_updates_nonce_gap() {
3935 let on_chain_balance = U256::from(10_000);
3936 let mut on_chain_nonce = 0;
3937 let mut f = MockTransactionFactory::default();
3938 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
3939
3940 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
3941 let tx_1 = tx_0.next();
3942 let tx_2 = tx_1.next();
3943
3944 let v0 = f.validated(tx_0);
3946 let v1 = f.validated(tx_1);
3947 let v2 = f.validated(tx_2);
3948
3949 let _res =
3951 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3952 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
3953
3954 assert!(pool.queued_transactions().is_empty());
3955 assert_eq!(2, pool.pending_transactions().len());
3956
3957 pool.remove_transaction_by_hash(v0.hash());
3959
3960 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
3962
3963 assert_eq!(2, pool.queued_transactions().len());
3965 assert!(pool.pending_transactions().is_empty());
3966
3967 let mut updated_accounts = HashMap::default();
3969 on_chain_nonce += 1;
3970 updated_accounts.insert(
3971 v0.sender_id(),
3972 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
3973 );
3974 pool.update_accounts(updated_accounts);
3975
3976 assert!(pool.queued_transactions().is_empty());
3978 assert_eq!(2, pool.pending_transactions().len());
3979 }
3980 #[test]
3981 fn test_transaction_removal() {
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
3990 let v0 = f.validated(tx_0);
3992 let v1 = f.validated(tx_1);
3993
3994 let _res =
3996 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3997 let _res =
3998 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
3999
4000 assert_eq!(0, pool.queued_transactions().len());
4001 assert_eq!(2, pool.pending_transactions().len());
4002
4003 pool.remove_transaction(v0.id());
4005 let pool_txs = pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>();
4007 assert_eq!(vec![v1.nonce()], pool_txs);
4008 }
4009 #[test]
4010 fn test_remove_transactions() {
4011 let on_chain_balance = U256::from(10_000);
4012 let on_chain_nonce = 0;
4013 let mut f = MockTransactionFactory::default();
4014 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4015
4016 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4017 let tx_1 = tx_0.next();
4018 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4019 let tx_3 = tx_2.next();
4020
4021 let v0 = f.validated(tx_0);
4023 let v1 = f.validated(tx_1);
4024 let v2 = f.validated(tx_2);
4025 let v3 = f.validated(tx_3);
4026
4027 let _res =
4029 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4030 let _res =
4031 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4032 let _res =
4033 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4034 let _res =
4035 pool.add_transaction(v3.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4036
4037 assert_eq!(0, pool.queued_transactions().len());
4038 assert_eq!(4, pool.pending_transactions().len());
4039
4040 pool.remove_transactions(vec![*v0.hash(), *v2.hash()]);
4041
4042 assert_eq!(2, pool.queued_transactions().len());
4043 assert!(pool.pending_transactions().is_empty());
4044 assert!(pool.contains(v1.hash()));
4045 assert!(pool.contains(v3.hash()));
4046 }
4047
4048 #[test]
4049 fn test_remove_transactions_middle_pending_hash() {
4050 let on_chain_balance = U256::from(10_000);
4051 let on_chain_nonce = 0;
4052 let mut f = MockTransactionFactory::default();
4053 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4054
4055 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4056 let tx_1 = tx_0.next();
4057 let tx_2 = tx_1.next();
4058 let tx_3 = tx_2.next();
4059
4060 let v0 = f.validated(tx_0);
4062 let v1 = f.validated(tx_1);
4063 let v2 = f.validated(tx_2);
4064 let v3 = f.validated(tx_3);
4065
4066 let _res = pool.add_transaction(v0, on_chain_balance, on_chain_nonce, None).unwrap();
4068 let _res =
4069 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4070 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4071 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4072
4073 assert_eq!(0, pool.queued_transactions().len());
4074 assert_eq!(4, pool.pending_transactions().len());
4075
4076 let mut removed_txs = pool.remove_transactions(vec![*v1.hash()]);
4077 assert_eq!(1, removed_txs.len());
4078
4079 assert_eq!(2, pool.queued_transactions().len());
4080 assert_eq!(1, pool.pending_transactions().len());
4081
4082 let removed_tx = removed_txs.pop().unwrap();
4084 let v1 = f.validated(removed_tx.transaction.clone());
4085 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4086 assert_eq!(0, pool.queued_transactions().len());
4087 assert_eq!(4, pool.pending_transactions().len());
4088 }
4089
4090 #[test]
4091 fn test_remove_transactions_and_descendants() {
4092 let on_chain_balance = U256::from(10_000);
4093 let on_chain_nonce = 0;
4094 let mut f = MockTransactionFactory::default();
4095 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4096
4097 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4098 let tx_1 = tx_0.next();
4099 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4100 let tx_3 = tx_2.next();
4101 let tx_4 = tx_3.next();
4102
4103 let v0 = f.validated(tx_0);
4105 let v1 = f.validated(tx_1);
4106 let v2 = f.validated(tx_2);
4107 let v3 = f.validated(tx_3);
4108 let v4 = f.validated(tx_4);
4109
4110 let _res =
4112 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4113 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4114 let _res =
4115 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4116 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4117 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4118
4119 assert_eq!(0, pool.queued_transactions().len());
4120 assert_eq!(5, pool.pending_transactions().len());
4121
4122 pool.remove_transactions_and_descendants(vec![*v0.hash(), *v2.hash()]);
4123
4124 assert_eq!(0, pool.queued_transactions().len());
4125 assert_eq!(0, pool.pending_transactions().len());
4126 }
4127 #[test]
4128 fn test_remove_descendants() {
4129 let on_chain_balance = U256::from(10_000);
4130 let on_chain_nonce = 0;
4131 let mut f = MockTransactionFactory::default();
4132 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4133
4134 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4135 let tx_1 = tx_0.next();
4136 let tx_2 = tx_1.next();
4137 let tx_3 = tx_2.next();
4138
4139 let v0 = f.validated(tx_0);
4141 let v1 = f.validated(tx_1);
4142 let v2 = f.validated(tx_2);
4143 let v3 = f.validated(tx_3);
4144
4145 let _res =
4147 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4148 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4149 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4150 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4151
4152 assert_eq!(0, pool.queued_transactions().len());
4153 assert_eq!(4, pool.pending_transactions().len());
4154
4155 let mut removed = Vec::new();
4156 pool.remove_transaction(v0.id());
4157 pool.remove_descendants(v0.id(), &mut removed);
4158
4159 assert_eq!(0, pool.queued_transactions().len());
4160 assert_eq!(0, pool.pending_transactions().len());
4161 assert_eq!(3, removed.len());
4162 }
4163 #[test]
4164 fn test_remove_transactions_by_sender() {
4165 let on_chain_balance = U256::from(10_000);
4166 let on_chain_nonce = 0;
4167 let mut f = MockTransactionFactory::default();
4168 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4169
4170 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4171 let tx_1 = tx_0.next();
4172 let tx_2 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4173 let tx_3 = tx_2.next();
4174 let tx_4 = tx_3.next();
4175
4176 let v0 = f.validated(tx_0);
4178 let v1 = f.validated(tx_1);
4179 let v2 = f.validated(tx_2);
4180 let v3 = f.validated(tx_3);
4181 let v4 = f.validated(tx_4);
4182
4183 let _res =
4185 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4186 let _res =
4187 pool.add_transaction(v1.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4188 let _res =
4189 pool.add_transaction(v2.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4190 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4191 let _res = pool.add_transaction(v4, on_chain_balance, on_chain_nonce, None).unwrap();
4192
4193 assert_eq!(0, pool.queued_transactions().len());
4194 assert_eq!(5, pool.pending_transactions().len());
4195
4196 pool.remove_transactions_by_sender(v2.sender_id());
4197
4198 assert_eq!(0, pool.queued_transactions().len());
4199 assert_eq!(2, pool.pending_transactions().len());
4200 assert!(pool.contains(v0.hash()));
4201 assert!(pool.contains(v1.hash()));
4202 }
4203 #[test]
4204 fn wrong_best_order_of_transactions() {
4205 let on_chain_balance = U256::from(10_000);
4206 let mut on_chain_nonce = 0;
4207 let mut f = MockTransactionFactory::default();
4208 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4209
4210 let tx_0 = MockTransaction::eip1559().set_gas_price(100).inc_limit();
4211 let tx_1 = tx_0.next();
4212 let tx_2 = tx_1.next();
4213 let tx_3 = tx_2.next();
4214
4215 let v0 = f.validated(tx_0);
4217 let v1 = f.validated(tx_1);
4218 let v2 = f.validated(tx_2);
4219 let v3 = f.validated(tx_3);
4220
4221 let _res =
4223 pool.add_transaction(v0.clone(), on_chain_balance, on_chain_nonce, None).unwrap();
4224 let _res = pool.add_transaction(v1, on_chain_balance, on_chain_nonce, None).unwrap();
4225
4226 assert_eq!(0, pool.queued_transactions().len());
4227 assert_eq!(2, pool.pending_transactions().len());
4228
4229 pool.remove_transaction(v0.id());
4231
4232 let _res = pool.add_transaction(v2, on_chain_balance, on_chain_nonce, None).unwrap();
4234
4235 assert_eq!(1, pool.queued_transactions().len());
4237 assert_eq!(1, pool.pending_transactions().len());
4238
4239 let mut updated_accounts = HashMap::default();
4241 on_chain_nonce += 1;
4242 updated_accounts.insert(
4243 v0.sender_id(),
4244 SenderInfo { state_nonce: on_chain_nonce, balance: on_chain_balance },
4245 );
4246 pool.update_accounts(updated_accounts);
4247
4248 assert_eq!(0, pool.queued_transactions().len());
4251 assert_eq!(2, pool.pending_transactions().len());
4252
4253 let _res = pool.add_transaction(v3, on_chain_balance, on_chain_nonce, None).unwrap();
4255 assert_eq!(0, pool.queued_transactions().len());
4256 assert_eq!(3, pool.pending_transactions().len());
4257
4258 assert_eq!(
4261 pool.best_transactions().map(|x| x.id().nonce).collect::<Vec<_>>(),
4262 vec![1, 2, 3]
4263 );
4264 }
4265
4266 #[test]
4267 fn test_best_with_attributes() {
4268 let on_chain_balance = U256::MAX;
4269 let on_chain_nonce = 0;
4270 let mut f = MockTransactionFactory::default();
4271 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4272
4273 let base_fee: u128 = 100;
4274 let blob_fee: u128 = 100;
4275
4276 let mut block_info = pool.block_info();
4278 block_info.pending_basefee = base_fee as u64;
4279 block_info.pending_blob_fee = Some(blob_fee);
4280 pool.set_block_info(block_info);
4281
4282 let tx1 = MockTransaction::eip4844()
4284 .with_sender(Address::with_last_byte(1))
4285 .with_max_fee(base_fee + 10)
4286 .with_blob_fee(blob_fee + 10);
4287 let tx2 = MockTransaction::eip4844()
4288 .with_sender(Address::with_last_byte(2))
4289 .with_max_fee(base_fee + 10)
4290 .with_blob_fee(blob_fee);
4291 let tx3 = MockTransaction::eip4844()
4292 .with_sender(Address::with_last_byte(3))
4293 .with_max_fee(base_fee)
4294 .with_blob_fee(blob_fee + 10);
4295 let tx4 = MockTransaction::eip4844()
4296 .with_sender(Address::with_last_byte(4))
4297 .with_max_fee(base_fee)
4298 .with_blob_fee(blob_fee);
4299 let tx5 = MockTransaction::eip4844()
4300 .with_sender(Address::with_last_byte(5))
4301 .with_max_fee(base_fee)
4302 .with_blob_fee(blob_fee - 10);
4303 let tx6 = MockTransaction::eip4844()
4304 .with_sender(Address::with_last_byte(6))
4305 .with_max_fee(base_fee - 10)
4306 .with_blob_fee(blob_fee);
4307 let tx7 = MockTransaction::eip4844()
4308 .with_sender(Address::with_last_byte(7))
4309 .with_max_fee(base_fee - 10)
4310 .with_blob_fee(blob_fee - 10);
4311
4312 for tx in vec![
4313 tx1.clone(),
4314 tx2.clone(),
4315 tx3.clone(),
4316 tx4.clone(),
4317 tx5.clone(),
4318 tx6.clone(),
4319 tx7.clone(),
4320 ] {
4321 pool.add_transaction(f.validated(tx.clone()), on_chain_balance, on_chain_nonce, None)
4322 .unwrap();
4323 }
4324
4325 let base_fee = base_fee as u64;
4326 let blob_fee = blob_fee as u64;
4327
4328 let cases = vec![
4329 (BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee + 5)), vec![tx1.clone()]),
4331 (
4333 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee)),
4334 vec![tx1.clone(), tx2.clone()],
4335 ),
4336 (
4338 BestTransactionsAttributes::new(base_fee + 5, Some(blob_fee - 5)),
4339 vec![tx1.clone(), tx2.clone()],
4340 ),
4341 (
4343 BestTransactionsAttributes::new(base_fee, Some(blob_fee + 5)),
4344 vec![tx1.clone(), tx3.clone()],
4345 ),
4346 (
4348 BestTransactionsAttributes::new(base_fee, Some(blob_fee)),
4349 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()],
4350 ),
4351 (
4353 BestTransactionsAttributes::new(base_fee, Some(blob_fee - 10)),
4354 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx5.clone()],
4355 ),
4356 (
4358 BestTransactionsAttributes::new(base_fee - 5, Some(blob_fee + 5)),
4359 vec![tx1.clone(), tx3.clone()],
4360 ),
4361 (
4363 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee)),
4364 vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone(), tx6.clone()],
4365 ),
4366 (
4368 BestTransactionsAttributes::new(base_fee - 10, Some(blob_fee - 10)),
4369 vec![tx1, tx2, tx5, tx3, tx4, tx6, tx7],
4370 ),
4371 ];
4372
4373 for (idx, (attribute, expected)) in cases.into_iter().enumerate() {
4374 let mut best = pool.best_transactions_with_attributes(attribute);
4375
4376 for (tx_idx, expected_tx) in expected.into_iter().enumerate() {
4377 let tx = best.next().expect("Transaction should be returned");
4378 assert_eq!(
4379 tx.transaction,
4380 expected_tx,
4381 "Failed tx {} in case {}",
4382 tx_idx + 1,
4383 idx + 1
4384 );
4385 }
4386
4387 assert!(best.next().is_none());
4389 }
4390 }
4391
4392 #[test]
4393 fn test_pending_ordering() {
4394 let mut f = MockTransactionFactory::default();
4395 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4396
4397 let tx_0 = MockTransaction::eip1559().with_nonce(1).set_gas_price(100).inc_limit();
4398 let tx_1 = tx_0.next();
4399
4400 let v0 = f.validated(tx_0);
4401 let v1 = f.validated(tx_1);
4402
4403 pool.add_transaction(v0.clone(), U256::MAX, 0, None).unwrap();
4405 assert_eq!(1, pool.queued_transactions().len());
4406
4407 pool.add_transaction(v1, U256::MAX, 1, None).unwrap();
4409
4410 assert_eq!(2, pool.pending_transactions().len());
4411 assert_eq!(0, pool.queued_transactions().len());
4412
4413 assert_eq!(
4414 pool.pending_pool.independent().get(&v0.sender_id()).unwrap().transaction.nonce(),
4415 v0.nonce()
4416 );
4417 }
4418
4419 #[test]
4421 fn one_sender_one_independent_transaction() {
4422 let mut on_chain_balance = U256::from(4_999); let mut on_chain_nonce = 40;
4424 let mut f = MockTransactionFactory::default();
4425 let mut pool = TxPool::mock();
4426 let mut submitted_txs = Vec::new();
4427
4428 let template =
4430 MockTransaction::eip1559().inc_price().inc_limit().with_value(U256::from(1_001));
4431
4432 for tx_nonce in 40..48 {
4435 let tx = f.validated(template.clone().with_nonce(tx_nonce).rng_hash());
4436 submitted_txs.push(*tx.id());
4437 pool.add_transaction(tx, on_chain_balance, on_chain_nonce, None).unwrap();
4438 }
4439
4440 on_chain_balance = U256::from(999_999);
4443 on_chain_nonce = 42;
4444 pool.remove_transaction(&submitted_txs[0]);
4445 pool.remove_transaction(&submitted_txs[1]);
4446
4447 for tx_nonce in 48..52 {
4449 pool.add_transaction(
4450 f.validated(template.clone().with_nonce(tx_nonce).rng_hash()),
4451 on_chain_balance,
4452 on_chain_nonce,
4453 None,
4454 )
4455 .unwrap();
4456 }
4457
4458 let best_txs: Vec<_> = pool.pending().best().map(|tx| *tx.id()).collect();
4459 assert_eq!(best_txs.len(), 10); assert_eq!(pool.pending_pool.independent().len(), 1);
4462 }
4463
4464 #[test]
4465 fn test_insertion_disorder() {
4466 let mut f = MockTransactionFactory::default();
4467 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4468
4469 let sender = address!("0x1234567890123456789012345678901234567890");
4470 let tx0 = f.validated_arc(
4471 MockTransaction::legacy().with_sender(sender).with_nonce(0).with_gas_price(10),
4472 );
4473 let tx1 = f.validated_arc(
4474 MockTransaction::eip1559()
4475 .with_sender(sender)
4476 .with_nonce(1)
4477 .with_gas_limit(1000)
4478 .with_gas_price(10),
4479 );
4480 let tx2 = f.validated_arc(
4481 MockTransaction::legacy().with_sender(sender).with_nonce(2).with_gas_price(10),
4482 );
4483 let tx3 = f.validated_arc(
4484 MockTransaction::legacy().with_sender(sender).with_nonce(3).with_gas_price(10),
4485 );
4486
4487 pool.add_transaction((*tx0).clone(), U256::from(1000), 0, None).unwrap();
4489 let mut best = pool.best_transactions();
4490 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4491 assert_eq!(t0.id(), tx0.id());
4492 pool.add_transaction((*tx1).clone(), U256::from(1000), 0, None).unwrap();
4494 let mut best = pool.best_transactions();
4495 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4496 assert_eq!(t0.id(), tx0.id());
4497 assert!(best.next().is_none());
4498
4499 pool.add_transaction((*tx2).clone(), U256::MAX, 0, None).unwrap();
4501
4502 let mut best = pool.best_transactions();
4503
4504 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4505 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4506 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4507 assert_eq!(t0.id(), tx0.id());
4508 assert_eq!(t1.id(), tx1.id());
4509 assert_eq!(t2.id(), tx2.id());
4510
4511 pool.add_transaction((*tx3).clone(), U256::MAX, 0, None).unwrap();
4513 let mut best = pool.best_transactions();
4514 let t0 = best.next().expect("tx0 should be put in the pending subpool");
4515 let t1 = best.next().expect("tx1 should be put in the pending subpool");
4516 let t2 = best.next().expect("tx2 should be put in the pending subpool");
4517 let t3 = best.next().expect("tx3 should be put in the pending subpool");
4518 assert_eq!(t0.id(), tx0.id());
4519 assert_eq!(t1.id(), tx1.id());
4520 assert_eq!(t2.id(), tx2.id());
4521 assert_eq!(t3.id(), tx3.id());
4522 }
4523
4524 #[test]
4525 fn test_non_4844_blob_fee_bit_invariant() {
4526 let mut f = MockTransactionFactory::default();
4527 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4528
4529 let non_4844_tx = MockTransaction::eip1559().set_max_fee(200).inc_limit();
4530 let validated = f.validated(non_4844_tx.clone());
4531
4532 assert!(!non_4844_tx.is_eip4844());
4533 pool.add_transaction(validated.clone(), U256::from(10_000), 0, None).unwrap();
4534
4535 let tx_meta = pool.all_transactions.txs.get(validated.id()).unwrap();
4537 assert!(tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4538 assert_eq!(tx_meta.subpool, SubPool::Pending);
4539 }
4540
4541 #[test]
4542 fn test_blob_fee_enforcement_only_applies_to_eip4844() {
4543 let mut f = MockTransactionFactory::default();
4544 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4545
4546 let mut block_info = pool.block_info();
4548 block_info.pending_blob_fee = Some(160);
4549 block_info.pending_basefee = 100;
4550 pool.set_block_info(block_info);
4551
4552 let eip4844_tx = MockTransaction::eip4844()
4553 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4554 .with_max_fee(200)
4555 .with_blob_fee(150) .inc_limit();
4557
4558 let non_4844_tx = MockTransaction::eip1559()
4559 .with_sender(address!("0x000000000000000000000000000000000000000b"))
4560 .set_max_fee(200)
4561 .inc_limit();
4562
4563 let validated_4844 = f.validated(eip4844_tx);
4564 let validated_non_4844 = f.validated(non_4844_tx);
4565
4566 pool.add_transaction(validated_4844.clone(), U256::from(10_000), 0, None).unwrap();
4567 pool.add_transaction(validated_non_4844.clone(), U256::from(10_000), 0, None).unwrap();
4568
4569 let tx_4844_meta = pool.all_transactions.txs.get(validated_4844.id()).unwrap();
4570 let tx_non_4844_meta = pool.all_transactions.txs.get(validated_non_4844.id()).unwrap();
4571
4572 assert!(!tx_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4574 assert_eq!(tx_4844_meta.subpool, SubPool::Blob);
4575
4576 assert!(tx_non_4844_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK));
4578 assert_eq!(tx_non_4844_meta.subpool, SubPool::Pending);
4579 }
4580
4581 #[test]
4582 fn test_basefee_decrease_preserves_non_4844_blob_fee_bit() {
4583 let mut f = MockTransactionFactory::default();
4584 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4585
4586 let non_4844_tx = MockTransaction::eip1559()
4588 .with_sender(address!("0x000000000000000000000000000000000000000a"))
4589 .set_max_fee(500) .inc_limit();
4591
4592 pool.update_basefee(600, |_| {});
4594
4595 let validated = f.validated(non_4844_tx);
4596 let tx_id = *validated.id();
4597 pool.add_transaction(validated, U256::from(10_000), 0, None).unwrap();
4598
4599 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4601 assert_eq!(tx_meta.subpool, SubPool::BaseFee);
4602 assert!(
4603 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4604 "Non-4844 tx in BaseFee pool must retain ENOUGH_BLOB_FEE_CAP_BLOCK bit"
4605 );
4606
4607 pool.update_basefee(400, |_| {});
4610
4611 let tx_meta = pool.all_transactions.txs.get(&tx_id).unwrap();
4613 assert_eq!(
4614 tx_meta.subpool,
4615 SubPool::Pending,
4616 "Non-4844 tx should be promoted from BaseFee to Pending after basefee decrease"
4617 );
4618 assert!(
4619 tx_meta.state.contains(TxState::ENOUGH_BLOB_FEE_CAP_BLOCK),
4620 "Non-4844 tx must NEVER lose ENOUGH_BLOB_FEE_CAP_BLOCK bit during basefee promotion"
4621 );
4622 assert!(
4623 tx_meta.state.contains(TxState::ENOUGH_FEE_CAP_BLOCK),
4624 "Non-4844 tx should gain ENOUGH_FEE_CAP_BLOCK bit after basefee decrease"
4625 );
4626 }
4627
4628 #[test]
4634 fn best_transactions_nonce_order_on_balance_unlock() {
4635 let mut f = MockTransactionFactory::default();
4636 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4637
4638 let sender = Address::random();
4639 let on_chain_balance = U256::from(10_000);
4640
4641 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
4643 let tx1 = tx0.next().inc_limit().with_value(U256::from(on_chain_balance));
4645 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
4647
4648 let v0 = f.validated(tx0);
4649 let v1 = f.validated(tx1);
4650 let v2 = f.validated(tx2);
4651
4652 pool.add_transaction(v0, on_chain_balance, 0, None).unwrap();
4654
4655 let mut best = pool.best_transactions();
4657
4658 let first = best.next().expect("should yield tx0");
4660 assert_eq!(first.id().nonce, 0);
4661
4662 pool.add_transaction(v1, on_chain_balance, 0, None).unwrap();
4665
4666 assert!(best.next().is_none(), "tx1 should be queued, not pending");
4668
4669 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
4673
4674 let t1 = best.next().expect("should yield a transaction");
4675 let t2 = best.next().expect("should yield a transaction");
4676
4677 assert_eq!(
4679 t1.id().nonce,
4680 1,
4681 "first yielded tx should be nonce 1, got nonce {}",
4682 t1.id().nonce
4683 );
4684 assert_eq!(
4685 t2.id().nonce,
4686 2,
4687 "second yielded tx should be nonce 2, got nonce {}",
4688 t2.id().nonce
4689 );
4690 }
4691
4692 #[test]
4696 fn best_transactions_nonce_order_on_gap_fill() {
4697 let mut f = MockTransactionFactory::default();
4698 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4699
4700 let sender = Address::random();
4701 let balance = U256::MAX;
4702
4703 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
4705 let tx1 = tx0.next().inc_limit();
4707
4708 let v0 = f.validated(tx0);
4709 let v1 = f.validated(tx1);
4710
4711 pool.add_transaction(v1, balance, 0, None).unwrap();
4713
4714 let mut best = pool.best_transactions();
4716 assert!(best.next().is_none(), "pool should have no pending txs yet");
4717
4718 pool.add_transaction(v0, balance, 0, None).unwrap();
4720
4721 let t0 = best.next().expect("should yield a transaction");
4722 let t1 = best.next().expect("should yield a transaction");
4723
4724 assert_eq!(t0.id().nonce, 0, "first yielded tx should be nonce 0, got {}", t0.id().nonce);
4725 assert_eq!(t1.id().nonce, 1, "second yielded tx should be nonce 1, got {}", t1.id().nonce);
4726 }
4727
4728 #[test]
4732 fn best_transactions_nonce_order_mixed_promotions() {
4733 let mut f = MockTransactionFactory::default();
4734 let mut pool = TxPool::new(MockOrdering::default(), Default::default());
4735
4736 let sender = Address::random();
4737 let low_balance = U256::from(10_000);
4738
4739 let tx0 = MockTransaction::eip1559().with_sender(sender).set_gas_price(100).inc_limit();
4741 let tx1 = tx0.next().inc_limit().with_value(U256::from(low_balance));
4743 let tx2 = tx1.next().inc_limit().with_value(U256::ZERO);
4745 let tx3 = tx2.next().inc_limit().with_value(U256::ZERO);
4747
4748 let v0 = f.validated(tx0);
4749 let v1 = f.validated(tx1);
4750 let v2 = f.validated(tx2);
4751 let v3 = f.validated(tx3);
4752
4753 pool.add_transaction(v0, low_balance, 0, None).unwrap();
4755
4756 pool.add_transaction(v1, low_balance, 0, None).unwrap();
4758
4759 pool.add_transaction(v3, low_balance, 0, None).unwrap();
4761
4762 let mut best = pool.best_transactions();
4763
4764 let first = best.next().expect("should yield tx0");
4766 assert_eq!(first.id().nonce, 0);
4767 assert!(best.next().is_none(), "only tx0 should be pending");
4768
4769 pool.add_transaction(v2, U256::MAX, 0, None).unwrap();
4774
4775 let t1 = best.next().expect("should yield nonce 1");
4776 let t2 = best.next().expect("should yield nonce 2");
4777 let t3 = best.next().expect("should yield nonce 3");
4778
4779 assert_eq!(t1.id().nonce, 1, "expected nonce 1, got {}", t1.id().nonce);
4780 assert_eq!(t2.id().nonce, 2, "expected nonce 2, got {}", t2.id().nonce);
4781 assert_eq!(t3.id().nonce, 3, "expected nonce 3, got {}", t3.id().nonce);
4782 }
4783}