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::PoolError,
9    traits::{
10        BestTransactionsAttributes, GetPooledTransactionLimit, NewBlobSidecar,
11        TransactionListenerKind,
12    },
13    validate::ValidTransaction,
14    AllPoolTransactions, AllTransactionsEvents, BestTransactions, BlockInfo, EthPoolTransaction,
15    EthPooledTransaction, NewTransactionEvent, PoolResult, PoolSize, PoolTransaction,
16    PropagatedTransactions, TransactionEvents, TransactionOrigin, TransactionPool,
17    TransactionValidationOutcome, TransactionValidator, ValidPoolTransaction,
18};
19use alloy_eips::{
20    eip1559::ETHEREUM_BLOCK_GAS_LIMIT_30M,
21    eip4844::{BlobAndProofV1, BlobTransactionSidecar},
22};
23use alloy_primitives::{Address, TxHash, B256, U256};
24use reth_eth_wire_types::HandleMempoolData;
25use reth_primitives_traits::Recovered;
26use std::{collections::HashSet, 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, Default)]
34#[non_exhaustive]
35pub struct NoopTransactionPool;
36
37impl TransactionPool for NoopTransactionPool {
38    type Transaction = EthPooledTransaction;
39
40    fn pool_size(&self) -> PoolSize {
41        Default::default()
42    }
43
44    fn block_info(&self) -> BlockInfo {
45        BlockInfo {
46            block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
47            last_seen_block_hash: Default::default(),
48            last_seen_block_number: 0,
49            pending_basefee: 0,
50            pending_blob_fee: None,
51        }
52    }
53
54    async fn add_transaction_and_subscribe(
55        &self,
56        _origin: TransactionOrigin,
57        transaction: Self::Transaction,
58    ) -> PoolResult<TransactionEvents> {
59        let hash = *transaction.hash();
60        Err(PoolError::other(hash, Box::new(NoopInsertError::new(transaction))))
61    }
62
63    async fn add_transaction(
64        &self,
65        _origin: TransactionOrigin,
66        transaction: Self::Transaction,
67    ) -> PoolResult<TxHash> {
68        let hash = *transaction.hash();
69        Err(PoolError::other(hash, Box::new(NoopInsertError::new(transaction))))
70    }
71
72    async fn add_transactions(
73        &self,
74        _origin: TransactionOrigin,
75        transactions: Vec<Self::Transaction>,
76    ) -> Vec<PoolResult<TxHash>> {
77        transactions
78            .into_iter()
79            .map(|transaction| {
80                let hash = *transaction.hash();
81                Err(PoolError::other(hash, Box::new(NoopInsertError::new(transaction))))
82            })
83            .collect()
84    }
85
86    fn transaction_event_listener(&self, _tx_hash: TxHash) -> Option<TransactionEvents> {
87        None
88    }
89
90    fn all_transactions_event_listener(&self) -> AllTransactionsEvents<Self::Transaction> {
91        AllTransactionsEvents::new(mpsc::channel(1).1)
92    }
93
94    fn pending_transactions_listener_for(
95        &self,
96        _kind: TransactionListenerKind,
97    ) -> Receiver<TxHash> {
98        mpsc::channel(1).1
99    }
100
101    fn new_transactions_listener(&self) -> Receiver<NewTransactionEvent<Self::Transaction>> {
102        mpsc::channel(1).1
103    }
104
105    fn blob_transaction_sidecars_listener(&self) -> Receiver<NewBlobSidecar> {
106        mpsc::channel(1).1
107    }
108
109    fn new_transactions_listener_for(
110        &self,
111        _kind: TransactionListenerKind,
112    ) -> Receiver<NewTransactionEvent<Self::Transaction>> {
113        mpsc::channel(1).1
114    }
115
116    fn pooled_transaction_hashes(&self) -> Vec<TxHash> {
117        vec![]
118    }
119
120    fn pooled_transaction_hashes_max(&self, _max: usize) -> Vec<TxHash> {
121        vec![]
122    }
123
124    fn pooled_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
125        vec![]
126    }
127
128    fn pooled_transactions_max(
129        &self,
130        _max: usize,
131    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
132        vec![]
133    }
134
135    fn get_pooled_transaction_elements(
136        &self,
137        _tx_hashes: Vec<TxHash>,
138        _limit: GetPooledTransactionLimit,
139    ) -> Vec<<Self::Transaction as PoolTransaction>::Pooled> {
140        vec![]
141    }
142
143    fn get_pooled_transaction_element(
144        &self,
145        _tx_hash: TxHash,
146    ) -> Option<Recovered<<Self::Transaction as PoolTransaction>::Pooled>> {
147        None
148    }
149
150    fn best_transactions(
151        &self,
152    ) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>> {
153        Box::new(std::iter::empty())
154    }
155
156    fn best_transactions_with_attributes(
157        &self,
158        _: BestTransactionsAttributes,
159    ) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>> {
160        Box::new(std::iter::empty())
161    }
162
163    fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
164        vec![]
165    }
166
167    fn pending_transactions_max(
168        &self,
169        _max: usize,
170    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
171        vec![]
172    }
173
174    fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
175        vec![]
176    }
177
178    fn all_transactions(&self) -> AllPoolTransactions<Self::Transaction> {
179        AllPoolTransactions::default()
180    }
181
182    fn remove_transactions(
183        &self,
184        _hashes: Vec<TxHash>,
185    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
186        vec![]
187    }
188
189    fn remove_transactions_and_descendants(
190        &self,
191        _hashes: Vec<TxHash>,
192    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
193        vec![]
194    }
195
196    fn remove_transactions_by_sender(
197        &self,
198        _sender: Address,
199    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
200        vec![]
201    }
202
203    fn retain_unknown<A>(&self, _announcement: &mut A)
204    where
205        A: HandleMempoolData,
206    {
207    }
208
209    fn get(&self, _tx_hash: &TxHash) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
210        None
211    }
212
213    fn get_all(&self, _txs: Vec<TxHash>) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
214        vec![]
215    }
216
217    fn on_propagated(&self, _txs: PropagatedTransactions) {}
218
219    fn get_transactions_by_sender(
220        &self,
221        _sender: Address,
222    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
223        vec![]
224    }
225
226    fn get_pending_transactions_with_predicate(
227        &self,
228        _predicate: impl FnMut(&ValidPoolTransaction<Self::Transaction>) -> bool,
229    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
230        vec![]
231    }
232
233    fn get_pending_transactions_by_sender(
234        &self,
235        _sender: Address,
236    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
237        vec![]
238    }
239
240    fn get_queued_transactions_by_sender(
241        &self,
242        _sender: Address,
243    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
244        vec![]
245    }
246
247    fn get_highest_transaction_by_sender(
248        &self,
249        _sender: Address,
250    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
251        None
252    }
253
254    fn get_highest_consecutive_transaction_by_sender(
255        &self,
256        _sender: Address,
257        _on_chain_nonce: u64,
258    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
259        None
260    }
261
262    fn get_transaction_by_sender_and_nonce(
263        &self,
264        _sender: Address,
265        _nonce: u64,
266    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
267        None
268    }
269
270    fn get_transactions_by_origin(
271        &self,
272        _origin: TransactionOrigin,
273    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
274        vec![]
275    }
276
277    fn get_pending_transactions_by_origin(
278        &self,
279        _origin: TransactionOrigin,
280    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
281        vec![]
282    }
283
284    fn unique_senders(&self) -> HashSet<Address> {
285        Default::default()
286    }
287
288    fn get_blob(
289        &self,
290        _tx_hash: TxHash,
291    ) -> Result<Option<Arc<BlobTransactionSidecar>>, BlobStoreError> {
292        Ok(None)
293    }
294
295    fn get_all_blobs(
296        &self,
297        _tx_hashes: Vec<TxHash>,
298    ) -> Result<Vec<(TxHash, Arc<BlobTransactionSidecar>)>, BlobStoreError> {
299        Ok(vec![])
300    }
301
302    fn get_all_blobs_exact(
303        &self,
304        tx_hashes: Vec<TxHash>,
305    ) -> Result<Vec<Arc<BlobTransactionSidecar>>, BlobStoreError> {
306        if tx_hashes.is_empty() {
307            return Ok(vec![])
308        }
309        Err(BlobStoreError::MissingSidecar(tx_hashes[0]))
310    }
311
312    fn get_blobs_for_versioned_hashes(
313        &self,
314        versioned_hashes: &[B256],
315    ) -> Result<Vec<Option<BlobAndProofV1>>, BlobStoreError> {
316        Ok(vec![None; versioned_hashes.len()])
317    }
318}
319
320/// A [`TransactionValidator`] that does nothing.
321#[derive(Debug, Clone)]
322#[non_exhaustive]
323pub struct MockTransactionValidator<T> {
324    propagate_local: bool,
325    _marker: PhantomData<T>,
326}
327
328impl<T: EthPoolTransaction> TransactionValidator for MockTransactionValidator<T> {
329    type Transaction = T;
330
331    async fn validate_transaction(
332        &self,
333        origin: TransactionOrigin,
334        mut transaction: Self::Transaction,
335    ) -> TransactionValidationOutcome<Self::Transaction> {
336        let maybe_sidecar = transaction.take_blob().maybe_sidecar().cloned();
337        // we return `balance: U256::MAX` to simulate a valid transaction which will never go into
338        // overdraft
339        TransactionValidationOutcome::Valid {
340            balance: U256::MAX,
341            state_nonce: 0,
342            transaction: ValidTransaction::new(transaction, maybe_sidecar),
343            propagate: match origin {
344                TransactionOrigin::External => true,
345                TransactionOrigin::Local => self.propagate_local,
346                TransactionOrigin::Private => false,
347            },
348        }
349    }
350}
351
352impl<T> MockTransactionValidator<T> {
353    /// Creates a new [`MockTransactionValidator`] that does not allow local transactions to be
354    /// propagated.
355    pub fn no_propagate_local() -> Self {
356        Self { propagate_local: false, _marker: Default::default() }
357    }
358}
359
360impl<T> Default for MockTransactionValidator<T> {
361    fn default() -> Self {
362        Self { propagate_local: true, _marker: Default::default() }
363    }
364}
365
366/// An error that contains the transaction that failed to be inserted into the noop pool.
367#[derive(Debug, Clone, thiserror::Error)]
368#[error("can't insert transaction into the noop pool that does nothing")]
369pub struct NoopInsertError {
370    tx: EthPooledTransaction,
371}
372
373impl NoopInsertError {
374    const fn new(tx: EthPooledTransaction) -> Self {
375        Self { tx }
376    }
377
378    /// Returns the transaction that failed to be inserted.
379    pub fn into_inner(self) -> EthPooledTransaction {
380        self.tx
381    }
382}