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::BlobStoreError,
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},
20    eip7594::BlobTransactionSidecarVariant,
21};
22use alloy_primitives::{map::AddressSet, Address, TxHash, 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_with_origins(
88        &self,
89        transactions: impl IntoIterator<Item = (TransactionOrigin, Self::Transaction)> + Send,
90    ) -> Vec<PoolResult<AddedTransactionOutcome>> {
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 append_pooled_transaction_elements(
158        &self,
159        _tx_hashes: &[TxHash],
160        _limit: GetPooledTransactionLimit,
161        _out: &mut Vec<<Self::Transaction as PoolTransaction>::Pooled>,
162    ) {
163    }
164
165    fn get_pooled_transaction_element(
166        &self,
167        _tx_hash: TxHash,
168    ) -> Option<Recovered<<Self::Transaction as PoolTransaction>::Pooled>> {
169        None
170    }
171
172    fn best_transactions(
173        &self,
174    ) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>> {
175        Box::new(std::iter::empty())
176    }
177
178    fn best_transactions_with_attributes(
179        &self,
180        _: BestTransactionsAttributes,
181    ) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>> {
182        Box::new(std::iter::empty())
183    }
184
185    fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
186        vec![]
187    }
188
189    fn pending_transactions_max(
190        &self,
191        _max: usize,
192    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
193        vec![]
194    }
195
196    fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
197        vec![]
198    }
199
200    fn pending_and_queued_txn_count(&self) -> (usize, usize) {
201        (0, 0)
202    }
203
204    fn all_transactions(&self) -> AllPoolTransactions<Self::Transaction> {
205        AllPoolTransactions::default()
206    }
207
208    fn all_transaction_hashes(&self) -> Vec<TxHash> {
209        vec![]
210    }
211
212    fn remove_transactions(
213        &self,
214        _hashes: Vec<TxHash>,
215    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
216        vec![]
217    }
218
219    fn remove_transactions_and_descendants(
220        &self,
221        _hashes: Vec<TxHash>,
222    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
223        vec![]
224    }
225
226    fn remove_transactions_by_sender(
227        &self,
228        _sender: Address,
229    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
230        vec![]
231    }
232
233    fn prune_transactions(
234        &self,
235        _hashes: Vec<TxHash>,
236    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
237        vec![]
238    }
239
240    fn retain_unknown<A>(&self, _announcement: &mut A)
241    where
242        A: HandleMempoolData,
243    {
244    }
245
246    fn get(&self, _tx_hash: &TxHash) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
247        None
248    }
249
250    fn get_all(&self, _txs: Vec<TxHash>) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
251        vec![]
252    }
253
254    fn on_propagated(&self, _txs: PropagatedTransactions) {}
255
256    fn get_transactions_by_sender(
257        &self,
258        _sender: Address,
259    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
260        vec![]
261    }
262
263    fn get_pending_transactions_with_predicate(
264        &self,
265        _predicate: impl FnMut(&ValidPoolTransaction<Self::Transaction>) -> bool,
266    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
267        vec![]
268    }
269
270    fn get_pending_transactions_by_sender(
271        &self,
272        _sender: Address,
273    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
274        vec![]
275    }
276
277    fn get_queued_transactions_by_sender(
278        &self,
279        _sender: Address,
280    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
281        vec![]
282    }
283
284    fn get_highest_transaction_by_sender(
285        &self,
286        _sender: Address,
287    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
288        None
289    }
290
291    fn get_highest_consecutive_transaction_by_sender(
292        &self,
293        _sender: Address,
294        _on_chain_nonce: u64,
295    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
296        None
297    }
298
299    fn get_transaction_by_sender_and_nonce(
300        &self,
301        _sender: Address,
302        _nonce: u64,
303    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
304        None
305    }
306
307    fn get_transactions_by_origin(
308        &self,
309        _origin: TransactionOrigin,
310    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
311        vec![]
312    }
313
314    fn get_pending_transactions_by_origin(
315        &self,
316        _origin: TransactionOrigin,
317    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
318        vec![]
319    }
320
321    fn unique_senders(&self) -> AddressSet {
322        Default::default()
323    }
324
325    fn get_blob(
326        &self,
327        _tx_hash: TxHash,
328    ) -> Result<Option<Arc<BlobTransactionSidecarVariant>>, BlobStoreError> {
329        Ok(None)
330    }
331
332    fn get_all_blobs(
333        &self,
334        _tx_hashes: Vec<TxHash>,
335    ) -> Result<Vec<(TxHash, Arc<BlobTransactionSidecarVariant>)>, BlobStoreError> {
336        Ok(vec![])
337    }
338
339    fn get_all_blobs_exact(
340        &self,
341        tx_hashes: Vec<TxHash>,
342    ) -> Result<Vec<Arc<BlobTransactionSidecarVariant>>, BlobStoreError> {
343        if tx_hashes.is_empty() {
344            return Ok(vec![])
345        }
346        Err(BlobStoreError::MissingSidecar(tx_hashes[0]))
347    }
348
349    fn get_blobs_for_versioned_hashes_v1(
350        &self,
351        versioned_hashes: &[B256],
352    ) -> Result<Vec<Option<BlobAndProofV1>>, BlobStoreError> {
353        Ok(vec![None; versioned_hashes.len()])
354    }
355
356    fn get_blobs_for_versioned_hashes_v2(
357        &self,
358        _versioned_hashes: &[B256],
359    ) -> Result<Option<Vec<BlobAndProofV2>>, BlobStoreError> {
360        Ok(None)
361    }
362
363    fn get_blobs_for_versioned_hashes_v3(
364        &self,
365        versioned_hashes: &[B256],
366    ) -> Result<Vec<Option<BlobAndProofV2>>, BlobStoreError> {
367        Ok(vec![None; versioned_hashes.len()])
368    }
369}
370
371/// A [`TransactionValidator`] that does nothing.
372#[derive(Debug, Clone)]
373#[non_exhaustive]
374pub struct MockTransactionValidator<T> {
375    propagate_local: bool,
376    return_invalid: bool,
377    _marker: PhantomData<T>,
378}
379
380impl<T: EthPoolTransaction> TransactionValidator for MockTransactionValidator<T> {
381    type Transaction = T;
382    type Block = reth_ethereum_primitives::Block;
383
384    async fn validate_transaction(
385        &self,
386        origin: TransactionOrigin,
387        mut transaction: Self::Transaction,
388    ) -> TransactionValidationOutcome<Self::Transaction> {
389        if self.return_invalid {
390            return TransactionValidationOutcome::Invalid(
391                transaction,
392                InvalidPoolTransactionError::Underpriced,
393            );
394        }
395        let maybe_sidecar = transaction.take_blob().maybe_sidecar().cloned();
396        // we return `balance: U256::MAX` to simulate a valid transaction which will never go into
397        // overdraft
398        TransactionValidationOutcome::Valid {
399            balance: U256::MAX,
400            state_nonce: 0,
401            bytecode_hash: None,
402            transaction: ValidTransaction::new(transaction, maybe_sidecar),
403            propagate: match origin {
404                TransactionOrigin::External => true,
405                TransactionOrigin::Local => self.propagate_local,
406                TransactionOrigin::Private => false,
407            },
408            authorities: None,
409        }
410    }
411}
412
413impl<T> MockTransactionValidator<T> {
414    /// Creates a new [`MockTransactionValidator`] that does not allow local transactions to be
415    /// propagated.
416    pub fn no_propagate_local() -> Self {
417        Self { propagate_local: false, return_invalid: false, _marker: Default::default() }
418    }
419    /// Creates a new [`MockTransactionValidator`] that always returns an invalid outcome.
420    pub fn return_invalid() -> Self {
421        Self { propagate_local: false, return_invalid: true, _marker: Default::default() }
422    }
423}
424
425impl<T> Default for MockTransactionValidator<T> {
426    fn default() -> Self {
427        Self { propagate_local: true, return_invalid: false, _marker: Default::default() }
428    }
429}
430
431/// An error that contains the transaction that failed to be inserted into the noop pool.
432#[derive(Debug, Clone, thiserror::Error)]
433#[error("can't insert transaction into the noop pool that does nothing")]
434pub struct NoopInsertError<T: EthPoolTransaction = EthPooledTransaction> {
435    tx: T,
436}
437
438impl<T: EthPoolTransaction> NoopInsertError<T> {
439    const fn new(tx: T) -> Self {
440        Self { tx }
441    }
442
443    /// Returns the transaction that failed to be inserted.
444    pub fn into_inner(self) -> T {
445        self.tx
446    }
447}