Skip to main content

reth_transaction_pool/
traits.rs

1//! Transaction Pool Traits and Types
2//!
3//! This module defines the core abstractions for transaction pool implementations,
4//! handling the complexity of different transaction representations across the
5//! network, mempool, and the chain itself.
6//!
7//! ## Key Concepts
8//!
9//! ### Transaction Representations
10//!
11//! Transactions exist in different formats throughout their lifecycle:
12//!
13//! 1. **Consensus Format** ([`PoolTransaction::Consensus`])
14//!    - The canonical format stored in blocks
15//!    - Minimal size for efficient storage
16//!    - Example: EIP-4844 transactions store only blob hashes: ([`TransactionSigned::Eip4844`])
17//!
18//! 2. **Pooled Format** ([`PoolTransaction::Pooled`])
19//!    - Extended format for network propagation
20//!    - Includes additional validation data
21//!    - Example: EIP-4844 transactions include full blob sidecars: ([`PooledTransactionVariant`])
22//!
23//! ### Type Relationships
24//!
25//! ```text
26//! NodePrimitives::SignedTx  ←──   NetworkPrimitives::BroadcastedTransaction
27//!        │                              │
28//!        │ (consensus format)           │ (announced to peers)
29//!        │                              │
30//!        └──────────┐  ┌────────────────┘
31//!                   ▼  ▼
32//!            PoolTransaction::Consensus
33//!                   │ ▲
34//!                   │ │ from pooled (always succeeds)
35//!                   │ │
36//!                   ▼ │ try_from consensus (may fail)
37//!            PoolTransaction::Pooled  ←──→  NetworkPrimitives::PooledTransaction
38//!                                             (sent on request)
39//! ```
40//!
41//! ### Special Cases
42//!
43//! #### EIP-4844 Blob Transactions
44//! - Consensus format: Only blob hashes (32 bytes each)
45//! - Pooled format: Full blobs + commitments + proofs (large data per blob)
46//! - Network behavior: Not broadcast automatically, only sent on explicit request
47//!
48//! #### Optimism Deposit Transactions
49//! - Only exist in consensus format
50//! - Never enter the mempool (system transactions)
51//! - Conversion from consensus to pooled always fails
52
53use crate::{
54    blobstore::BlobStoreError,
55    error::{InvalidPoolTransactionError, PoolError, PoolResult},
56    pool::{
57        state::SubPool, BestTransactionFilter, NewTransactionEvent, TransactionEvents,
58        TransactionListenerKind,
59    },
60    validate::ValidPoolTransaction,
61    AddedTransactionOutcome, AllTransactionsEvents,
62};
63use alloy_consensus::{error::ValueError, transaction::TxHashRef, BlockHeader, Signed, Typed2718};
64use alloy_eips::{
65    eip2718::{Encodable2718, WithEncoded},
66    eip2930::AccessList,
67    eip4844::{
68        env_settings::KzgSettings, BlobAndProofV1, BlobAndProofV2, BlobCellsAndProofsV1,
69        BlobTransactionValidationError,
70    },
71    eip7594::BlobTransactionSidecarVariant,
72    eip7702::SignedAuthorization,
73};
74use alloy_primitives::{map::AddressSet, Address, Bytes, TxHash, TxKind, B128, B256, U256};
75use futures_util::{ready, Stream};
76use reth_eth_wire_types::HandleMempoolData;
77use reth_ethereum_primitives::{PooledTransactionVariant, TransactionSigned};
78use reth_execution_types::ChangedAccount;
79use reth_primitives_traits::{Block, InMemorySize, Recovered, SealedBlock, SignedTransaction};
80use serde::{Deserialize, Serialize};
81use std::{
82    collections::HashMap,
83    fmt,
84    fmt::Debug,
85    future::Future,
86    pin::Pin,
87    sync::Arc,
88    task::{Context, Poll},
89};
90use tokio::sync::mpsc::Receiver;
91
92/// The `PeerId` type.
93pub type PeerId = alloy_primitives::B512;
94
95/// Helper type alias to access [`PoolTransaction`] for a given [`TransactionPool`].
96pub type PoolTx<P> = <P as TransactionPool>::Transaction;
97/// Helper type alias to access [`PoolTransaction::Consensus`] for a given [`TransactionPool`].
98pub type PoolConsensusTx<P> = <<P as TransactionPool>::Transaction as PoolTransaction>::Consensus;
99
100/// Helper type alias to access [`PoolTransaction::Pooled`] for a given [`TransactionPool`].
101pub type PoolPooledTx<P> = <<P as TransactionPool>::Transaction as PoolTransaction>::Pooled;
102
103/// General purpose abstraction of a transaction-pool.
104///
105/// This is intended to be used by API-consumers such as RPC that need inject new incoming,
106/// unverified transactions. And by block production that needs to get transactions to execute in a
107/// new block.
108///
109/// Note: This requires `Clone` for convenience, since it is assumed that this will be implemented
110/// for a wrapped `Arc` type, see also [`Pool`](crate::Pool).
111#[auto_impl::auto_impl(&, Arc)]
112pub trait TransactionPool: Clone + Debug + Send + Sync {
113    /// The transaction type of the pool
114    type Transaction: EthPoolTransaction;
115
116    /// Returns stats about the pool and all sub-pools.
117    fn pool_size(&self) -> PoolSize;
118
119    /// Returns the block the pool is currently tracking.
120    ///
121    /// This tracks the block that the pool has last seen.
122    fn block_info(&self) -> BlockInfo;
123
124    /// Imports an _external_ transaction.
125    ///
126    /// This is intended to be used by the network to insert incoming transactions received over the
127    /// p2p network.
128    ///
129    /// Consumer: P2P
130    fn add_external_transaction(
131        &self,
132        transaction: Self::Transaction,
133    ) -> impl Future<Output = PoolResult<AddedTransactionOutcome>> + Send {
134        self.add_transaction(TransactionOrigin::External, transaction)
135    }
136
137    /// Imports all _external_ transactions
138    ///
139    /// Consumer: Utility
140    fn add_external_transactions(
141        &self,
142        transactions: Vec<Self::Transaction>,
143    ) -> impl Future<Output = Vec<PoolResult<AddedTransactionOutcome>>> + Send {
144        self.add_transactions(TransactionOrigin::External, transactions)
145    }
146
147    /// Adds an _unvalidated_ transaction into the pool and subscribe to state changes.
148    ///
149    /// This is the same as [`TransactionPool::add_transaction`] but returns an event stream for the
150    /// given transaction.
151    ///
152    /// Consumer: Custom
153    fn add_transaction_and_subscribe(
154        &self,
155        origin: TransactionOrigin,
156        transaction: Self::Transaction,
157    ) -> impl Future<Output = PoolResult<TransactionEvents>> + Send;
158
159    /// Adds an _unvalidated_ transaction into the pool.
160    ///
161    /// Consumer: RPC
162    fn add_transaction(
163        &self,
164        origin: TransactionOrigin,
165        transaction: Self::Transaction,
166    ) -> impl Future<Output = PoolResult<AddedTransactionOutcome>> + Send;
167
168    /// Adds the given _unvalidated_ transactions into the pool.
169    ///
170    /// All transactions will use the same `origin`.
171    ///
172    /// Returns a list of results.
173    ///
174    /// Consumer: RPC
175    fn add_transactions(
176        &self,
177        origin: TransactionOrigin,
178        transactions: Vec<Self::Transaction>,
179    ) -> impl Future<Output = Vec<PoolResult<AddedTransactionOutcome>>> + Send;
180
181    /// Adds the given _unvalidated_ transactions into the pool.
182    ///
183    /// Each transaction is paired with its own [`TransactionOrigin`].
184    ///
185    /// Returns a list of results.
186    ///
187    /// Consumer: RPC
188    fn add_transactions_with_origins(
189        &self,
190        transactions: Vec<(TransactionOrigin, Self::Transaction)>,
191    ) -> impl Future<Output = Vec<PoolResult<AddedTransactionOutcome>>> + Send;
192
193    /// Submit a consensus transaction directly to the pool
194    fn add_consensus_transaction(
195        &self,
196        tx: Recovered<<Self::Transaction as PoolTransaction>::Consensus>,
197        origin: TransactionOrigin,
198    ) -> impl Future<Output = PoolResult<AddedTransactionOutcome>> + Send {
199        async move {
200            let tx_hash = *tx.tx_hash();
201
202            let pool_transaction = match Self::Transaction::try_from_consensus(tx) {
203                Ok(tx) => tx,
204                Err(e) => return Err(PoolError::other(tx_hash, e.to_string())),
205            };
206
207            self.add_transaction(origin, pool_transaction).await
208        }
209    }
210
211    /// Submit a consensus transaction and subscribe to event stream
212    fn add_consensus_transaction_and_subscribe(
213        &self,
214        tx: Recovered<<Self::Transaction as PoolTransaction>::Consensus>,
215        origin: TransactionOrigin,
216    ) -> impl Future<Output = PoolResult<TransactionEvents>> + Send {
217        async move {
218            let tx_hash = *tx.tx_hash();
219
220            let pool_transaction = match Self::Transaction::try_from_consensus(tx) {
221                Ok(tx) => tx,
222                Err(e) => return Err(PoolError::other(tx_hash, e.to_string())),
223            };
224
225            self.add_transaction_and_subscribe(origin, pool_transaction).await
226        }
227    }
228
229    /// Returns a new transaction change event stream for the given transaction.
230    ///
231    /// Returns `None` if the transaction is not in the pool.
232    fn transaction_event_listener(&self, tx_hash: TxHash) -> Option<TransactionEvents>;
233
234    /// Returns a new transaction change event stream for _all_ transactions in the pool.
235    fn all_transactions_event_listener(&self) -> AllTransactionsEvents<Self::Transaction>;
236
237    /// Returns a new Stream that yields transactions hashes for new __pending__ transactions
238    /// inserted into the pool that are allowed to be propagated.
239    ///
240    /// Note: This is intended for networking and will __only__ yield transactions that are allowed
241    /// to be propagated over the network, see also [`TransactionListenerKind`].
242    ///
243    /// Consumer: RPC/P2P
244    fn pending_transactions_listener(&self) -> Receiver<TxHash> {
245        self.pending_transactions_listener_for(TransactionListenerKind::PropagateOnly)
246    }
247
248    /// Returns a new [Receiver] that yields transactions hashes for new __pending__ transactions
249    /// inserted into the pending pool depending on the given [`TransactionListenerKind`] argument.
250    fn pending_transactions_listener_for(&self, kind: TransactionListenerKind) -> Receiver<TxHash>;
251
252    /// Returns a new stream that yields new valid transactions added to the pool.
253    fn new_transactions_listener(&self) -> Receiver<NewTransactionEvent<Self::Transaction>> {
254        self.new_transactions_listener_for(TransactionListenerKind::PropagateOnly)
255    }
256
257    /// Returns a new [Receiver] that yields blob "sidecars" (blobs w/ assoc. kzg
258    /// commitments/proofs) for eip-4844 transactions inserted into the pool
259    fn blob_transaction_sidecars_listener(&self) -> Receiver<NewBlobSidecar>;
260
261    /// Returns a new stream that yields new valid transactions added to the pool
262    /// depending on the given [`TransactionListenerKind`] argument.
263    fn new_transactions_listener_for(
264        &self,
265        kind: TransactionListenerKind,
266    ) -> Receiver<NewTransactionEvent<Self::Transaction>>;
267
268    /// Returns a new Stream that yields new transactions added to the pending sub-pool.
269    ///
270    /// This is a convenience wrapper around [`Self::new_transactions_listener`] that filters for
271    /// [`SubPool::Pending`](crate::SubPool).
272    fn new_pending_pool_transactions_listener(
273        &self,
274    ) -> NewSubpoolTransactionStream<Self::Transaction> {
275        NewSubpoolTransactionStream::new(
276            self.new_transactions_listener_for(TransactionListenerKind::PropagateOnly),
277            SubPool::Pending,
278        )
279    }
280
281    /// Returns a new Stream that yields new transactions added to the basefee sub-pool.
282    ///
283    /// This is a convenience wrapper around [`Self::new_transactions_listener`] that filters for
284    /// [`SubPool::BaseFee`](crate::SubPool).
285    fn new_basefee_pool_transactions_listener(
286        &self,
287    ) -> NewSubpoolTransactionStream<Self::Transaction> {
288        NewSubpoolTransactionStream::new(self.new_transactions_listener(), SubPool::BaseFee)
289    }
290
291    /// Returns a new Stream that yields new transactions added to the queued-pool.
292    ///
293    /// This is a convenience wrapper around [`Self::new_transactions_listener`] that filters for
294    /// [`SubPool::Queued`](crate::SubPool).
295    fn new_queued_transactions_listener(&self) -> NewSubpoolTransactionStream<Self::Transaction> {
296        NewSubpoolTransactionStream::new(self.new_transactions_listener(), SubPool::Queued)
297    }
298
299    /// Returns a new Stream that yields new transactions added to the blob sub-pool.
300    ///
301    /// This is a convenience wrapper around [`Self::new_transactions_listener`] that filters for
302    /// [`SubPool::Blob`](crate::SubPool).
303    fn new_blob_pool_transactions_listener(
304        &self,
305    ) -> NewSubpoolTransactionStream<Self::Transaction> {
306        NewSubpoolTransactionStream::new(self.new_transactions_listener(), SubPool::Blob)
307    }
308
309    /// Returns the _hashes_ of all transactions in the pool that are allowed to be propagated.
310    ///
311    /// This excludes hashes that aren't allowed to be propagated.
312    ///
313    /// Note: This returns a `Vec` but should guarantee that all hashes are unique.
314    ///
315    /// Consumer: P2P
316    fn pooled_transaction_hashes(&self) -> Vec<TxHash>;
317
318    /// Returns only the first `max` hashes of transactions in the pool.
319    ///
320    /// Consumer: P2P
321    fn pooled_transaction_hashes_max(&self, max: usize) -> Vec<TxHash>;
322
323    /// Returns the _full_ transaction objects all transactions in the pool that are allowed to be
324    /// propagated.
325    ///
326    /// This is intended to be used by the network for the initial exchange of pooled transaction
327    /// _hashes_
328    ///
329    /// Note: This returns a `Vec` but should guarantee that all transactions are unique.
330    ///
331    /// Caution: In case of blob transactions, this does not include the sidecar.
332    ///
333    /// Consumer: P2P
334    fn pooled_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
335
336    /// Returns only the first `max` transactions in the pool.
337    ///
338    /// Consumer: P2P
339    fn pooled_transactions_max(
340        &self,
341        max: usize,
342    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
343
344    /// Returns converted [`PooledTransactionVariant`] for the given transaction hashes that are
345    /// allowed to be propagated.
346    ///
347    /// This adheres to the expected behavior of
348    /// [`GetPooledTransactions`](https://github.com/ethereum/devp2p/blob/master/caps/eth.md#getpooledtransactions-0x09):
349    ///
350    /// The transactions must be in same order as in the request, but it is OK to skip transactions
351    /// which are not available.
352    ///
353    /// If the transaction is a blob transaction, the sidecar will be included.
354    ///
355    /// Consumer: P2P
356    fn get_pooled_transaction_elements(
357        &self,
358        tx_hashes: Vec<TxHash>,
359        limit: GetPooledTransactionLimit,
360    ) -> Vec<<Self::Transaction as PoolTransaction>::Pooled>;
361
362    /// Extends the given vector with pooled transactions for the given hashes that are allowed to
363    /// be propagated.
364    ///
365    /// This adheres to the expected behavior of [`Self::get_pooled_transaction_elements`].
366    ///
367    /// Consumer: P2P
368    fn append_pooled_transaction_elements(
369        &self,
370        tx_hashes: &[TxHash],
371        limit: GetPooledTransactionLimit,
372        out: &mut Vec<<Self::Transaction as PoolTransaction>::Pooled>,
373    ) {
374        out.extend(self.get_pooled_transaction_elements(tx_hashes.to_vec(), limit));
375    }
376
377    /// Returns the pooled transaction variant for the given transaction hash.
378    ///
379    /// This adheres to the expected behavior of
380    /// [`GetPooledTransactions`](https://github.com/ethereum/devp2p/blob/master/caps/eth.md#getpooledtransactions-0x09):
381    ///
382    /// If the transaction is a blob transaction, the sidecar will be included.
383    ///
384    /// It is expected that this variant represents the valid p2p format for full transactions.
385    /// E.g. for EIP-4844 transactions this is the consensus transaction format with the blob
386    /// sidecar.
387    ///
388    /// Consumer: P2P
389    fn get_pooled_transaction_element(
390        &self,
391        tx_hash: TxHash,
392    ) -> Option<Recovered<<Self::Transaction as PoolTransaction>::Pooled>>;
393
394    /// Returns an iterator that yields transactions that are ready for block production.
395    ///
396    /// Consumer: Block production
397    fn best_transactions(
398        &self,
399    ) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>>;
400
401    /// Returns an iterator that yields transactions that are ready for block production with the
402    /// given base fee and optional blob fee attributes.
403    ///
404    /// Consumer: Block production
405    fn best_transactions_with_attributes(
406        &self,
407        best_transactions_attributes: BestTransactionsAttributes,
408    ) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>>;
409
410    /// Returns all transactions that can be included in the next block.
411    ///
412    /// This is primarily used for the `txpool_` RPC namespace:
413    /// <https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool> which distinguishes
414    /// between `pending` and `queued` transactions, where `pending` are transactions ready for
415    /// inclusion in the next block and `queued` are transactions that are ready for inclusion in
416    /// future blocks.
417    ///
418    /// Consumer: RPC
419    fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
420
421    /// Returns a pending transaction if it exists and is ready for immediate execution
422    /// (i.e., has the lowest nonce among the sender's pending transactions).
423    fn get_pending_transaction_by_sender_and_nonce(
424        &self,
425        sender: Address,
426        nonce: u64,
427    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
428        self.best_transactions().find(|tx| tx.sender() == sender && tx.nonce() == nonce)
429    }
430
431    /// Returns first `max` transactions that can be included in the next block.
432    /// See <https://github.com/paradigmxyz/reth/issues/12767#issuecomment-2493223579>
433    ///
434    /// Consumer: Block production
435    fn pending_transactions_max(
436        &self,
437        max: usize,
438    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
439
440    /// Returns all transactions that can be included in _future_ blocks.
441    ///
442    /// This and [`Self::pending_transactions`] are mutually exclusive.
443    ///
444    /// Consumer: RPC
445    fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
446
447    /// Returns the number of transactions that are ready for inclusion in the next block and the
448    /// number of transactions that are ready for inclusion in future blocks: `(pending, queued)`.
449    fn pending_and_queued_txn_count(&self) -> (usize, usize);
450
451    /// Returns all transactions that are currently in the pool grouped by whether they are ready
452    /// for inclusion in the next block or not.
453    ///
454    /// This is primarily used for the `txpool_` namespace: <https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool>
455    ///
456    /// Consumer: RPC
457    fn all_transactions(&self) -> AllPoolTransactions<Self::Transaction>;
458
459    /// Returns the _hashes_ of all transactions regardless of whether they can be propagated or
460    /// not.
461    ///
462    /// Unlike [`Self::pooled_transaction_hashes`] this doesn't consider whether the transaction can
463    /// be propagated or not.
464    ///
465    /// Note: This returns a `Vec` but should guarantee that all hashes are unique.
466    ///
467    /// Consumer: Utility
468    fn all_transaction_hashes(&self) -> Vec<TxHash>;
469
470    /// Removes a single transaction corresponding to the given hash.
471    ///
472    /// Note: This removes the transaction as if it got discarded (_not_ mined).
473    ///
474    /// Returns the removed transaction if it was found in the pool.
475    ///
476    /// Consumer: Utility
477    fn remove_transaction(
478        &self,
479        hash: TxHash,
480    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
481        self.remove_transactions(vec![hash]).pop()
482    }
483
484    /// Removes all transactions corresponding to the given hashes.
485    ///
486    /// Note: This removes the transactions as if they got discarded (_not_ mined).
487    ///
488    /// Consumer: Utility
489    fn remove_transactions(
490        &self,
491        hashes: Vec<TxHash>,
492    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
493
494    /// Removes all transactions corresponding to the given hashes.
495    ///
496    /// Also removes all _dependent_ transactions.
497    ///
498    /// Consumer: Utility
499    fn remove_transactions_and_descendants(
500        &self,
501        hashes: Vec<TxHash>,
502    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
503
504    /// Removes all transactions from the given sender
505    ///
506    /// Consumer: Utility
507    fn remove_transactions_by_sender(
508        &self,
509        sender: Address,
510    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
511
512    /// Prunes a single transaction from the pool.
513    ///
514    /// This is similar to [`Self::remove_transaction`] but treats the transaction as _mined_
515    /// rather than discarded. The key difference is that pruning does **not** park descendant
516    /// transactions: their nonce requirements are considered satisfied, so they remain in whatever
517    /// sub-pool they currently occupy and can be included in the next block.
518    ///
519    /// In contrast, [`Self::remove_transaction`] treats the removal as a discard, which
520    /// introduces a nonce gap and moves all descendant transactions to the queued (parked)
521    /// sub-pool.
522    ///
523    /// Returns the pruned transaction if it existed in the pool.
524    ///
525    /// Consumer: Utility
526    fn prune_transaction(
527        &self,
528        hash: TxHash,
529    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
530        self.prune_transactions(vec![hash]).pop()
531    }
532
533    /// Prunes all transactions corresponding to the given hashes from the pool.
534    ///
535    /// This behaves like [`Self::prune_transaction`] but for multiple transactions at once.
536    /// Each transaction is removed as if it was mined: descendant transactions are **not** parked
537    /// and their nonce requirements are considered satisfied.
538    ///
539    /// This is useful for scenarios like Flashblocks where transactions are committed across
540    /// multiple partial blocks without a canonical state update: previously committed transactions
541    /// can be pruned so that the best-transactions iterator yields their descendants in the
542    /// correct priority order.
543    ///
544    /// Consumer: Utility
545    fn prune_transactions(
546        &self,
547        hashes: Vec<TxHash>,
548    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
549
550    /// Retains only those hashes that are unknown to the pool.
551    /// In other words, removes all transactions from the given set that are currently present in
552    /// the pool. Returns hashes already known to the pool.
553    ///
554    /// Consumer: P2P
555    fn retain_unknown<A>(&self, announcement: &mut A)
556    where
557        A: HandleMempoolData;
558
559    /// Returns if the transaction for the given hash is already included in this pool.
560    fn contains(&self, tx_hash: &TxHash) -> bool {
561        self.get(tx_hash).is_some()
562    }
563
564    /// Returns the transaction for the given hash.
565    fn get(&self, tx_hash: &TxHash) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>>;
566
567    /// Returns all transactions objects for the given hashes.
568    ///
569    /// Caution: This in case of blob transactions, this does not include the sidecar.
570    fn get_all(&self, txs: Vec<TxHash>) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
571
572    /// Notify the pool about transactions that are propagated to peers.
573    ///
574    /// Consumer: P2P
575    fn on_propagated(&self, txs: PropagatedTransactions);
576
577    /// Returns all transactions sent by a given user
578    fn get_transactions_by_sender(
579        &self,
580        sender: Address,
581    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
582
583    /// Returns all pending transactions filtered by predicate
584    fn get_pending_transactions_with_predicate(
585        &self,
586        predicate: impl FnMut(&ValidPoolTransaction<Self::Transaction>) -> bool,
587    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
588
589    /// Returns all pending transactions sent by a given user
590    fn get_pending_transactions_by_sender(
591        &self,
592        sender: Address,
593    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
594
595    /// Returns all queued transactions sent by a given user
596    fn get_queued_transactions_by_sender(
597        &self,
598        sender: Address,
599    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
600
601    /// Returns the highest transaction sent by a given user
602    fn get_highest_transaction_by_sender(
603        &self,
604        sender: Address,
605    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>>;
606
607    /// Returns the transaction with the highest nonce that is executable given the on chain nonce.
608    /// In other words the highest non nonce gapped transaction.
609    ///
610    /// Note: The next pending pooled transaction must have the on chain nonce.
611    ///
612    /// For example, for a given on chain nonce of `5`, the next transaction must have that nonce.
613    /// If the pool contains txs `[5,6,7]` this returns tx `7`.
614    /// If the pool contains txs `[6,7]` this returns `None` because the next valid nonce (5) is
615    /// missing, which means txs `[6,7]` are nonce gapped.
616    fn get_highest_consecutive_transaction_by_sender(
617        &self,
618        sender: Address,
619        on_chain_nonce: u64,
620    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>>;
621
622    /// Returns a transaction sent by a given user and a nonce
623    fn get_transaction_by_sender_and_nonce(
624        &self,
625        sender: Address,
626        nonce: u64,
627    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>>;
628
629    /// Returns all transactions that where submitted with the given [`TransactionOrigin`]
630    fn get_transactions_by_origin(
631        &self,
632        origin: TransactionOrigin,
633    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
634
635    /// Returns all pending transactions filtered by [`TransactionOrigin`]
636    fn get_pending_transactions_by_origin(
637        &self,
638        origin: TransactionOrigin,
639    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
640
641    /// Returns all transactions that where submitted as [`TransactionOrigin::Local`]
642    fn get_local_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
643        self.get_transactions_by_origin(TransactionOrigin::Local)
644    }
645
646    /// Returns all transactions that where submitted as [`TransactionOrigin::Private`]
647    fn get_private_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
648        self.get_transactions_by_origin(TransactionOrigin::Private)
649    }
650
651    /// Returns all transactions that where submitted as [`TransactionOrigin::External`]
652    fn get_external_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
653        self.get_transactions_by_origin(TransactionOrigin::External)
654    }
655
656    /// Returns all pending transactions that where submitted as [`TransactionOrigin::Local`]
657    fn get_local_pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
658        self.get_pending_transactions_by_origin(TransactionOrigin::Local)
659    }
660
661    /// Returns all pending transactions that where submitted as [`TransactionOrigin::Private`]
662    fn get_private_pending_transactions(
663        &self,
664    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
665        self.get_pending_transactions_by_origin(TransactionOrigin::Private)
666    }
667
668    /// Returns all pending transactions that where submitted as [`TransactionOrigin::External`]
669    fn get_external_pending_transactions(
670        &self,
671    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
672        self.get_pending_transactions_by_origin(TransactionOrigin::External)
673    }
674
675    /// Returns a set of all senders of transactions in the pool
676    fn unique_senders(&self) -> AddressSet;
677
678    /// Returns the [`BlobTransactionSidecarVariant`] for the given transaction hash if it exists in
679    /// the blob store.
680    fn get_blob(
681        &self,
682        tx_hash: TxHash,
683    ) -> Result<Option<Arc<BlobTransactionSidecarVariant>>, BlobStoreError>;
684
685    /// Returns all [`BlobTransactionSidecarVariant`] for the given transaction hashes if they
686    /// exists in the blob store.
687    ///
688    /// This only returns the blobs that were found in the store.
689    /// If there's no blob it will not be returned.
690    fn get_all_blobs(
691        &self,
692        tx_hashes: Vec<TxHash>,
693    ) -> Result<Vec<(TxHash, Arc<BlobTransactionSidecarVariant>)>, BlobStoreError>;
694
695    /// Returns the exact [`BlobTransactionSidecarVariant`] for the given transaction hashes in the
696    /// order they were requested.
697    ///
698    /// Returns an error if any of the blobs are not found in the blob store.
699    fn get_all_blobs_exact(
700        &self,
701        tx_hashes: Vec<TxHash>,
702    ) -> Result<Vec<Arc<BlobTransactionSidecarVariant>>, BlobStoreError>;
703
704    /// Return the [`BlobAndProofV1`]s for a list of blob versioned hashes.
705    fn get_blobs_for_versioned_hashes_v1(
706        &self,
707        versioned_hashes: &[B256],
708    ) -> Result<Vec<Option<BlobAndProofV1>>, BlobStoreError>;
709
710    /// Return the [`BlobAndProofV2`]s for a list of blob versioned hashes.
711    /// Blobs and proofs are returned only if they are present for _all_ of the requested versioned
712    /// hashes.
713    fn get_blobs_for_versioned_hashes_v2(
714        &self,
715        versioned_hashes: &[B256],
716    ) -> Result<Option<Vec<BlobAndProofV2>>, BlobStoreError>;
717
718    /// Return the [`BlobAndProofV2`]s for a list of blob versioned hashes.
719    ///
720    /// The response is always the same length as the request. Missing or older-version blobs are
721    /// returned as `None` elements.
722    fn get_blobs_for_versioned_hashes_v3(
723        &self,
724        versioned_hashes: &[B256],
725    ) -> Result<Vec<Option<BlobAndProofV2>>, BlobStoreError>;
726
727    /// Return the [`BlobCellsAndProofsV1`]s for a list of blob versioned hashes and requested cell
728    /// indices.
729    ///
730    /// The response is always the same length as the request. Missing or older-version blobs are
731    /// returned as `None` elements.
732    fn get_blobs_for_versioned_hashes_v4(
733        &self,
734        versioned_hashes: &[B256],
735        indices_bitarray: B128,
736    ) -> Result<Vec<Option<BlobCellsAndProofsV1>>, BlobStoreError>;
737}
738
739/// Extension for [`TransactionPool`] trait that allows to set the current block info.
740#[auto_impl::auto_impl(&, Arc)]
741pub trait TransactionPoolExt: TransactionPool {
742    /// The block type used for chain tip updates.
743    type Block: Block;
744
745    /// Sets the current block info for the pool.
746    fn set_block_info(&self, info: BlockInfo);
747
748    /// Event listener for when the pool needs to be updated.
749    ///
750    /// Implementers need to update the pool accordingly:
751    ///
752    /// ## Fee changes
753    ///
754    /// The [`CanonicalStateUpdate`] includes the base and blob fee of the pending block, which
755    /// affects the dynamic fee requirement of pending transactions in the pool.
756    ///
757    /// ## EIP-4844 Blob transactions
758    ///
759    /// Mined blob transactions need to be removed from the pool, but from the pool only. The blob
760    /// sidecar must not be removed from the blob store. Only after a blob transaction is
761    /// finalized, its sidecar is removed from the blob store. This ensures that in case of a reorg,
762    /// the sidecar is still available.
763    fn on_canonical_state_change(&self, update: CanonicalStateUpdate<'_, Self::Block>);
764
765    /// Updates the accounts in the pool
766    fn update_accounts(&self, accounts: Vec<ChangedAccount>);
767
768    /// Deletes the blob sidecar for the given transaction from the blob store
769    fn delete_blob(&self, tx: B256);
770
771    /// Deletes multiple blob sidecars from the blob store
772    fn delete_blobs(&self, txs: Vec<B256>);
773
774    /// Maintenance function to cleanup blobs that are no longer needed.
775    fn cleanup_blobs(&self);
776}
777
778/// A Helper type that bundles all transactions in the pool.
779#[derive(Debug, Clone)]
780pub struct AllPoolTransactions<T: PoolTransaction> {
781    /// Transactions that are ready for inclusion in the next block.
782    pub pending: Vec<Arc<ValidPoolTransaction<T>>>,
783    /// Transactions that are ready for inclusion in _future_ blocks, but are currently parked,
784    /// because they depend on other transactions that are not yet included in the pool (nonce gap)
785    /// or otherwise blocked.
786    pub queued: Vec<Arc<ValidPoolTransaction<T>>>,
787}
788
789// === impl AllPoolTransactions ===
790
791impl<T: PoolTransaction> AllPoolTransactions<T> {
792    /// Returns the combined number of all transactions.
793    pub const fn count(&self) -> usize {
794        self.pending.len() + self.queued.len()
795    }
796
797    /// Returns an iterator over all pending [`Recovered`] transactions.
798    pub fn pending_recovered(&self) -> impl Iterator<Item = Recovered<T::Consensus>> + '_ {
799        self.pending.iter().map(|tx| tx.to_consensus())
800    }
801
802    /// Returns an iterator over all queued [`Recovered`] transactions.
803    pub fn queued_recovered(&self) -> impl Iterator<Item = Recovered<T::Consensus>> + '_ {
804        self.queued.iter().map(|tx| tx.to_consensus())
805    }
806
807    /// Returns an iterator over all transactions, both pending and queued.
808    pub fn all(&self) -> impl Iterator<Item = Recovered<T::Consensus>> + '_ {
809        self.pending.iter().chain(self.queued.iter()).map(|tx| tx.to_consensus())
810    }
811}
812
813impl<T: PoolTransaction> Default for AllPoolTransactions<T> {
814    fn default() -> Self {
815        Self { pending: Default::default(), queued: Default::default() }
816    }
817}
818
819impl<T: PoolTransaction> IntoIterator for AllPoolTransactions<T> {
820    type Item = Arc<ValidPoolTransaction<T>>;
821    type IntoIter = std::iter::Chain<
822        std::vec::IntoIter<Arc<ValidPoolTransaction<T>>>,
823        std::vec::IntoIter<Arc<ValidPoolTransaction<T>>>,
824    >;
825
826    fn into_iter(self) -> Self::IntoIter {
827        self.pending.into_iter().chain(self.queued)
828    }
829}
830
831/// Represents transactions that were propagated over the network.
832#[derive(Debug, Clone, Eq, PartialEq, Default)]
833pub struct PropagatedTransactions(pub HashMap<TxHash, Vec<PropagateKind>>);
834
835impl PropagatedTransactions {
836    /// Records a propagation of a transaction to a peer.
837    pub fn record(&mut self, hash: TxHash, kind: PropagateKind) {
838        self.0.entry(hash).or_default().push(kind);
839    }
840
841    /// Returns the number of distinct transactions that were propagated.
842    pub fn len(&self) -> usize {
843        self.0.len()
844    }
845
846    /// Returns true if no transactions were propagated.
847    pub fn is_empty(&self) -> bool {
848        self.0.is_empty()
849    }
850
851    /// Returns the propagation info for a specific transaction.
852    pub fn get(&self, hash: &TxHash) -> Option<&[PropagateKind]> {
853        self.0.get(hash).map(Vec::as_slice)
854    }
855}
856
857impl IntoIterator for PropagatedTransactions {
858    type Item = (TxHash, Vec<PropagateKind>);
859    type IntoIter = std::collections::hash_map::IntoIter<TxHash, Vec<PropagateKind>>;
860
861    fn into_iter(self) -> Self::IntoIter {
862        self.0.into_iter()
863    }
864}
865
866/// Represents how a transaction was propagated over the network.
867#[derive(Debug, Copy, Clone, Eq, PartialEq)]
868#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
869pub enum PropagateKind {
870    /// The full transaction object was sent to the peer.
871    ///
872    /// This is equivalent to the `Transaction` message
873    Full(PeerId),
874    /// Only the Hash was propagated to the peer.
875    Hash(PeerId),
876}
877
878// === impl PropagateKind ===
879
880impl PropagateKind {
881    /// Returns the peer the transaction was sent to
882    pub const fn peer(&self) -> &PeerId {
883        match self {
884            Self::Full(peer) | Self::Hash(peer) => peer,
885        }
886    }
887
888    /// Returns true if the transaction was sent as a full transaction
889    pub const fn is_full(&self) -> bool {
890        matches!(self, Self::Full(_))
891    }
892
893    /// Returns true if the transaction was sent as a hash
894    pub const fn is_hash(&self) -> bool {
895        matches!(self, Self::Hash(_))
896    }
897}
898
899impl From<PropagateKind> for PeerId {
900    fn from(value: PropagateKind) -> Self {
901        match value {
902            PropagateKind::Full(peer) | PropagateKind::Hash(peer) => peer,
903        }
904    }
905}
906
907/// This type represents a new blob sidecar that has been stored in the transaction pool's
908/// blobstore; it includes the `TransactionHash` of the blob transaction along with the assoc.
909/// sidecar (blobs, commitments, proofs)
910#[derive(Debug, Clone)]
911pub struct NewBlobSidecar {
912    /// hash of the EIP-4844 transaction.
913    pub tx_hash: TxHash,
914    /// the blob transaction sidecar.
915    pub sidecar: Arc<BlobTransactionSidecarVariant>,
916}
917
918/// Where the transaction originates from.
919///
920/// Depending on where the transaction was picked up, it affects how the transaction is handled
921/// internally, e.g. limits for simultaneous transaction of one sender.
922#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, Deserialize, Serialize)]
923pub enum TransactionOrigin {
924    /// Transaction is coming from a local source.
925    #[default]
926    Local,
927    /// Transaction has been received externally.
928    ///
929    /// This is usually considered an "untrusted" source, for example received from another in the
930    /// network.
931    External,
932    /// Transaction is originated locally and is intended to remain private.
933    ///
934    /// This type of transaction should not be propagated to the network. It's meant for
935    /// private usage within the local node only.
936    Private,
937}
938
939// === impl TransactionOrigin ===
940
941impl TransactionOrigin {
942    /// Whether the transaction originates from a local source.
943    pub const fn is_local(&self) -> bool {
944        matches!(self, Self::Local)
945    }
946
947    /// Whether the transaction originates from an external source.
948    pub const fn is_external(&self) -> bool {
949        matches!(self, Self::External)
950    }
951    /// Whether the transaction originates from a private source.
952    pub const fn is_private(&self) -> bool {
953        matches!(self, Self::Private)
954    }
955}
956
957/// Represents the kind of update to the canonical state.
958#[derive(Debug, Clone, Copy, PartialEq, Eq)]
959pub enum PoolUpdateKind {
960    /// The update was due to a block commit.
961    Commit,
962    /// The update was due to a reorganization.
963    Reorg,
964}
965
966/// Represents changes after a new canonical block or range of canonical blocks was added to the
967/// chain.
968///
969/// It is expected that this is only used if the added blocks are canonical to the pool's last known
970/// block hash. In other words, the first added block of the range must be the child of the last
971/// known block hash.
972///
973/// This is used to update the pool state accordingly.
974#[derive(Clone, Debug)]
975pub struct CanonicalStateUpdate<'a, B: Block> {
976    /// Hash of the tip block.
977    pub new_tip: &'a SealedBlock<B>,
978    /// EIP-1559 Base fee of the _next_ (pending) block
979    ///
980    /// The base fee of a block depends on the utilization of the last block and its base fee.
981    pub pending_block_base_fee: u64,
982    /// EIP-4844 blob fee of the _next_ (pending) block
983    ///
984    /// Only after Cancun
985    pub pending_block_blob_fee: Option<u128>,
986    /// A set of changed accounts across a range of blocks.
987    pub changed_accounts: Vec<ChangedAccount>,
988    /// All mined transactions in the block range.
989    pub mined_transactions: Vec<B256>,
990    /// The kind of update to the canonical state.
991    pub update_kind: PoolUpdateKind,
992}
993
994impl<B> CanonicalStateUpdate<'_, B>
995where
996    B: Block,
997{
998    /// Returns the number of the tip block.
999    pub fn number(&self) -> u64 {
1000        self.new_tip.number()
1001    }
1002
1003    /// Returns the hash of the tip block.
1004    pub fn hash(&self) -> B256 {
1005        self.new_tip.hash()
1006    }
1007
1008    /// Timestamp of the latest chain update
1009    pub fn timestamp(&self) -> u64 {
1010        self.new_tip.timestamp()
1011    }
1012
1013    /// Returns the block info for the tip block.
1014    pub fn block_info(&self) -> BlockInfo {
1015        BlockInfo {
1016            block_gas_limit: self.new_tip.gas_limit(),
1017            last_seen_block_hash: self.hash(),
1018            last_seen_block_number: self.number(),
1019            pending_basefee: self.pending_block_base_fee,
1020            pending_blob_fee: self.pending_block_blob_fee,
1021        }
1022    }
1023}
1024
1025impl<B> fmt::Display for CanonicalStateUpdate<'_, B>
1026where
1027    B: Block,
1028{
1029    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1030        f.debug_struct("CanonicalStateUpdate")
1031            .field("hash", &self.hash())
1032            .field("number", &self.number())
1033            .field("pending_block_base_fee", &self.pending_block_base_fee)
1034            .field("pending_block_blob_fee", &self.pending_block_blob_fee)
1035            .field("changed_accounts", &self.changed_accounts.len())
1036            .field("mined_transactions", &self.mined_transactions.len())
1037            .finish()
1038    }
1039}
1040
1041/// Alias to restrict the [`BestTransactions`] items to the pool's transaction type.
1042pub type BestTransactionsFor<Pool> = Box<
1043    dyn BestTransactions<Item = Arc<ValidPoolTransaction<<Pool as TransactionPool>::Transaction>>>,
1044>;
1045
1046/// An `Iterator` that only returns transactions that are ready to be executed.
1047///
1048/// This makes no assumptions about the order of the transactions, but expects that _all_
1049/// transactions are valid (no nonce gaps.) for the tracked state of the pool.
1050///
1051/// Note: this iterator will always return the best transaction that it currently knows.
1052/// There is no guarantee transactions will be returned sequentially in decreasing
1053/// priority order.
1054pub trait BestTransactions: Iterator + Send {
1055    /// Mark the transaction as invalid.
1056    ///
1057    /// Implementers must ensure all subsequent transaction _don't_ depend on this transaction.
1058    /// In other words, this must remove the given transaction _and_ drain all transaction that
1059    /// depend on it.
1060    fn mark_invalid(&mut self, transaction: &Self::Item, kind: InvalidPoolTransactionError);
1061
1062    /// An iterator may be able to receive additional pending transactions that weren't present it
1063    /// the pool when it was created.
1064    ///
1065    /// This ensures that iterator will return the best transaction that it currently knows and not
1066    /// listen to pool updates.
1067    fn no_updates(&mut self);
1068
1069    /// Convenience function for [`Self::no_updates`] that returns the iterator again.
1070    fn without_updates(mut self) -> Self
1071    where
1072        Self: Sized,
1073    {
1074        self.no_updates();
1075        self
1076    }
1077
1078    /// Skip all blob transactions.
1079    ///
1080    /// There's only limited blob space available in a block, once exhausted, EIP-4844 transactions
1081    /// can no longer be included.
1082    ///
1083    /// If called then the iterator will no longer yield blob transactions.
1084    ///
1085    /// Note: this will also exclude any transactions that depend on blob transactions.
1086    fn skip_blobs(&mut self) {
1087        self.set_skip_blobs(true);
1088    }
1089
1090    /// Controls whether the iterator skips blob transactions or not.
1091    ///
1092    /// If set to true, no blob transactions will be returned.
1093    fn set_skip_blobs(&mut self, skip_blobs: bool);
1094
1095    /// Convenience function for [`Self::skip_blobs`] that returns the iterator again.
1096    fn without_blobs(mut self) -> Self
1097    where
1098        Self: Sized,
1099    {
1100        self.skip_blobs();
1101        self
1102    }
1103
1104    /// Creates an iterator which uses a closure to determine whether a transaction should be
1105    /// returned by the iterator.
1106    ///
1107    /// All items the closure returns false for are marked as invalid via [`Self::mark_invalid`] and
1108    /// descendant transactions will be skipped.
1109    fn filter_transactions<P>(self, predicate: P) -> BestTransactionFilter<Self, P>
1110    where
1111        P: FnMut(&Self::Item) -> bool,
1112        Self: Sized,
1113    {
1114        BestTransactionFilter::new(self, predicate)
1115    }
1116}
1117
1118impl<T> BestTransactions for Box<T>
1119where
1120    T: BestTransactions + ?Sized,
1121{
1122    fn mark_invalid(&mut self, transaction: &Self::Item, kind: InvalidPoolTransactionError) {
1123        (**self).mark_invalid(transaction, kind)
1124    }
1125
1126    fn no_updates(&mut self) {
1127        (**self).no_updates();
1128    }
1129
1130    fn skip_blobs(&mut self) {
1131        (**self).skip_blobs();
1132    }
1133
1134    fn set_skip_blobs(&mut self, skip_blobs: bool) {
1135        (**self).set_skip_blobs(skip_blobs);
1136    }
1137}
1138
1139/// A no-op implementation that yields no transactions.
1140impl<T> BestTransactions for std::iter::Empty<T> {
1141    fn mark_invalid(&mut self, _tx: &T, _kind: InvalidPoolTransactionError) {}
1142
1143    fn no_updates(&mut self) {}
1144
1145    fn skip_blobs(&mut self) {}
1146
1147    fn set_skip_blobs(&mut self, _skip_blobs: bool) {}
1148}
1149
1150/// A filter that allows to check if a transaction satisfies a set of conditions
1151pub trait TransactionFilter {
1152    /// The type of the transaction to check.
1153    type Transaction;
1154
1155    /// Returns true if the transaction satisfies the conditions.
1156    fn is_valid(&self, transaction: &Self::Transaction) -> bool;
1157}
1158
1159/// A no-op implementation of [`TransactionFilter`] which
1160/// marks all transactions as valid.
1161#[derive(Debug, Clone)]
1162pub struct NoopTransactionFilter<T>(std::marker::PhantomData<T>);
1163
1164// We can't derive Default because this forces T to be
1165// Default as well, which isn't necessary.
1166impl<T> Default for NoopTransactionFilter<T> {
1167    fn default() -> Self {
1168        Self(std::marker::PhantomData)
1169    }
1170}
1171
1172impl<T> TransactionFilter for NoopTransactionFilter<T> {
1173    type Transaction = T;
1174
1175    fn is_valid(&self, _transaction: &Self::Transaction) -> bool {
1176        true
1177    }
1178}
1179
1180/// A Helper type that bundles the best transactions attributes together.
1181#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1182pub struct BestTransactionsAttributes {
1183    /// The base fee attribute for best transactions.
1184    pub basefee: u64,
1185    /// The blob fee attribute for best transactions.
1186    pub blob_fee: Option<u64>,
1187}
1188
1189// === impl BestTransactionsAttributes ===
1190
1191impl BestTransactionsAttributes {
1192    /// Creates a new `BestTransactionsAttributes` with the given basefee and blob fee.
1193    pub const fn new(basefee: u64, blob_fee: Option<u64>) -> Self {
1194        Self { basefee, blob_fee }
1195    }
1196
1197    /// Creates a new `BestTransactionsAttributes` with the given basefee.
1198    pub const fn base_fee(basefee: u64) -> Self {
1199        Self::new(basefee, None)
1200    }
1201
1202    /// Sets the given blob fee.
1203    pub const fn with_blob_fee(mut self, blob_fee: u64) -> Self {
1204        self.blob_fee = Some(blob_fee);
1205        self
1206    }
1207}
1208
1209/// Trait for transaction types stored in the transaction pool.
1210///
1211/// This trait represents the actual transaction object stored in the mempool, which includes not
1212/// only the transaction data itself but also additional metadata needed for efficient pool
1213/// operations. Implementations typically cache values that are frequently accessed during
1214/// transaction ordering, validation, and eviction.
1215///
1216/// ## Key Responsibilities
1217///
1218/// 1. **Metadata Caching**: Store computed values like address, cost and encoded size
1219/// 2. **Representation Conversion**: Handle conversions between consensus and pooled
1220///    representations
1221/// 3. **Validation Support**: Provide methods for pool-specific validation rules
1222///
1223/// ## Cached Metadata
1224///
1225/// Implementations should cache frequently accessed values to avoid recomputation:
1226/// - **Address**: Recovered sender address of the transaction
1227/// - **Cost**: Max amount spendable (gas × price + value + blob costs)
1228/// - **Size**: RLP encoded length for mempool size limits
1229///
1230/// See [`EthPooledTransaction`] for a reference implementation.
1231///
1232/// ## Transaction Representations
1233///
1234/// This trait abstracts over the different representations a transaction can have:
1235///
1236/// 1. **Consensus representation** (`Consensus` associated type): The canonical form included in
1237///    blocks
1238///    - Compact representation without networking metadata
1239///    - For EIP-4844: includes only blob hashes, not the actual blobs
1240///    - Used for block execution and state transitions
1241///
1242/// 2. **Pooled representation** (`Pooled` associated type): The form used for network propagation
1243///    - May include additional data for validation
1244///    - For EIP-4844: includes full blob sidecars (blobs, commitments, proofs)
1245///    - Used for mempool validation and p2p gossiping
1246///
1247/// ## Why Two Representations?
1248///
1249/// This distinction is necessary because:
1250///
1251/// - **EIP-4844 blob transactions**: Require large blob sidecars for validation that would bloat
1252///   blocks if included. Only blob hashes are stored on-chain.
1253///
1254/// - **Network efficiency**: Blob transactions are not broadcast to all peers automatically but
1255///   must be explicitly requested to reduce bandwidth usage.
1256///
1257/// - **Special transactions**: Some transactions (like OP deposit transactions) exist only in
1258///   consensus format and are never in the mempool.
1259///
1260/// ## Conversion Rules
1261///
1262/// - `Consensus` → `Pooled`: May fail for transactions that cannot be pooled (e.g., OP deposit
1263///   transactions, blob transactions without sidecars)
1264/// - `Pooled` → `Consensus`: Always succeeds (pooled is a superset)
1265pub trait PoolTransaction:
1266    alloy_consensus::Transaction + InMemorySize + Debug + Send + Sync + Clone
1267{
1268    /// Associated error type for the `try_from_consensus` method.
1269    type TryFromConsensusError: fmt::Display;
1270
1271    /// Associated type representing the raw consensus variant of the transaction.
1272    type Consensus: SignedTransaction + From<Self::Pooled>;
1273
1274    /// Associated type representing the recovered pooled variant of the transaction.
1275    type Pooled: TryFrom<Self::Consensus, Error = Self::TryFromConsensusError> + SignedTransaction;
1276
1277    /// Define a method to convert from the `Consensus` type to `Self`
1278    ///
1279    /// This conversion may fail for transactions that are valid for inclusion in blocks
1280    /// but cannot exist in the transaction pool. Examples include:
1281    ///
1282    /// - **OP Deposit transactions**: These are special system transactions that are directly
1283    ///   included in blocks by the sequencer/validator and never enter the mempool
1284    /// - **Blob transactions without sidecars**: After being included in a block, the sidecar data
1285    ///   is pruned, making the consensus transaction unpoolable
1286    fn try_from_consensus(
1287        tx: Recovered<Self::Consensus>,
1288    ) -> Result<Self, Self::TryFromConsensusError> {
1289        let (tx, signer) = tx.into_parts();
1290        Ok(Self::from_pooled(Recovered::new_unchecked(tx.try_into()?, signer)))
1291    }
1292
1293    /// Clone the transaction into a consensus variant.
1294    ///
1295    /// This method is preferred when the [`PoolTransaction`] already wraps the consensus variant.
1296    fn clone_into_consensus(&self) -> Recovered<Self::Consensus> {
1297        self.clone().into_consensus()
1298    }
1299
1300    /// Returns a reference to the consensus transaction with the recovered sender.
1301    fn consensus_ref(&self) -> Recovered<&Self::Consensus>;
1302
1303    /// Define a method to convert from the `Self` type to `Consensus`
1304    fn into_consensus(self) -> Recovered<Self::Consensus>;
1305
1306    /// Converts the transaction into consensus format while preserving the EIP-2718 encoded bytes.
1307    /// This is used to optimize transaction execution by reusing cached encoded bytes instead of
1308    /// re-encoding the transaction. The cached bytes are particularly useful in payload building
1309    /// where the same transaction may be executed multiple times.
1310    fn into_consensus_with2718(self) -> WithEncoded<Recovered<Self::Consensus>> {
1311        self.into_consensus().into_encoded()
1312    }
1313
1314    /// Define a method to convert from the `Pooled` type to `Self`
1315    fn from_pooled(pooled: Recovered<Self::Pooled>) -> Self;
1316
1317    /// Tries to convert the `Consensus` type into the `Pooled` type.
1318    fn try_into_pooled(self) -> Result<Recovered<Self::Pooled>, Self::TryFromConsensusError> {
1319        let consensus = self.into_consensus();
1320        let (tx, signer) = consensus.into_parts();
1321        Ok(Recovered::new_unchecked(tx.try_into()?, signer))
1322    }
1323
1324    /// Clones the consensus transactions and tries to convert the `Consensus` type into the
1325    /// `Pooled` type.
1326    fn clone_into_pooled(&self) -> Result<Recovered<Self::Pooled>, Self::TryFromConsensusError> {
1327        let consensus = self.clone_into_consensus();
1328        let (tx, signer) = consensus.into_parts();
1329        Ok(Recovered::new_unchecked(tx.try_into()?, signer))
1330    }
1331
1332    /// Converts the `Pooled` type into the `Consensus` type.
1333    fn pooled_into_consensus(tx: Self::Pooled) -> Self::Consensus {
1334        tx.into()
1335    }
1336
1337    /// Hash of the transaction.
1338    fn hash(&self) -> &TxHash;
1339
1340    /// The Sender of the transaction.
1341    fn sender(&self) -> Address;
1342
1343    /// Reference to the Sender of the transaction.
1344    fn sender_ref(&self) -> &Address;
1345
1346    /// Returns the cost that this transaction is allowed to consume:
1347    ///
1348    /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`.
1349    /// For legacy transactions: `gas_price * gas_limit + tx_value`.
1350    /// For EIP-4844 blob transactions: `max_fee_per_gas * gas_limit + tx_value +
1351    /// max_blob_fee_per_gas * blob_gas_used`.
1352    fn cost(&self) -> &U256;
1353
1354    /// Returns the length of the rlp encoded transaction object
1355    ///
1356    /// Note: Implementations should cache this value.
1357    fn encoded_length(&self) -> usize;
1358
1359    /// Ensures that the transaction's code size does not exceed the provided `max_init_code_size`.
1360    ///
1361    /// This is specifically relevant for contract creation transactions ([`TxKind::Create`]),
1362    /// where the input data contains the initialization code. If the input code size exceeds
1363    /// the configured limit, an [`InvalidPoolTransactionError::ExceedsMaxInitCodeSize`] error is
1364    /// returned.
1365    fn ensure_max_init_code_size(
1366        &self,
1367        max_init_code_size: usize,
1368    ) -> Result<(), InvalidPoolTransactionError> {
1369        let input_len = self.input().len();
1370        if self.is_create() && input_len > max_init_code_size {
1371            Err(InvalidPoolTransactionError::ExceedsMaxInitCodeSize(input_len, max_init_code_size))
1372        } else {
1373            Ok(())
1374        }
1375    }
1376
1377    /// Allows to communicate to the pool that the transaction doesn't require a nonce check.
1378    fn requires_nonce_check(&self) -> bool {
1379        true
1380    }
1381}
1382
1383/// Super trait for transactions that can be converted to and from Eth transactions intended for the
1384/// ethereum style pool.
1385///
1386/// This extends the [`PoolTransaction`] trait with additional methods that are specific to the
1387/// Ethereum pool.
1388pub trait EthPoolTransaction: PoolTransaction {
1389    /// Extracts the blob sidecar from the transaction.
1390    fn take_blob(&mut self) -> EthBlobTransactionSidecar;
1391
1392    /// A specialization for the EIP-4844 transaction type.
1393    /// Tries to reattach the blob sidecar to the transaction.
1394    ///
1395    /// This returns an option, but callers should ensure that the transaction is an EIP-4844
1396    /// transaction: [`Typed2718::is_eip4844`].
1397    fn try_into_pooled_eip4844(
1398        self,
1399        sidecar: Arc<BlobTransactionSidecarVariant>,
1400    ) -> Option<Recovered<Self::Pooled>>;
1401
1402    /// Tries to convert the `Consensus` type with a blob sidecar into the `Pooled` type.
1403    ///
1404    /// Returns `None` if passed transaction is not a blob transaction.
1405    fn try_from_eip4844(
1406        tx: Recovered<Self::Consensus>,
1407        sidecar: BlobTransactionSidecarVariant,
1408    ) -> Option<Self>;
1409
1410    /// Validates the blob sidecar of the transaction with the given settings.
1411    fn validate_blob(
1412        &self,
1413        blob: &BlobTransactionSidecarVariant,
1414        settings: &KzgSettings,
1415    ) -> Result<(), BlobTransactionValidationError>;
1416}
1417
1418/// The default [`PoolTransaction`] for the [Pool](crate::Pool) for Ethereum.
1419///
1420/// This type wraps a consensus transaction with additional cached data that's
1421/// frequently accessed by the pool for transaction ordering and validation:
1422///
1423/// - `cost`: Pre-calculated max cost (gas * price + value + blob costs)
1424/// - `encoded_length`: Cached RLP encoding length for size limits
1425/// - `blob_sidecar`: Blob data state (None/Missing/Present)
1426///
1427/// This avoids recalculating these values repeatedly during pool operations.
1428#[derive(Debug, Clone, PartialEq, Eq)]
1429pub struct EthPooledTransaction<T = TransactionSigned> {
1430    /// `EcRecovered` transaction, the consensus format.
1431    pub transaction: Recovered<T>,
1432
1433    /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`.
1434    /// For legacy transactions: `gas_price * gas_limit + tx_value`.
1435    /// For EIP-4844 blob transactions: `max_fee_per_gas * gas_limit + tx_value +
1436    /// max_blob_fee_per_gas * blob_gas_used`.
1437    pub cost: U256,
1438
1439    /// This is the RLP length of the transaction, computed when the transaction is added to the
1440    /// pool.
1441    pub encoded_length: usize,
1442
1443    /// The blob side car for this transaction
1444    pub blob_sidecar: EthBlobTransactionSidecar,
1445}
1446
1447impl<T: SignedTransaction> EthPooledTransaction<T> {
1448    /// Create new instance of [Self].
1449    ///
1450    /// Caution: In case of blob transactions, this does marks the blob sidecar as
1451    /// [`EthBlobTransactionSidecar::Missing`]
1452    pub fn new(transaction: Recovered<T>, encoded_length: usize) -> Self {
1453        let mut blob_sidecar = EthBlobTransactionSidecar::None;
1454
1455        let gas_cost = U256::from(transaction.max_fee_per_gas())
1456            .saturating_mul(U256::from(transaction.gas_limit()));
1457
1458        let mut cost = gas_cost.saturating_add(transaction.value());
1459
1460        if let (Some(blob_gas_used), Some(max_fee_per_blob_gas)) =
1461            (transaction.blob_gas_used(), transaction.max_fee_per_blob_gas())
1462        {
1463            // Add max blob cost using saturating math to avoid overflow
1464            cost = cost.saturating_add(U256::from(
1465                max_fee_per_blob_gas.saturating_mul(blob_gas_used as u128),
1466            ));
1467
1468            // because the blob sidecar is not included in this transaction variant, mark it as
1469            // missing
1470            blob_sidecar = EthBlobTransactionSidecar::Missing;
1471        }
1472
1473        Self { transaction, cost, encoded_length, blob_sidecar }
1474    }
1475
1476    /// Return the reference to the underlying transaction.
1477    pub const fn transaction(&self) -> &Recovered<T> {
1478        &self.transaction
1479    }
1480}
1481
1482impl PoolTransaction for EthPooledTransaction {
1483    type TryFromConsensusError = ValueError<TransactionSigned>;
1484
1485    type Consensus = TransactionSigned;
1486
1487    type Pooled = PooledTransactionVariant;
1488
1489    fn clone_into_consensus(&self) -> Recovered<Self::Consensus> {
1490        self.transaction().clone()
1491    }
1492
1493    fn consensus_ref(&self) -> Recovered<&Self::Consensus> {
1494        Recovered::new_unchecked(&*self.transaction, self.transaction.signer())
1495    }
1496
1497    fn into_consensus(self) -> Recovered<Self::Consensus> {
1498        self.transaction
1499    }
1500
1501    fn from_pooled(tx: Recovered<Self::Pooled>) -> Self {
1502        let encoded_length = tx.encode_2718_len();
1503        let (tx, signer) = tx.into_parts();
1504        match tx {
1505            PooledTransactionVariant::Eip4844(tx) => {
1506                // include the blob sidecar
1507                let (tx, sig, hash) = tx.into_parts();
1508                let (tx, blob) = tx.into_parts();
1509                let tx = Signed::new_unchecked(tx, sig, hash);
1510                let tx = TransactionSigned::from(tx);
1511                let tx = Recovered::new_unchecked(tx, signer);
1512                let mut pooled = Self::new(tx, encoded_length);
1513                pooled.blob_sidecar = EthBlobTransactionSidecar::Present(blob);
1514                pooled
1515            }
1516            tx => {
1517                // no blob sidecar
1518                let tx = Recovered::new_unchecked(tx.into(), signer);
1519                Self::new(tx, encoded_length)
1520            }
1521        }
1522    }
1523
1524    /// Returns hash of the transaction.
1525    fn hash(&self) -> &TxHash {
1526        self.transaction.tx_hash()
1527    }
1528
1529    /// Returns the Sender of the transaction.
1530    fn sender(&self) -> Address {
1531        self.transaction.signer()
1532    }
1533
1534    /// Returns a reference to the Sender of the transaction.
1535    fn sender_ref(&self) -> &Address {
1536        self.transaction.signer_ref()
1537    }
1538
1539    /// Returns the cost that this transaction is allowed to consume:
1540    ///
1541    /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`.
1542    /// For legacy transactions: `gas_price * gas_limit + tx_value`.
1543    /// For EIP-4844 blob transactions: `max_fee_per_gas * gas_limit + tx_value +
1544    /// max_blob_fee_per_gas * blob_gas_used`.
1545    fn cost(&self) -> &U256 {
1546        &self.cost
1547    }
1548
1549    /// Returns the length of the rlp encoded object
1550    fn encoded_length(&self) -> usize {
1551        self.encoded_length
1552    }
1553}
1554
1555impl<T: Typed2718> Typed2718 for EthPooledTransaction<T> {
1556    fn ty(&self) -> u8 {
1557        self.transaction.ty()
1558    }
1559}
1560
1561impl<T: InMemorySize> InMemorySize for EthPooledTransaction<T> {
1562    fn size(&self) -> usize {
1563        self.transaction.size()
1564    }
1565}
1566
1567impl<T: alloy_consensus::Transaction> alloy_consensus::Transaction for EthPooledTransaction<T> {
1568    fn chain_id(&self) -> Option<alloy_primitives::ChainId> {
1569        self.transaction.chain_id()
1570    }
1571
1572    fn nonce(&self) -> u64 {
1573        self.transaction.nonce()
1574    }
1575
1576    fn gas_limit(&self) -> u64 {
1577        self.transaction.gas_limit()
1578    }
1579
1580    fn gas_price(&self) -> Option<u128> {
1581        self.transaction.gas_price()
1582    }
1583
1584    fn max_fee_per_gas(&self) -> u128 {
1585        self.transaction.max_fee_per_gas()
1586    }
1587
1588    fn max_priority_fee_per_gas(&self) -> Option<u128> {
1589        self.transaction.max_priority_fee_per_gas()
1590    }
1591
1592    fn max_fee_per_blob_gas(&self) -> Option<u128> {
1593        self.transaction.max_fee_per_blob_gas()
1594    }
1595
1596    fn priority_fee_or_price(&self) -> u128 {
1597        self.transaction.priority_fee_or_price()
1598    }
1599
1600    fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
1601        self.transaction.effective_gas_price(base_fee)
1602    }
1603
1604    fn is_dynamic_fee(&self) -> bool {
1605        self.transaction.is_dynamic_fee()
1606    }
1607
1608    fn kind(&self) -> TxKind {
1609        self.transaction.kind()
1610    }
1611
1612    fn is_create(&self) -> bool {
1613        self.transaction.is_create()
1614    }
1615
1616    fn value(&self) -> U256 {
1617        self.transaction.value()
1618    }
1619
1620    fn input(&self) -> &Bytes {
1621        self.transaction.input()
1622    }
1623
1624    fn access_list(&self) -> Option<&AccessList> {
1625        self.transaction.access_list()
1626    }
1627
1628    fn blob_versioned_hashes(&self) -> Option<&[B256]> {
1629        self.transaction.blob_versioned_hashes()
1630    }
1631
1632    fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
1633        self.transaction.authorization_list()
1634    }
1635}
1636
1637impl EthPoolTransaction for EthPooledTransaction {
1638    fn take_blob(&mut self) -> EthBlobTransactionSidecar {
1639        if self.is_eip4844() {
1640            std::mem::replace(&mut self.blob_sidecar, EthBlobTransactionSidecar::Missing)
1641        } else {
1642            EthBlobTransactionSidecar::None
1643        }
1644    }
1645
1646    fn try_into_pooled_eip4844(
1647        self,
1648        sidecar: Arc<BlobTransactionSidecarVariant>,
1649    ) -> Option<Recovered<Self::Pooled>> {
1650        let (signed_transaction, signer) = self.into_consensus().into_parts();
1651        let pooled_transaction =
1652            signed_transaction.try_into_pooled_eip4844(Arc::unwrap_or_clone(sidecar)).ok()?;
1653
1654        Some(Recovered::new_unchecked(pooled_transaction, signer))
1655    }
1656
1657    fn try_from_eip4844(
1658        tx: Recovered<Self::Consensus>,
1659        sidecar: BlobTransactionSidecarVariant,
1660    ) -> Option<Self> {
1661        let (tx, signer) = tx.into_parts();
1662        tx.try_into_pooled_eip4844(sidecar)
1663            .ok()
1664            .map(|tx| tx.with_signer(signer))
1665            .map(Self::from_pooled)
1666    }
1667
1668    fn validate_blob(
1669        &self,
1670        sidecar: &BlobTransactionSidecarVariant,
1671        settings: &KzgSettings,
1672    ) -> Result<(), BlobTransactionValidationError> {
1673        match self.transaction.inner().as_eip4844() {
1674            Some(tx) => tx.tx().validate_blob(sidecar, settings),
1675            _ => Err(BlobTransactionValidationError::NotBlobTransaction(self.ty())),
1676        }
1677    }
1678}
1679
1680/// Represents the blob sidecar of the [`EthPooledTransaction`].
1681///
1682/// EIP-4844 blob transactions require additional data (blobs, commitments, proofs)
1683/// for validation that is not included in the consensus format. This enum tracks
1684/// the sidecar state throughout the transaction's lifecycle in the pool.
1685#[derive(Debug, Clone, PartialEq, Eq)]
1686pub enum EthBlobTransactionSidecar {
1687    /// This transaction does not have a blob sidecar
1688    /// (applies to all non-EIP-4844 transaction types)
1689    None,
1690    /// This transaction has a blob sidecar (EIP-4844) but it is missing.
1691    ///
1692    /// This can happen when:
1693    /// - The sidecar was extracted after the transaction was added to the pool
1694    /// - The transaction was re-injected after a reorg without its sidecar
1695    /// - The transaction was recovered from the consensus format (e.g., from a block)
1696    Missing,
1697    /// The EIP-4844 transaction was received from the network with its complete sidecar.
1698    ///
1699    /// This sidecar contains:
1700    /// - The actual blob data (large data per blob)
1701    /// - KZG commitments for each blob
1702    /// - KZG proofs for validation
1703    ///
1704    /// The sidecar is required for validating the transaction but is not included
1705    /// in blocks (only the blob hashes are included in the consensus format).
1706    Present(BlobTransactionSidecarVariant),
1707}
1708
1709impl EthBlobTransactionSidecar {
1710    /// Returns the blob sidecar if it is present
1711    pub const fn maybe_sidecar(&self) -> Option<&BlobTransactionSidecarVariant> {
1712        match self {
1713            Self::Present(sidecar) => Some(sidecar),
1714            _ => None,
1715        }
1716    }
1717}
1718
1719/// Represents the current status of the pool.
1720#[derive(Debug, Clone, Copy, Default)]
1721pub struct PoolSize {
1722    /// Number of transactions in the _pending_ sub-pool.
1723    pub pending: usize,
1724    /// Reported size of transactions in the _pending_ sub-pool.
1725    pub pending_size: usize,
1726    /// Number of transactions in the _blob_ pool.
1727    pub blob: usize,
1728    /// Reported size of transactions in the _blob_ pool.
1729    pub blob_size: usize,
1730    /// Number of transactions in the _basefee_ pool.
1731    pub basefee: usize,
1732    /// Reported size of transactions in the _basefee_ sub-pool.
1733    pub basefee_size: usize,
1734    /// Number of transactions in the _queued_ sub-pool.
1735    pub queued: usize,
1736    /// Reported size of transactions in the _queued_ sub-pool.
1737    pub queued_size: usize,
1738    /// Number of all transactions of all sub-pools
1739    ///
1740    /// Note: this is the sum of ```pending + basefee + queued + blob```
1741    pub total: usize,
1742}
1743
1744// === impl PoolSize ===
1745
1746impl PoolSize {
1747    /// Asserts that the invariants of the pool size are met.
1748    #[cfg(test)]
1749    pub(crate) fn assert_invariants(&self) {
1750        assert_eq!(self.total, self.pending + self.basefee + self.queued + self.blob);
1751    }
1752}
1753
1754/// Represents the current status of the pool.
1755#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
1756pub struct BlockInfo {
1757    /// Hash for the currently tracked block.
1758    pub last_seen_block_hash: B256,
1759    /// Currently tracked block.
1760    pub last_seen_block_number: u64,
1761    /// Current block gas limit for the latest block.
1762    pub block_gas_limit: u64,
1763    /// Currently enforced base fee: the threshold for the basefee sub-pool.
1764    ///
1765    /// Note: this is the derived base fee of the _next_ block that builds on the block the pool is
1766    /// currently tracking.
1767    pub pending_basefee: u64,
1768    /// Currently enforced blob fee: the threshold for eip-4844 blob transactions.
1769    ///
1770    /// Note: this is the derived blob fee of the _next_ block that builds on the block the pool is
1771    /// currently tracking
1772    pub pending_blob_fee: Option<u128>,
1773}
1774
1775/// The limit to enforce for [`TransactionPool::get_pooled_transaction_elements`].
1776#[derive(Debug, Clone, Copy, Eq, PartialEq)]
1777pub enum GetPooledTransactionLimit {
1778    /// No limit, return all transactions.
1779    None,
1780    /// Enforce a size limit on the returned transactions, for example 2MB
1781    ResponseSizeSoftLimit(usize),
1782}
1783
1784impl GetPooledTransactionLimit {
1785    /// Returns true if the given size exceeds the limit.
1786    #[inline]
1787    pub const fn exceeds(&self, size: usize) -> bool {
1788        match self {
1789            Self::None => false,
1790            Self::ResponseSizeSoftLimit(limit) => size > *limit,
1791        }
1792    }
1793}
1794
1795/// A Stream that yields full transactions the subpool
1796#[must_use = "streams do nothing unless polled"]
1797#[derive(Debug)]
1798pub struct NewSubpoolTransactionStream<Tx: PoolTransaction> {
1799    st: Receiver<NewTransactionEvent<Tx>>,
1800    subpool: SubPool,
1801}
1802
1803// === impl NewSubpoolTransactionStream ===
1804
1805impl<Tx: PoolTransaction> NewSubpoolTransactionStream<Tx> {
1806    /// Create a new stream that yields full transactions from the subpool
1807    pub const fn new(st: Receiver<NewTransactionEvent<Tx>>, subpool: SubPool) -> Self {
1808        Self { st, subpool }
1809    }
1810
1811    /// Tries to receive the next value for this stream.
1812    pub fn try_recv(
1813        &mut self,
1814    ) -> Result<NewTransactionEvent<Tx>, tokio::sync::mpsc::error::TryRecvError> {
1815        loop {
1816            let event = self.st.try_recv()?;
1817            if event.subpool == self.subpool {
1818                return Ok(event)
1819            }
1820        }
1821    }
1822}
1823
1824impl<Tx: PoolTransaction> Stream for NewSubpoolTransactionStream<Tx> {
1825    type Item = NewTransactionEvent<Tx>;
1826
1827    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
1828        loop {
1829            match ready!(self.st.poll_recv(cx)) {
1830                Some(event) => {
1831                    if event.subpool == self.subpool {
1832                        return Poll::Ready(Some(event))
1833                    }
1834                }
1835                None => return Poll::Ready(None),
1836            }
1837        }
1838    }
1839}
1840
1841#[cfg(test)]
1842mod tests {
1843    use super::*;
1844    use alloy_consensus::{
1845        EthereumTxEnvelope, SignableTransaction, TxEip1559, TxEip2930, TxEip4844, TxEip7702,
1846        TxEnvelope, TxLegacy,
1847    };
1848    use alloy_eips::eip4844::DATA_GAS_PER_BLOB;
1849    use alloy_primitives::Signature;
1850
1851    #[test]
1852    fn test_pool_size_invariants() {
1853        let pool_size = PoolSize {
1854            pending: 10,
1855            pending_size: 1000,
1856            blob: 5,
1857            blob_size: 500,
1858            basefee: 8,
1859            basefee_size: 800,
1860            queued: 7,
1861            queued_size: 700,
1862            total: 10 + 5 + 8 + 7, // Correct total
1863        };
1864
1865        // Call the assert_invariants method to check if the invariants are correct
1866        pool_size.assert_invariants();
1867    }
1868
1869    #[test]
1870    #[should_panic]
1871    fn test_pool_size_invariants_fail() {
1872        let pool_size = PoolSize {
1873            pending: 10,
1874            pending_size: 1000,
1875            blob: 5,
1876            blob_size: 500,
1877            basefee: 8,
1878            basefee_size: 800,
1879            queued: 7,
1880            queued_size: 700,
1881            total: 10 + 5 + 8, // Incorrect total
1882        };
1883
1884        // Call the assert_invariants method, which should panic
1885        pool_size.assert_invariants();
1886    }
1887
1888    #[test]
1889    fn test_eth_pooled_transaction_new_legacy() {
1890        // Create a legacy transaction with specific parameters
1891        let tx = TxEnvelope::Legacy(
1892            TxLegacy {
1893                gas_price: 10,
1894                gas_limit: 1000,
1895                value: U256::from(100),
1896                ..Default::default()
1897            }
1898            .into_signed(Signature::test_signature()),
1899        );
1900        let transaction = Recovered::new_unchecked(tx, Default::default());
1901        let pooled_tx = EthPooledTransaction::new(transaction.clone(), 200);
1902
1903        // Check that the pooled transaction is created correctly
1904        assert_eq!(pooled_tx.transaction, transaction);
1905        assert_eq!(pooled_tx.encoded_length, 200);
1906        assert_eq!(pooled_tx.blob_sidecar, EthBlobTransactionSidecar::None);
1907        assert_eq!(pooled_tx.cost, U256::from(100) + U256::from(10 * 1000));
1908    }
1909
1910    #[test]
1911    fn test_eth_pooled_transaction_new_eip2930() {
1912        // Create an EIP-2930 transaction with specific parameters
1913        let tx = TxEnvelope::Eip2930(
1914            TxEip2930 {
1915                gas_price: 10,
1916                gas_limit: 1000,
1917                value: U256::from(100),
1918                ..Default::default()
1919            }
1920            .into_signed(Signature::test_signature()),
1921        );
1922        let transaction = Recovered::new_unchecked(tx, Default::default());
1923        let pooled_tx = EthPooledTransaction::new(transaction.clone(), 200);
1924        let expected_cost = U256::from(100) + (U256::from(10 * 1000));
1925
1926        assert_eq!(pooled_tx.transaction, transaction);
1927        assert_eq!(pooled_tx.encoded_length, 200);
1928        assert_eq!(pooled_tx.blob_sidecar, EthBlobTransactionSidecar::None);
1929        assert_eq!(pooled_tx.cost, expected_cost);
1930    }
1931
1932    #[test]
1933    fn test_eth_pooled_transaction_new_eip1559() {
1934        // Create an EIP-1559 transaction with specific parameters
1935        let tx = TxEnvelope::Eip1559(
1936            TxEip1559 {
1937                max_fee_per_gas: 10,
1938                gas_limit: 1000,
1939                value: U256::from(100),
1940                ..Default::default()
1941            }
1942            .into_signed(Signature::test_signature()),
1943        );
1944        let transaction = Recovered::new_unchecked(tx, Default::default());
1945        let pooled_tx = EthPooledTransaction::new(transaction.clone(), 200);
1946
1947        // Check that the pooled transaction is created correctly
1948        assert_eq!(pooled_tx.transaction, transaction);
1949        assert_eq!(pooled_tx.encoded_length, 200);
1950        assert_eq!(pooled_tx.blob_sidecar, EthBlobTransactionSidecar::None);
1951        assert_eq!(pooled_tx.cost, U256::from(100) + U256::from(10 * 1000));
1952    }
1953
1954    #[test]
1955    fn test_eth_pooled_transaction_new_eip4844() {
1956        // Create an EIP-4844 transaction with specific parameters
1957        let tx = EthereumTxEnvelope::Eip4844(
1958            TxEip4844 {
1959                max_fee_per_gas: 10,
1960                gas_limit: 1000,
1961                value: U256::from(100),
1962                max_fee_per_blob_gas: 5,
1963                blob_versioned_hashes: vec![B256::default()],
1964                ..Default::default()
1965            }
1966            .into_signed(Signature::test_signature()),
1967        );
1968        let transaction = Recovered::new_unchecked(tx, Default::default());
1969        let pooled_tx = EthPooledTransaction::new(transaction.clone(), 300);
1970
1971        // Check that the pooled transaction is created correctly
1972        assert_eq!(pooled_tx.transaction, transaction);
1973        assert_eq!(pooled_tx.encoded_length, 300);
1974        assert_eq!(pooled_tx.blob_sidecar, EthBlobTransactionSidecar::Missing);
1975        let expected_cost =
1976            U256::from(100) + U256::from(10 * 1000) + U256::from(5 * DATA_GAS_PER_BLOB);
1977        assert_eq!(pooled_tx.cost, expected_cost);
1978    }
1979
1980    #[test]
1981    fn test_eth_pooled_transaction_new_eip7702() {
1982        // Init an EIP-7702 transaction with specific parameters
1983        let tx = EthereumTxEnvelope::<TxEip4844>::Eip7702(
1984            TxEip7702 {
1985                max_fee_per_gas: 10,
1986                gas_limit: 1000,
1987                value: U256::from(100),
1988                ..Default::default()
1989            }
1990            .into_signed(Signature::test_signature()),
1991        );
1992        let transaction = Recovered::new_unchecked(tx, Default::default());
1993        let pooled_tx = EthPooledTransaction::new(transaction.clone(), 200);
1994
1995        // Check that the pooled transaction is created correctly
1996        assert_eq!(pooled_tx.transaction, transaction);
1997        assert_eq!(pooled_tx.encoded_length, 200);
1998        assert_eq!(pooled_tx.blob_sidecar, EthBlobTransactionSidecar::None);
1999        assert_eq!(pooled_tx.cost, U256::from(100) + U256::from(10 * 1000));
2000    }
2001
2002    #[test]
2003    fn test_pooled_transaction_limit() {
2004        // No limit should never exceed
2005        let limit_none = GetPooledTransactionLimit::None;
2006        // Any size should return false
2007        assert!(!limit_none.exceeds(1000));
2008
2009        // Size limit of 2MB (2 * 1024 * 1024 bytes)
2010        let size_limit_2mb = GetPooledTransactionLimit::ResponseSizeSoftLimit(2 * 1024 * 1024);
2011
2012        // Test with size below the limit
2013        // 1MB is below 2MB, should return false
2014        assert!(!size_limit_2mb.exceeds(1024 * 1024));
2015
2016        // Test with size exactly at the limit
2017        // 2MB equals the limit, should return false
2018        assert!(!size_limit_2mb.exceeds(2 * 1024 * 1024));
2019
2020        // Test with size exceeding the limit
2021        // 3MB is above the 2MB limit, should return true
2022        assert!(size_limit_2mb.exceeds(3 * 1024 * 1024));
2023    }
2024}