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