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::{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#[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(
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#[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 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 pub fn no_propagate_local() -> Self {
410 Self { propagate_local: false, return_invalid: false, _marker: Default::default() }
411 }
412 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#[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 pub fn into_inner(self) -> T {
438 self.tx
439 }
440}