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