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