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::{Address, TxHash, B256, U256};
23use reth_eth_wire_types::HandleMempoolData;
24use reth_primitives_traits::Recovered;
25use std::{collections::HashSet, 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    fn transaction_event_listener(&self, _tx_hash: TxHash) -> Option<TransactionEvents> {
102        None
103    }
104
105    fn all_transactions_event_listener(&self) -> AllTransactionsEvents<Self::Transaction> {
106        AllTransactionsEvents::new(mpsc::channel(1).1)
107    }
108
109    fn pending_transactions_listener_for(
110        &self,
111        _kind: TransactionListenerKind,
112    ) -> Receiver<TxHash> {
113        mpsc::channel(1).1
114    }
115
116    fn new_transactions_listener(&self) -> Receiver<NewTransactionEvent<Self::Transaction>> {
117        mpsc::channel(1).1
118    }
119
120    fn blob_transaction_sidecars_listener(&self) -> Receiver<NewBlobSidecar> {
121        mpsc::channel(1).1
122    }
123
124    fn new_transactions_listener_for(
125        &self,
126        _kind: TransactionListenerKind,
127    ) -> Receiver<NewTransactionEvent<Self::Transaction>> {
128        mpsc::channel(1).1
129    }
130
131    fn pooled_transaction_hashes(&self) -> Vec<TxHash> {
132        vec![]
133    }
134
135    fn pooled_transaction_hashes_max(&self, _max: usize) -> Vec<TxHash> {
136        vec![]
137    }
138
139    fn pooled_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
140        vec![]
141    }
142
143    fn pooled_transactions_max(
144        &self,
145        _max: usize,
146    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
147        vec![]
148    }
149
150    fn get_pooled_transaction_elements(
151        &self,
152        _tx_hashes: Vec<TxHash>,
153        _limit: GetPooledTransactionLimit,
154    ) -> Vec<<Self::Transaction as PoolTransaction>::Pooled> {
155        vec![]
156    }
157
158    fn get_pooled_transaction_element(
159        &self,
160        _tx_hash: TxHash,
161    ) -> Option<Recovered<<Self::Transaction as PoolTransaction>::Pooled>> {
162        None
163    }
164
165    fn best_transactions(
166        &self,
167    ) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>> {
168        Box::new(std::iter::empty())
169    }
170
171    fn best_transactions_with_attributes(
172        &self,
173        _: BestTransactionsAttributes,
174    ) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>> {
175        Box::new(std::iter::empty())
176    }
177
178    fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
179        vec![]
180    }
181
182    fn pending_transactions_max(
183        &self,
184        _max: usize,
185    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
186        vec![]
187    }
188
189    fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
190        vec![]
191    }
192
193    fn pending_and_queued_txn_count(&self) -> (usize, usize) {
194        (0, 0)
195    }
196
197    fn all_transactions(&self) -> AllPoolTransactions<Self::Transaction> {
198        AllPoolTransactions::default()
199    }
200
201    fn all_transaction_hashes(&self) -> Vec<TxHash> {
202        vec![]
203    }
204
205    fn remove_transactions(
206        &self,
207        _hashes: Vec<TxHash>,
208    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
209        vec![]
210    }
211
212    fn remove_transactions_and_descendants(
213        &self,
214        _hashes: Vec<TxHash>,
215    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
216        vec![]
217    }
218
219    fn remove_transactions_by_sender(
220        &self,
221        _sender: Address,
222    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
223        vec![]
224    }
225
226    fn retain_unknown<A>(&self, _announcement: &mut A)
227    where
228        A: HandleMempoolData,
229    {
230    }
231
232    fn get(&self, _tx_hash: &TxHash) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
233        None
234    }
235
236    fn get_all(&self, _txs: Vec<TxHash>) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
237        vec![]
238    }
239
240    fn on_propagated(&self, _txs: PropagatedTransactions) {}
241
242    fn get_transactions_by_sender(
243        &self,
244        _sender: Address,
245    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
246        vec![]
247    }
248
249    fn get_pending_transactions_with_predicate(
250        &self,
251        _predicate: impl FnMut(&ValidPoolTransaction<Self::Transaction>) -> bool,
252    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
253        vec![]
254    }
255
256    fn get_pending_transactions_by_sender(
257        &self,
258        _sender: Address,
259    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
260        vec![]
261    }
262
263    fn get_queued_transactions_by_sender(
264        &self,
265        _sender: Address,
266    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
267        vec![]
268    }
269
270    fn get_highest_transaction_by_sender(
271        &self,
272        _sender: Address,
273    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
274        None
275    }
276
277    fn get_highest_consecutive_transaction_by_sender(
278        &self,
279        _sender: Address,
280        _on_chain_nonce: u64,
281    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
282        None
283    }
284
285    fn get_transaction_by_sender_and_nonce(
286        &self,
287        _sender: Address,
288        _nonce: u64,
289    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
290        None
291    }
292
293    fn get_transactions_by_origin(
294        &self,
295        _origin: TransactionOrigin,
296    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
297        vec![]
298    }
299
300    fn get_pending_transactions_by_origin(
301        &self,
302        _origin: TransactionOrigin,
303    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
304        vec![]
305    }
306
307    fn unique_senders(&self) -> HashSet<Address> {
308        Default::default()
309    }
310
311    fn get_blob(
312        &self,
313        _tx_hash: TxHash,
314    ) -> Result<Option<Arc<BlobTransactionSidecarVariant>>, BlobStoreError> {
315        Ok(None)
316    }
317
318    fn get_all_blobs(
319        &self,
320        _tx_hashes: Vec<TxHash>,
321    ) -> Result<Vec<(TxHash, Arc<BlobTransactionSidecarVariant>)>, BlobStoreError> {
322        Ok(vec![])
323    }
324
325    fn get_all_blobs_exact(
326        &self,
327        tx_hashes: Vec<TxHash>,
328    ) -> Result<Vec<Arc<BlobTransactionSidecarVariant>>, BlobStoreError> {
329        if tx_hashes.is_empty() {
330            return Ok(vec![])
331        }
332        Err(BlobStoreError::MissingSidecar(tx_hashes[0]))
333    }
334
335    fn get_blobs_for_versioned_hashes_v1(
336        &self,
337        versioned_hashes: &[B256],
338    ) -> Result<Vec<Option<BlobAndProofV1>>, BlobStoreError> {
339        Ok(vec![None; versioned_hashes.len()])
340    }
341
342    fn get_blobs_for_versioned_hashes_v2(
343        &self,
344        _versioned_hashes: &[B256],
345    ) -> Result<Option<Vec<BlobAndProofV2>>, BlobStoreError> {
346        Ok(None)
347    }
348}
349
350/// A [`TransactionValidator`] that does nothing.
351#[derive(Debug, Clone)]
352#[non_exhaustive]
353pub struct MockTransactionValidator<T> {
354    propagate_local: bool,
355    return_invalid: bool,
356    _marker: PhantomData<T>,
357}
358
359impl<T: EthPoolTransaction> TransactionValidator for MockTransactionValidator<T> {
360    type Transaction = T;
361
362    async fn validate_transaction(
363        &self,
364        origin: TransactionOrigin,
365        mut transaction: Self::Transaction,
366    ) -> TransactionValidationOutcome<Self::Transaction> {
367        if self.return_invalid {
368            return TransactionValidationOutcome::Invalid(
369                transaction,
370                InvalidPoolTransactionError::Underpriced,
371            );
372        }
373        let maybe_sidecar = transaction.take_blob().maybe_sidecar().cloned();
374        // we return `balance: U256::MAX` to simulate a valid transaction which will never go into
375        // overdraft
376        TransactionValidationOutcome::Valid {
377            balance: U256::MAX,
378            state_nonce: 0,
379            bytecode_hash: None,
380            transaction: ValidTransaction::new(transaction, maybe_sidecar),
381            propagate: match origin {
382                TransactionOrigin::External => true,
383                TransactionOrigin::Local => self.propagate_local,
384                TransactionOrigin::Private => false,
385            },
386            authorities: None,
387        }
388    }
389}
390
391impl<T> MockTransactionValidator<T> {
392    /// Creates a new [`MockTransactionValidator`] that does not allow local transactions to be
393    /// propagated.
394    pub fn no_propagate_local() -> Self {
395        Self { propagate_local: false, return_invalid: false, _marker: Default::default() }
396    }
397    /// Creates a new [`MockTransactionValidator`] that always return a invalid outcome.
398    pub fn return_invalid() -> Self {
399        Self { propagate_local: false, return_invalid: true, _marker: Default::default() }
400    }
401}
402
403impl<T> Default for MockTransactionValidator<T> {
404    fn default() -> Self {
405        Self { propagate_local: true, return_invalid: false, _marker: Default::default() }
406    }
407}
408
409/// An error that contains the transaction that failed to be inserted into the noop pool.
410#[derive(Debug, Clone, thiserror::Error)]
411#[error("can't insert transaction into the noop pool that does nothing")]
412pub struct NoopInsertError<T: EthPoolTransaction = EthPooledTransaction> {
413    tx: T,
414}
415
416impl<T: EthPoolTransaction> NoopInsertError<T> {
417    const fn new(tx: T) -> Self {
418        Self { tx }
419    }
420
421    /// Returns the transaction that failed to be inserted.
422    pub fn into_inner(self) -> T {
423        self.tx
424    }
425}