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