Skip to main content

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