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