reth_transaction_pool/
noop.rs

1//! A transaction pool implementation that does nothing.
2//!
3//! This is useful for wiring components together that don't require an actual pool but still need
4//! to be generic over it.
5
6use crate::{
7    blobstore::BlobStoreError,
8    error::{InvalidPoolTransactionError, PoolError},
9    pool::TransactionListenerKind,
10    traits::{BestTransactionsAttributes, GetPooledTransactionLimit, NewBlobSidecar},
11    validate::ValidTransaction,
12    AllPoolTransactions, AllTransactionsEvents, BestTransactions, BlockInfo, EthPoolTransaction,
13    EthPooledTransaction, NewTransactionEvent, PoolResult, PoolSize, PoolTransaction,
14    PropagatedTransactions, TransactionEvents, TransactionOrigin, TransactionPool,
15    TransactionValidationOutcome, TransactionValidator, ValidPoolTransaction,
16};
17use alloy_eips::{
18    eip1559::ETHEREUM_BLOCK_GAS_LIMIT_30M,
19    eip4844::{BlobAndProofV1, BlobTransactionSidecar},
20};
21use alloy_primitives::{Address, TxHash, B256, U256};
22use reth_eth_wire_types::HandleMempoolData;
23use reth_primitives_traits::Recovered;
24use std::{collections::HashSet, marker::PhantomData, sync::Arc};
25use tokio::sync::{mpsc, mpsc::Receiver};
26
27/// A [`TransactionPool`] implementation that does nothing.
28///
29/// All transactions are rejected and no events are emitted.
30/// This type will never hold any transactions and is only useful for wiring components together.
31#[derive(Debug, Clone)]
32#[non_exhaustive]
33pub struct NoopTransactionPool<T: EthPoolTransaction = EthPooledTransaction> {
34    /// Type marker
35    _marker: PhantomData<T>,
36}
37
38impl<T: EthPoolTransaction> NoopTransactionPool<T> {
39    /// Creates a new [`NoopTransactionPool`].
40    pub fn new() -> Self {
41        Self { _marker: Default::default() }
42    }
43}
44
45impl Default for NoopTransactionPool<EthPooledTransaction> {
46    fn default() -> Self {
47        Self { _marker: Default::default() }
48    }
49}
50
51impl<T: EthPoolTransaction> TransactionPool for NoopTransactionPool<T> {
52    type Transaction = T;
53
54    fn pool_size(&self) -> PoolSize {
55        Default::default()
56    }
57
58    fn block_info(&self) -> BlockInfo {
59        BlockInfo {
60            block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
61            last_seen_block_hash: Default::default(),
62            last_seen_block_number: 0,
63            pending_basefee: 0,
64            pending_blob_fee: None,
65        }
66    }
67
68    async fn add_transaction_and_subscribe(
69        &self,
70        _origin: TransactionOrigin,
71        transaction: Self::Transaction,
72    ) -> PoolResult<TransactionEvents> {
73        let hash = *transaction.hash();
74        Err(PoolError::other(hash, Box::new(NoopInsertError::new(transaction))))
75    }
76
77    async fn add_transaction(
78        &self,
79        _origin: TransactionOrigin,
80        transaction: Self::Transaction,
81    ) -> PoolResult<TxHash> {
82        let hash = *transaction.hash();
83        Err(PoolError::other(hash, Box::new(NoopInsertError::new(transaction))))
84    }
85
86    async fn add_transactions(
87        &self,
88        _origin: TransactionOrigin,
89        transactions: Vec<Self::Transaction>,
90    ) -> Vec<PoolResult<TxHash>> {
91        transactions
92            .into_iter()
93            .map(|transaction| {
94                let hash = *transaction.hash();
95                Err(PoolError::other(hash, Box::new(NoopInsertError::new(transaction))))
96            })
97            .collect()
98    }
99
100    fn transaction_event_listener(&self, _tx_hash: TxHash) -> Option<TransactionEvents> {
101        None
102    }
103
104    fn all_transactions_event_listener(&self) -> AllTransactionsEvents<Self::Transaction> {
105        AllTransactionsEvents::new(mpsc::channel(1).1)
106    }
107
108    fn pending_transactions_listener_for(
109        &self,
110        _kind: TransactionListenerKind,
111    ) -> Receiver<TxHash> {
112        mpsc::channel(1).1
113    }
114
115    fn new_transactions_listener(&self) -> Receiver<NewTransactionEvent<Self::Transaction>> {
116        mpsc::channel(1).1
117    }
118
119    fn blob_transaction_sidecars_listener(&self) -> Receiver<NewBlobSidecar> {
120        mpsc::channel(1).1
121    }
122
123    fn new_transactions_listener_for(
124        &self,
125        _kind: TransactionListenerKind,
126    ) -> Receiver<NewTransactionEvent<Self::Transaction>> {
127        mpsc::channel(1).1
128    }
129
130    fn pooled_transaction_hashes(&self) -> Vec<TxHash> {
131        vec![]
132    }
133
134    fn pooled_transaction_hashes_max(&self, _max: usize) -> Vec<TxHash> {
135        vec![]
136    }
137
138    fn pooled_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
139        vec![]
140    }
141
142    fn pooled_transactions_max(
143        &self,
144        _max: usize,
145    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
146        vec![]
147    }
148
149    fn get_pooled_transaction_elements(
150        &self,
151        _tx_hashes: Vec<TxHash>,
152        _limit: GetPooledTransactionLimit,
153    ) -> Vec<<Self::Transaction as PoolTransaction>::Pooled> {
154        vec![]
155    }
156
157    fn get_pooled_transaction_element(
158        &self,
159        _tx_hash: TxHash,
160    ) -> Option<Recovered<<Self::Transaction as PoolTransaction>::Pooled>> {
161        None
162    }
163
164    fn best_transactions(
165        &self,
166    ) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>> {
167        Box::new(std::iter::empty())
168    }
169
170    fn best_transactions_with_attributes(
171        &self,
172        _: BestTransactionsAttributes,
173    ) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>> {
174        Box::new(std::iter::empty())
175    }
176
177    fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
178        vec![]
179    }
180
181    fn pending_transactions_max(
182        &self,
183        _max: usize,
184    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
185        vec![]
186    }
187
188    fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
189        vec![]
190    }
191
192    fn all_transactions(&self) -> AllPoolTransactions<Self::Transaction> {
193        AllPoolTransactions::default()
194    }
195
196    fn remove_transactions(
197        &self,
198        _hashes: Vec<TxHash>,
199    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
200        vec![]
201    }
202
203    fn remove_transactions_and_descendants(
204        &self,
205        _hashes: Vec<TxHash>,
206    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
207        vec![]
208    }
209
210    fn remove_transactions_by_sender(
211        &self,
212        _sender: Address,
213    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
214        vec![]
215    }
216
217    fn retain_unknown<A>(&self, _announcement: &mut A)
218    where
219        A: HandleMempoolData,
220    {
221    }
222
223    fn get(&self, _tx_hash: &TxHash) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
224        None
225    }
226
227    fn get_all(&self, _txs: Vec<TxHash>) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
228        vec![]
229    }
230
231    fn on_propagated(&self, _txs: PropagatedTransactions) {}
232
233    fn get_transactions_by_sender(
234        &self,
235        _sender: Address,
236    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
237        vec![]
238    }
239
240    fn get_pending_transactions_with_predicate(
241        &self,
242        _predicate: impl FnMut(&ValidPoolTransaction<Self::Transaction>) -> bool,
243    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
244        vec![]
245    }
246
247    fn get_pending_transactions_by_sender(
248        &self,
249        _sender: Address,
250    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
251        vec![]
252    }
253
254    fn get_queued_transactions_by_sender(
255        &self,
256        _sender: Address,
257    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
258        vec![]
259    }
260
261    fn get_highest_transaction_by_sender(
262        &self,
263        _sender: Address,
264    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
265        None
266    }
267
268    fn get_highest_consecutive_transaction_by_sender(
269        &self,
270        _sender: Address,
271        _on_chain_nonce: u64,
272    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
273        None
274    }
275
276    fn get_transaction_by_sender_and_nonce(
277        &self,
278        _sender: Address,
279        _nonce: u64,
280    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
281        None
282    }
283
284    fn get_transactions_by_origin(
285        &self,
286        _origin: TransactionOrigin,
287    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
288        vec![]
289    }
290
291    fn get_pending_transactions_by_origin(
292        &self,
293        _origin: TransactionOrigin,
294    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
295        vec![]
296    }
297
298    fn unique_senders(&self) -> HashSet<Address> {
299        Default::default()
300    }
301
302    fn get_blob(
303        &self,
304        _tx_hash: TxHash,
305    ) -> Result<Option<Arc<BlobTransactionSidecar>>, BlobStoreError> {
306        Ok(None)
307    }
308
309    fn get_all_blobs(
310        &self,
311        _tx_hashes: Vec<TxHash>,
312    ) -> Result<Vec<(TxHash, Arc<BlobTransactionSidecar>)>, BlobStoreError> {
313        Ok(vec![])
314    }
315
316    fn get_all_blobs_exact(
317        &self,
318        tx_hashes: Vec<TxHash>,
319    ) -> Result<Vec<Arc<BlobTransactionSidecar>>, BlobStoreError> {
320        if tx_hashes.is_empty() {
321            return Ok(vec![])
322        }
323        Err(BlobStoreError::MissingSidecar(tx_hashes[0]))
324    }
325
326    fn get_blobs_for_versioned_hashes(
327        &self,
328        versioned_hashes: &[B256],
329    ) -> Result<Vec<Option<BlobAndProofV1>>, BlobStoreError> {
330        Ok(vec![None; versioned_hashes.len()])
331    }
332}
333
334/// A [`TransactionValidator`] that does nothing.
335#[derive(Debug, Clone)]
336#[non_exhaustive]
337pub struct MockTransactionValidator<T> {
338    propagate_local: bool,
339    return_invalid: bool,
340    _marker: PhantomData<T>,
341}
342
343impl<T: EthPoolTransaction> TransactionValidator for MockTransactionValidator<T> {
344    type Transaction = T;
345
346    async fn validate_transaction(
347        &self,
348        origin: TransactionOrigin,
349        mut transaction: Self::Transaction,
350    ) -> TransactionValidationOutcome<Self::Transaction> {
351        if self.return_invalid {
352            return TransactionValidationOutcome::Invalid(
353                transaction,
354                InvalidPoolTransactionError::Underpriced,
355            );
356        }
357        let maybe_sidecar = transaction.take_blob().maybe_sidecar().cloned();
358        // we return `balance: U256::MAX` to simulate a valid transaction which will never go into
359        // overdraft
360        TransactionValidationOutcome::Valid {
361            balance: U256::MAX,
362            state_nonce: 0,
363            transaction: ValidTransaction::new(transaction, maybe_sidecar),
364            propagate: match origin {
365                TransactionOrigin::External => true,
366                TransactionOrigin::Local => self.propagate_local,
367                TransactionOrigin::Private => false,
368            },
369        }
370    }
371}
372
373impl<T> MockTransactionValidator<T> {
374    /// Creates a new [`MockTransactionValidator`] that does not allow local transactions to be
375    /// propagated.
376    pub fn no_propagate_local() -> Self {
377        Self { propagate_local: false, return_invalid: false, _marker: Default::default() }
378    }
379    /// Creates a new [`MockTransactionValidator`] that always return a invalid outcome.
380    pub fn return_invalid() -> Self {
381        Self { propagate_local: false, return_invalid: true, _marker: Default::default() }
382    }
383}
384
385impl<T> Default for MockTransactionValidator<T> {
386    fn default() -> Self {
387        Self { propagate_local: true, return_invalid: false, _marker: Default::default() }
388    }
389}
390
391/// An error that contains the transaction that failed to be inserted into the noop pool.
392#[derive(Debug, Clone, thiserror::Error)]
393#[error("can't insert transaction into the noop pool that does nothing")]
394pub struct NoopInsertError<T: EthPoolTransaction = EthPooledTransaction> {
395    tx: T,
396}
397
398impl<T: EthPoolTransaction> NoopInsertError<T> {
399    const fn new(tx: T) -> Self {
400        Self { tx }
401    }
402
403    /// Returns the transaction that failed to be inserted.
404    pub fn into_inner(self) -> T {
405        self.tx
406    }
407}