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::BlobTransactionSidecarVariant,
22};
23use alloy_primitives::{map::AddressSet, Address, TxHash, B128, 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_transaction_hashes(&self) -> Vec<TxHash> {
224        vec![]
225    }
226
227    fn remove_transactions(
228        &self,
229        _hashes: Vec<TxHash>,
230    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
231        vec![]
232    }
233
234    fn remove_transactions_and_descendants(
235        &self,
236        _hashes: Vec<TxHash>,
237    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
238        vec![]
239    }
240
241    fn remove_transactions_by_sender(
242        &self,
243        _sender: Address,
244    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
245        vec![]
246    }
247
248    fn prune_transactions(
249        &self,
250        _hashes: Vec<TxHash>,
251    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
252        vec![]
253    }
254
255    fn retain_unknown<A>(&self, _announcement: &mut A)
256    where
257        A: HandleMempoolData,
258    {
259    }
260
261    fn retain_contains<A>(&self, _announcement: &mut A)
262    where
263        A: HandleMempoolData,
264    {
265    }
266
267    fn get(&self, _tx_hash: &TxHash) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
268        None
269    }
270
271    fn get_all(&self, _txs: Vec<TxHash>) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
272        vec![]
273    }
274
275    fn on_propagated(&self, _txs: PropagatedTransactions) {}
276
277    fn get_transactions_by_sender(
278        &self,
279        _sender: Address,
280    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
281        vec![]
282    }
283
284    fn get_pending_transactions_with_predicate(
285        &self,
286        _predicate: impl FnMut(&ValidPoolTransaction<Self::Transaction>) -> bool,
287    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
288        vec![]
289    }
290
291    fn get_pending_transactions_by_sender(
292        &self,
293        _sender: Address,
294    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
295        vec![]
296    }
297
298    fn get_queued_transactions_by_sender(
299        &self,
300        _sender: Address,
301    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
302        vec![]
303    }
304
305    fn get_highest_transaction_by_sender(
306        &self,
307        _sender: Address,
308    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
309        None
310    }
311
312    fn get_highest_consecutive_transaction_by_sender(
313        &self,
314        _sender: Address,
315        _on_chain_nonce: u64,
316    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
317        None
318    }
319
320    fn get_transaction_by_sender_and_nonce(
321        &self,
322        _sender: Address,
323        _nonce: u64,
324    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
325        None
326    }
327
328    fn get_transactions_by_origin(
329        &self,
330        _origin: TransactionOrigin,
331    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
332        vec![]
333    }
334
335    fn get_pending_transactions_by_origin(
336        &self,
337        _origin: TransactionOrigin,
338    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
339        vec![]
340    }
341
342    fn unique_senders(&self) -> AddressSet {
343        Default::default()
344    }
345
346    fn get_blob(
347        &self,
348        _tx_hash: TxHash,
349    ) -> Result<Option<Arc<BlobTransactionSidecarVariant>>, BlobStoreError> {
350        Ok(None)
351    }
352
353    fn get_all_blobs(
354        &self,
355        _tx_hashes: Vec<TxHash>,
356    ) -> Result<Vec<(TxHash, Arc<BlobTransactionSidecarVariant>)>, BlobStoreError> {
357        Ok(vec![])
358    }
359
360    fn get_all_blobs_exact(
361        &self,
362        tx_hashes: Vec<TxHash>,
363    ) -> Result<Vec<Arc<BlobTransactionSidecarVariant>>, BlobStoreError> {
364        if tx_hashes.is_empty() {
365            return Ok(vec![])
366        }
367        Err(BlobStoreError::MissingSidecar(tx_hashes[0]))
368    }
369
370    fn get_blobs_for_versioned_hashes_v1(
371        &self,
372        versioned_hashes: &[B256],
373    ) -> Result<Vec<Option<BlobAndProofV1>>, BlobStoreError> {
374        Ok(vec![None; versioned_hashes.len()])
375    }
376
377    fn get_blobs_for_versioned_hashes_v2(
378        &self,
379        _versioned_hashes: &[B256],
380    ) -> Result<Option<Vec<BlobAndProofV2>>, BlobStoreError> {
381        Ok(None)
382    }
383
384    fn get_blobs_for_versioned_hashes_v3(
385        &self,
386        versioned_hashes: &[B256],
387    ) -> Result<Vec<Option<BlobAndProofV2>>, BlobStoreError> {
388        Ok(vec![None; versioned_hashes.len()])
389    }
390
391    fn get_blobs_for_versioned_hashes_v4(
392        &self,
393        versioned_hashes: &[B256],
394        _indices_bitarray: B128,
395    ) -> Result<Vec<Option<BlobCellsAndProofsV1>>, BlobStoreError> {
396        Ok(vec![None; versioned_hashes.len()])
397    }
398
399    fn has_blobs_for_versioned_hashes(
400        &self,
401        versioned_hashes: &[B256],
402    ) -> Result<Vec<bool>, BlobStoreError> {
403        Ok(vec![false; versioned_hashes.len()])
404    }
405
406    fn blob_store(&self) -> Box<dyn BlobStore> {
407        Box::new(NoopBlobStore)
408    }
409}
410
411/// A [`TransactionValidator`] that does nothing.
412#[derive(Debug, Clone)]
413#[non_exhaustive]
414pub struct MockTransactionValidator<T> {
415    propagate_local: bool,
416    return_invalid: bool,
417    _marker: PhantomData<T>,
418}
419
420impl<T: EthPoolTransaction> TransactionValidator for MockTransactionValidator<T> {
421    type Transaction = T;
422    type Block = reth_ethereum_primitives::Block;
423
424    async fn validate_transaction(
425        &self,
426        origin: TransactionOrigin,
427        mut transaction: Self::Transaction,
428    ) -> TransactionValidationOutcome<Self::Transaction> {
429        if self.return_invalid {
430            return TransactionValidationOutcome::Invalid(
431                transaction,
432                InvalidPoolTransactionError::Underpriced,
433            );
434        }
435        let maybe_sidecar = match transaction.take_blob() {
436            EthBlobTransactionSidecar::Present(sidecar) => Some(sidecar),
437            _ => None,
438        };
439        // we return `balance: U256::MAX` to simulate a valid transaction which will never go into
440        // overdraft
441        TransactionValidationOutcome::Valid {
442            balance: U256::MAX,
443            state_nonce: 0,
444            bytecode_hash: None,
445            transaction: ValidTransaction::new(transaction, maybe_sidecar),
446            propagate: match origin {
447                TransactionOrigin::External => true,
448                TransactionOrigin::Local => self.propagate_local,
449                TransactionOrigin::Private => false,
450            },
451            authorities: None,
452        }
453    }
454}
455
456impl<T> MockTransactionValidator<T> {
457    /// Creates a new [`MockTransactionValidator`] that does not allow local transactions to be
458    /// propagated.
459    pub fn no_propagate_local() -> Self {
460        Self { propagate_local: false, return_invalid: false, _marker: Default::default() }
461    }
462    /// Creates a new [`MockTransactionValidator`] that always returns an invalid outcome.
463    pub fn return_invalid() -> Self {
464        Self { propagate_local: false, return_invalid: true, _marker: Default::default() }
465    }
466}
467
468impl<T> Default for MockTransactionValidator<T> {
469    fn default() -> Self {
470        Self { propagate_local: true, return_invalid: false, _marker: Default::default() }
471    }
472}
473
474/// An error that contains the transaction that failed to be inserted into the noop pool.
475#[derive(Debug, Clone, thiserror::Error)]
476#[error("can't insert transaction into the noop pool that does nothing")]
477pub struct NoopInsertError<T: EthPoolTransaction = EthPooledTransaction> {
478    tx: T,
479}
480
481impl<T: EthPoolTransaction> NoopInsertError<T> {
482    const fn new(tx: T) -> Self {
483        Self { tx }
484    }
485
486    /// Returns the transaction that failed to be inserted.
487    pub fn into_inner(self) -> T {
488        self.tx
489    }
490}