1use crate::{
2 traits::{BlockSource, ReceiptProvider},
3 AccountReader, BalProvider, BalStoreHandle, BlockHashReader, BlockIdReader, BlockNumReader,
4 BlockReader, BlockReaderIdExt, ChainSpecProvider, ChangeSetReader, HeaderProvider,
5 PruneCheckpointReader, RangeEnd, RangeResponse, RangeResult, ReceiptProviderIdExt,
6 StateProvider, StateProviderBox, StateProviderFactory, StateRangeProvider,
7 StateRangeProviderFactory, StateRangeView, StateReader, StateRootProvider, StorageRangeResult,
8 TransactionVariant, TransactionsProvider,
9};
10use alloy_consensus::{
11 constants::EMPTY_ROOT_HASH,
12 transaction::{TransactionMeta, TxHashRef},
13 BlockHeader,
14};
15use alloy_eips::{BlockHashOrNumber, BlockId, BlockNumHash, BlockNumberOrTag};
16use alloy_primitives::{
17 keccak256,
18 map::{AddressMap, B256Map, HashMap},
19 Address, BlockHash, BlockNumber, Bytes, StorageKey, StorageValue, TxHash, TxNumber, B256, U256,
20};
21use parking_lot::Mutex;
22use reth_chain_state::{CanonStateNotifications, CanonStateSubscriptions, ExecutedBlock};
23use reth_chainspec::{ChainInfo, EthChainSpec};
24use reth_db::transaction::DbTx;
25use reth_db_api::{
26 mock::{DatabaseMock, TxMock},
27 models::{AccountBeforeTx, StorageSettings, StoredBlockBodyIndices},
28};
29use reth_ethereum_primitives::EthPrimitives;
30use reth_execution_types::{ExecutionOutcome, RecoveredBlockAndExecutionOutput};
31use reth_primitives_traits::{
32 Account, Block, BlockBody, Bytecode, GotExpected, NodePrimitives, RecoveredBlock, SealedHeader,
33 SignerRecoverable, StorageEntry,
34};
35use reth_prune_types::{PruneCheckpoint, PruneModes, PruneSegment};
36use reth_stages_types::{StageCheckpoint, StageId};
37use reth_storage_api::{
38 BlockBodyIndicesProvider, BytecodeReader, DBProvider, DatabaseProviderFactory, DbTxProvider,
39 HashedPostStateProvider, HistoryInfo, HistoryReader, NodePrimitivesProvider,
40 StageCheckpointReader, StateProofProvider, StorageChangeSetReader, StorageRootProvider,
41 StorageSettingsCache,
42};
43use reth_storage_errors::provider::{ConsistentViewError, ProviderError, ProviderResult};
44use reth_trie::{
45 updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof,
46 MultiProofTargets, StorageMultiProof, StorageProof, TrieInput,
47};
48use std::{
49 collections::{BTreeMap, VecDeque},
50 fmt::Debug,
51 ops::{RangeBounds, RangeInclusive},
52 sync::{
53 atomic::{AtomicBool, AtomicUsize, Ordering},
54 Arc,
55 },
56};
57use tokio::sync::broadcast;
58
59#[derive(Debug)]
61pub struct MockEthProvider<T: NodePrimitives = EthPrimitives, ChainSpec = reth_chainspec::ChainSpec>
62{
63 pub blocks: Arc<Mutex<B256Map<T::Block>>>,
65 pub headers: Arc<Mutex<B256Map<<T::Block as Block>::Header>>>,
67 pub receipts: Arc<Mutex<HashMap<BlockNumber, Vec<T::Receipt>>>>,
69 pub accounts: Arc<Mutex<AddressMap<ExtendedAccount>>>,
71 pub chain_spec: Arc<ChainSpec>,
73 pub state_roots: Arc<Mutex<Vec<B256>>>,
75 pub block_body_indices: Arc<Mutex<HashMap<BlockNumber, StoredBlockBodyIndices>>>,
77 stage_checkpoints: Arc<Mutex<HashMap<StageId, StageCheckpoint>>>,
79 pending_block_num_hash: Arc<Mutex<Option<BlockNumHash>>>,
81 pub bal_store: BalStoreHandle,
83 database_provider_available: Arc<AtomicBool>,
85 snap_state_reads_fail: Arc<AtomicBool>,
87 snap_state_range_available: Arc<AtomicBool>,
89 snap_state_range_resolutions: Arc<AtomicUsize>,
91 snap_account_range: Arc<Mutex<MockAccountRange>>,
93 snap_storage_roots: Arc<Mutex<B256Map<B256>>>,
95 snap_storage_ranges: Arc<Mutex<VecDeque<MockStorageRangeOutcome>>>,
97 snap_storage_range_requests: Arc<Mutex<Vec<MockStorageRangeRequest>>>,
99 snap_account_proof: Arc<Mutex<Option<Vec<Bytes>>>>,
101 snap_storage_proof: Arc<Mutex<Option<Vec<Bytes>>>>,
103 tx: TxMock,
104 prune_modes: Arc<PruneModes>,
105}
106
107type MockAccountRange = Option<(Vec<(B256, Account)>, RangeEnd)>;
109#[derive(Debug, Clone)]
111enum MockStorageRangeOutcome {
112 Error,
114 AccountMissing,
116 Found(Vec<(B256, U256)>, RangeEnd),
118}
119type MockStorageRangeRequest = (B256, B256, B256, usize);
121
122impl<T: NodePrimitives, ChainSpec> Clone for MockEthProvider<T, ChainSpec>
123where
124 T::Block: Clone,
125{
126 fn clone(&self) -> Self {
127 Self {
128 blocks: self.blocks.clone(),
129 headers: self.headers.clone(),
130 receipts: self.receipts.clone(),
131 accounts: self.accounts.clone(),
132 chain_spec: self.chain_spec.clone(),
133 state_roots: self.state_roots.clone(),
134 block_body_indices: self.block_body_indices.clone(),
135 stage_checkpoints: self.stage_checkpoints.clone(),
136 pending_block_num_hash: self.pending_block_num_hash.clone(),
137 bal_store: self.bal_store.clone(),
138 database_provider_available: self.database_provider_available.clone(),
139 snap_state_reads_fail: self.snap_state_reads_fail.clone(),
140 snap_state_range_available: self.snap_state_range_available.clone(),
141 snap_state_range_resolutions: self.snap_state_range_resolutions.clone(),
142 snap_account_range: self.snap_account_range.clone(),
143 snap_storage_roots: self.snap_storage_roots.clone(),
144 snap_storage_ranges: self.snap_storage_ranges.clone(),
145 snap_storage_range_requests: self.snap_storage_range_requests.clone(),
146 snap_account_proof: self.snap_account_proof.clone(),
147 snap_storage_proof: self.snap_storage_proof.clone(),
148 tx: self.tx.clone(),
149 prune_modes: self.prune_modes.clone(),
150 }
151 }
152}
153
154impl<T: NodePrimitives> MockEthProvider<T, reth_chainspec::ChainSpec> {
155 pub fn new() -> Self {
157 Self {
158 blocks: Default::default(),
159 headers: Default::default(),
160 receipts: Default::default(),
161 accounts: Default::default(),
162 chain_spec: Arc::new(reth_chainspec::ChainSpecBuilder::mainnet().build()),
163 state_roots: Default::default(),
164 block_body_indices: Default::default(),
165 stage_checkpoints: Default::default(),
166 pending_block_num_hash: Default::default(),
167 bal_store: Default::default(),
168 database_provider_available: Default::default(),
169 snap_state_reads_fail: Default::default(),
170 snap_state_range_available: Default::default(),
171 snap_state_range_resolutions: Default::default(),
172 snap_account_range: Default::default(),
173 snap_storage_roots: Default::default(),
174 snap_storage_ranges: Default::default(),
175 snap_storage_range_requests: Default::default(),
176 snap_account_proof: Default::default(),
177 snap_storage_proof: Default::default(),
178 tx: Default::default(),
179 prune_modes: Default::default(),
180 }
181 }
182}
183
184impl<T: NodePrimitives, ChainSpec> MockEthProvider<T, ChainSpec> {
185 pub fn enable_database_provider(&self) {
187 self.database_provider_available.store(true, Ordering::Relaxed);
188 }
189
190 pub fn set_snap_state_reads_fail(&self, fail: bool) {
192 self.snap_state_reads_fail.store(fail, Ordering::Relaxed);
193 }
194
195 pub fn set_snap_account_range(&self, accounts: Vec<(B256, Account)>, end: RangeEnd) {
197 self.snap_state_range_available.store(true, Ordering::Relaxed);
198 *self.snap_account_range.lock() = Some((accounts, end));
199 }
200
201 pub fn set_snap_storage_root(&self, hashed_address: B256, storage_root: B256) {
203 self.snap_storage_roots.lock().insert(hashed_address, storage_root);
204 }
205
206 pub fn push_snap_storage_range(&self, slots: Vec<(B256, U256)>, end: RangeEnd) {
208 self.snap_state_range_available.store(true, Ordering::Relaxed);
209 self.snap_storage_ranges.lock().push_back(MockStorageRangeOutcome::Found(slots, end));
210 }
211
212 pub fn push_missing_snap_storage_account(&self) {
214 self.snap_state_range_available.store(true, Ordering::Relaxed);
215 self.snap_storage_ranges.lock().push_back(MockStorageRangeOutcome::AccountMissing);
216 }
217
218 pub fn push_unavailable_snap_storage_range(&self) {
220 self.snap_state_range_available.store(true, Ordering::Relaxed);
221 self.snap_storage_ranges.lock().push_back(MockStorageRangeOutcome::Error);
222 }
223
224 pub fn snap_storage_ranges_remaining(&self) -> usize {
226 self.snap_storage_ranges.lock().len()
227 }
228
229 pub fn snap_storage_range_requests(&self) -> Vec<(B256, B256, B256, usize)> {
231 self.snap_storage_range_requests.lock().clone()
232 }
233
234 pub fn snap_state_range_resolutions(&self) -> usize {
236 self.snap_state_range_resolutions.load(Ordering::Relaxed)
237 }
238
239 pub fn set_snap_account_proof(&self, proof: Option<Vec<Bytes>>) {
241 *self.snap_account_proof.lock() = proof;
242 }
243
244 pub fn set_snap_storage_proof(&self, proof: Option<Vec<Bytes>>) {
246 *self.snap_storage_proof.lock() = proof;
247 }
248
249 fn ensure_snap_state_reads_succeed(&self) -> ProviderResult<()> {
250 if self.snap_state_reads_fail.load(Ordering::Relaxed) {
251 return Err(ProviderError::BestBlockNotFound)
252 }
253 Ok(())
254 }
255
256 pub fn add_block(&self, hash: B256, block: T::Block) {
258 self.add_header(hash, block.header().clone());
259 self.blocks.lock().insert(hash, block);
260 }
261
262 pub fn extend_blocks(&self, iter: impl IntoIterator<Item = (B256, T::Block)>) {
264 for (hash, block) in iter {
265 self.add_block(hash, block)
266 }
267 }
268
269 pub fn add_header(&self, hash: B256, header: <T::Block as Block>::Header) {
271 self.headers.lock().insert(hash, header);
272 }
273
274 pub fn extend_headers(
276 &self,
277 iter: impl IntoIterator<Item = (B256, <T::Block as Block>::Header)>,
278 ) {
279 for (hash, header) in iter {
280 self.add_header(hash, header)
281 }
282 }
283
284 pub fn add_account(&self, address: Address, account: ExtendedAccount) {
286 self.accounts.lock().insert(address, account);
287 }
288
289 pub fn extend_accounts(&self, iter: impl IntoIterator<Item = (Address, ExtendedAccount)>) {
291 for (address, account) in iter {
292 self.add_account(address, account)
293 }
294 }
295
296 pub fn add_receipts(&self, block_number: BlockNumber, receipts: Vec<T::Receipt>) {
298 self.receipts.lock().insert(block_number, receipts);
299 }
300
301 pub fn extend_receipts(&self, iter: impl IntoIterator<Item = (BlockNumber, Vec<T::Receipt>)>) {
303 for (block_number, receipts) in iter {
304 self.add_receipts(block_number, receipts);
305 }
306 }
307
308 pub fn add_block_body_indices(
310 &self,
311 block_number: BlockNumber,
312 indices: StoredBlockBodyIndices,
313 ) {
314 self.block_body_indices.lock().insert(block_number, indices);
315 }
316
317 pub fn add_stage_checkpoint(&self, id: StageId, checkpoint: StageCheckpoint) {
319 self.stage_checkpoints.lock().insert(id, checkpoint);
320 }
321
322 pub fn set_pending_block_num_hash(&self, num_hash: Option<BlockNumHash>) {
324 *self.pending_block_num_hash.lock() = num_hash;
325 }
326
327 pub fn add_state_root(&self, state_root: B256) {
329 self.state_roots.lock().push(state_root);
330 }
331
332 pub fn with_chain_spec<C>(self, chain_spec: C) -> MockEthProvider<T, C> {
334 MockEthProvider {
335 blocks: self.blocks,
336 headers: self.headers,
337 receipts: self.receipts,
338 accounts: self.accounts,
339 chain_spec: Arc::new(chain_spec),
340 state_roots: self.state_roots,
341 block_body_indices: self.block_body_indices,
342 stage_checkpoints: self.stage_checkpoints,
343 pending_block_num_hash: self.pending_block_num_hash,
344 bal_store: self.bal_store,
345 database_provider_available: self.database_provider_available,
346 snap_state_reads_fail: self.snap_state_reads_fail,
347 snap_state_range_available: self.snap_state_range_available,
348 snap_state_range_resolutions: self.snap_state_range_resolutions,
349 snap_account_range: self.snap_account_range,
350 snap_storage_roots: self.snap_storage_roots,
351 snap_storage_ranges: self.snap_storage_ranges,
352 snap_storage_range_requests: self.snap_storage_range_requests,
353 snap_account_proof: self.snap_account_proof,
354 snap_storage_proof: self.snap_storage_proof,
355 tx: self.tx,
356 prune_modes: self.prune_modes,
357 }
358 }
359
360 pub fn with_genesis_block(self) -> Self
364 where
365 ChainSpec: EthChainSpec<Header = <T::Block as Block>::Header>,
366 <T::Block as Block>::Body: Default,
367 {
368 let genesis_hash = self.chain_spec.genesis_hash();
369 let genesis_header = self.chain_spec.genesis_header().clone();
370 let genesis_block = T::Block::new(genesis_header, Default::default());
371 self.add_block(genesis_hash, genesis_block);
372 self
373 }
374}
375
376impl Default for MockEthProvider {
377 fn default() -> Self {
378 Self::new()
379 }
380}
381
382impl<T: NodePrimitives, ChainSpec> BalProvider for MockEthProvider<T, ChainSpec> {
383 fn bal_store(&self) -> &BalStoreHandle {
384 &self.bal_store
385 }
386}
387
388impl<T, ChainSpec> StateRangeProviderFactory for MockEthProvider<T, ChainSpec>
389where
390 T: NodePrimitives,
391 T::Block: Clone,
392 ChainSpec: Send + Sync + 'static,
393{
394 fn state_range_provider(&self, _state_root: B256) -> ProviderResult<Option<StateRangeView>> {
395 self.snap_state_range_resolutions.fetch_add(1, Ordering::Relaxed);
396 self.ensure_snap_state_reads_succeed()?;
397 if !self.snap_state_range_available.load(Ordering::Relaxed) {
398 return Ok(None)
399 }
400 Ok(Some(Box::new(self.clone())))
401 }
402}
403
404impl<T: NodePrimitives, ChainSpec> StateRangeProvider for MockEthProvider<T, ChainSpec> {
405 fn account_range(
406 &self,
407 _start: B256,
408 _limit: B256,
409 _response_bytes: usize,
410 ) -> RangeResult<(B256, Account)> {
411 self.ensure_snap_state_reads_succeed()?;
412 let (items, end) =
413 self.snap_account_range.lock().clone().ok_or(ProviderError::BestBlockNotFound)?;
414 Ok(RangeResponse { items, end })
415 }
416
417 fn storage_root_by_hash(&self, hashed_address: B256) -> ProviderResult<B256> {
418 self.ensure_snap_state_reads_succeed()?;
419 self.snap_storage_roots
420 .lock()
421 .get(&hashed_address)
422 .copied()
423 .ok_or(ProviderError::BestBlockNotFound)
424 }
425
426 fn storage_range(
427 &self,
428 hashed_address: B256,
429 start: B256,
430 limit: B256,
431 response_bytes: usize,
432 ) -> StorageRangeResult {
433 self.ensure_snap_state_reads_succeed()?;
434 self.snap_storage_range_requests.lock().push((
435 hashed_address,
436 start,
437 limit,
438 response_bytes,
439 ));
440 let outcome =
441 self.snap_storage_ranges.lock().pop_front().ok_or(ProviderError::BestBlockNotFound)?;
442 match outcome {
443 MockStorageRangeOutcome::Error => Err(ProviderError::BestBlockNotFound),
444 MockStorageRangeOutcome::AccountMissing => Ok(None),
445 MockStorageRangeOutcome::Found(items, end) => Ok(Some(RangeResponse { items, end })),
446 }
447 }
448
449 fn account_range_proof(&self, _keys: &[B256]) -> ProviderResult<Vec<Bytes>> {
450 self.ensure_snap_state_reads_succeed()?;
451 self.snap_account_proof.lock().clone().ok_or(ProviderError::BestBlockNotFound)
452 }
453
454 fn storage_range_proof(
455 &self,
456 _hashed_address: B256,
457 _keys: &[B256],
458 ) -> ProviderResult<Vec<Bytes>> {
459 self.ensure_snap_state_reads_succeed()?;
460 self.snap_storage_proof.lock().clone().ok_or(ProviderError::BestBlockNotFound)
461 }
462}
463
464#[derive(Debug, Clone)]
466pub struct ExtendedAccount {
467 account: Account,
468 bytecode: Option<Bytecode>,
469 storage: HashMap<StorageKey, StorageValue>,
470}
471
472impl ExtendedAccount {
473 pub fn new(nonce: u64, balance: U256) -> Self {
475 Self {
476 account: Account { nonce, balance, ..Default::default() },
477 bytecode: None,
478 storage: Default::default(),
479 }
480 }
481
482 pub fn with_bytecode(mut self, bytecode: Bytes) -> Self {
484 let hash = keccak256(&bytecode);
485 self.account.bytecode_hash = Some(hash);
486 self.bytecode = Some(Bytecode::new_raw(bytecode));
487 self
488 }
489
490 pub fn extend_storage(
493 mut self,
494 storage: impl IntoIterator<Item = (StorageKey, StorageValue)>,
495 ) -> Self {
496 self.storage.extend(storage);
497 self
498 }
499
500 #[cfg(feature = "account-ext")]
502 pub fn with_extension(
503 mut self,
504 extension: impl Into<reth_primitives_traits::AccountExtension>,
505 ) -> Self {
506 self.account.extension = extension.into();
507 self
508 }
509}
510
511impl<T: NodePrimitives, ChainSpec: EthChainSpec + Clone + 'static> DatabaseProviderFactory
512 for MockEthProvider<T, ChainSpec>
513{
514 type DB = DatabaseMock;
515 type Provider = Self;
516 type ProviderRW = Self;
517
518 fn database_provider_ro(&self) -> ProviderResult<Self::Provider> {
519 if self.database_provider_available.load(Ordering::Relaxed) {
520 Ok(self.clone())
521 } else {
522 Err(ConsistentViewError::Syncing { best_block: GotExpected::new(0, 0) }.into())
523 }
524 }
525
526 fn database_provider_rw(&self) -> ProviderResult<Self::ProviderRW> {
527 if self.database_provider_available.load(Ordering::Relaxed) {
528 Ok(self.clone())
529 } else {
530 Err(ConsistentViewError::Syncing { best_block: GotExpected::new(0, 0) }.into())
531 }
532 }
533}
534
535impl<T: NodePrimitives, ChainSpec: EthChainSpec + 'static> HistoryReader
536 for MockEthProvider<T, ChainSpec>
537{
538 fn account_history_info(
539 &self,
540 _address: Address,
541 _block_number: BlockNumber,
542 _lowest_available_block_number: Option<BlockNumber>,
543 ) -> ProviderResult<HistoryInfo> {
544 Ok(HistoryInfo::InPlainState)
545 }
546
547 fn storage_history_info(
548 &self,
549 _address: Address,
550 _storage_key: B256,
551 _block_number: BlockNumber,
552 _lowest_available_block_number: Option<BlockNumber>,
553 ) -> ProviderResult<HistoryInfo> {
554 Ok(HistoryInfo::InPlainState)
555 }
556}
557
558impl<T: NodePrimitives, ChainSpec: EthChainSpec + 'static> DbTxProvider
559 for MockEthProvider<T, ChainSpec>
560{
561 type Tx = TxMock;
562
563 fn tx(&self) -> &Self::Tx {
564 &self.tx
565 }
566}
567
568impl<T: NodePrimitives, ChainSpec: EthChainSpec + 'static> DBProvider
569 for MockEthProvider<T, ChainSpec>
570{
571 fn tx_mut(&mut self) -> &mut Self::Tx {
572 &mut self.tx
573 }
574
575 fn into_tx(self) -> Self::Tx {
576 self.tx
577 }
578
579 fn commit(self) -> ProviderResult<()> {
580 Ok(self.tx.commit()?)
581 }
582
583 fn prune_modes_ref(&self) -> &PruneModes {
584 &self.prune_modes
585 }
586}
587
588impl<T: NodePrimitives, ChainSpec: EthChainSpec + Send + Sync + 'static> HeaderProvider
589 for MockEthProvider<T, ChainSpec>
590{
591 type Header = <T::Block as Block>::Header;
592
593 fn header(&self, block_hash: BlockHash) -> ProviderResult<Option<Self::Header>> {
594 let lock = self.headers.lock();
595 Ok(lock.get(&block_hash).cloned())
596 }
597
598 fn header_by_number(&self, num: u64) -> ProviderResult<Option<Self::Header>> {
599 let lock = self.headers.lock();
600 Ok(lock.values().find(|h| h.number() == num).cloned())
601 }
602
603 fn headers_range(
604 &self,
605 range: impl RangeBounds<BlockNumber>,
606 ) -> ProviderResult<Vec<Self::Header>> {
607 let lock = self.headers.lock();
608
609 let mut headers: Vec<_> =
610 lock.values().filter(|header| range.contains(&header.number())).cloned().collect();
611 headers.sort_by_key(|header| header.number());
612
613 Ok(headers)
614 }
615
616 fn sealed_header(
617 &self,
618 number: BlockNumber,
619 ) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
620 Ok(self.header_by_number(number)?.map(SealedHeader::seal_slow))
621 }
622
623 fn sealed_headers_while(
624 &self,
625 range: impl RangeBounds<BlockNumber>,
626 mut predicate: impl FnMut(&SealedHeader<Self::Header>) -> bool,
627 ) -> ProviderResult<Vec<SealedHeader<Self::Header>>> {
628 Ok(self
629 .headers_range(range)?
630 .into_iter()
631 .map(SealedHeader::seal_slow)
632 .take_while(|h| predicate(h))
633 .collect())
634 }
635}
636
637impl<T, ChainSpec> ChainSpecProvider for MockEthProvider<T, ChainSpec>
638where
639 T: NodePrimitives,
640 ChainSpec: EthChainSpec + 'static + Debug + Send + Sync,
641{
642 type ChainSpec = ChainSpec;
643
644 fn chain_spec(&self) -> Arc<Self::ChainSpec> {
645 self.chain_spec.clone()
646 }
647}
648
649impl<T: NodePrimitives, ChainSpec: EthChainSpec + 'static> TransactionsProvider
650 for MockEthProvider<T, ChainSpec>
651{
652 type Transaction = T::SignedTx;
653
654 fn transaction_id(&self, tx_hash: TxHash) -> ProviderResult<Option<TxNumber>> {
655 let lock = self.blocks.lock();
656 let tx_number = lock
657 .values()
658 .flat_map(|block| block.body().transactions())
659 .position(|tx| *tx.tx_hash() == tx_hash)
660 .map(|pos| pos as TxNumber);
661
662 Ok(tx_number)
663 }
664
665 fn transaction_by_id(&self, id: TxNumber) -> ProviderResult<Option<Self::Transaction>> {
666 let lock = self.blocks.lock();
667 let transaction =
668 lock.values().flat_map(|block| block.body().transactions()).nth(id as usize).cloned();
669
670 Ok(transaction)
671 }
672
673 fn transaction_by_id_unhashed(
674 &self,
675 id: TxNumber,
676 ) -> ProviderResult<Option<Self::Transaction>> {
677 let lock = self.blocks.lock();
678 let transaction =
679 lock.values().flat_map(|block| block.body().transactions()).nth(id as usize).cloned();
680
681 Ok(transaction)
682 }
683
684 fn transaction_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Transaction>> {
685 Ok(self.blocks.lock().iter().find_map(|(_, block)| {
686 block.body().transactions_iter().find(|tx| *tx.tx_hash() == hash).cloned()
687 }))
688 }
689
690 fn transaction_by_hash_with_meta(
691 &self,
692 hash: TxHash,
693 ) -> ProviderResult<Option<(Self::Transaction, TransactionMeta)>> {
694 let lock = self.blocks.lock();
695 for (block_hash, block) in lock.iter() {
696 for (index, tx) in block.body().transactions_iter().enumerate() {
697 if *tx.tx_hash() == hash {
698 let meta = TransactionMeta {
699 tx_hash: hash,
700 index: index as u64,
701 block_hash: *block_hash,
702 block_number: block.header().number(),
703 base_fee: block.header().base_fee_per_gas(),
704 excess_blob_gas: block.header().excess_blob_gas(),
705 timestamp: block.header().timestamp(),
706 };
707 return Ok(Some((tx.clone(), meta)))
708 }
709 }
710 }
711 Ok(None)
712 }
713
714 fn transactions_by_block(
715 &self,
716 id: BlockHashOrNumber,
717 ) -> ProviderResult<Option<Vec<Self::Transaction>>> {
718 Ok(self.block(id)?.map(|b| b.body().clone_transactions()))
719 }
720
721 fn transactions_by_block_range(
722 &self,
723 range: impl RangeBounds<alloy_primitives::BlockNumber>,
724 ) -> ProviderResult<Vec<Vec<Self::Transaction>>> {
725 let mut map = BTreeMap::new();
727 for block in self.blocks.lock().values() {
728 if range.contains(&block.header().number()) {
729 map.insert(block.header().number(), block.body().clone_transactions());
730 }
731 }
732
733 Ok(map.into_values().collect())
734 }
735
736 fn transactions_by_tx_range(
737 &self,
738 range: impl RangeBounds<TxNumber>,
739 ) -> ProviderResult<Vec<Self::Transaction>> {
740 let lock = self.blocks.lock();
741 let transactions = lock
742 .values()
743 .flat_map(|block| block.body().transactions())
744 .enumerate()
745 .filter(|&(tx_number, _)| range.contains(&(tx_number as TxNumber)))
746 .map(|(_, tx)| tx.clone())
747 .collect();
748
749 Ok(transactions)
750 }
751
752 fn senders_by_tx_range(
753 &self,
754 range: impl RangeBounds<TxNumber>,
755 ) -> ProviderResult<Vec<Address>> {
756 let lock = self.blocks.lock();
757 let transactions = lock
758 .values()
759 .flat_map(|block| block.body().transactions())
760 .enumerate()
761 .filter_map(|(tx_number, tx)| {
762 if range.contains(&(tx_number as TxNumber)) {
763 tx.recover_signer().ok()
764 } else {
765 None
766 }
767 })
768 .collect();
769
770 Ok(transactions)
771 }
772
773 fn transaction_sender(&self, id: TxNumber) -> ProviderResult<Option<Address>> {
774 self.transaction_by_id(id).map(|tx_option| tx_option.map(|tx| tx.recover_signer().unwrap()))
775 }
776}
777
778impl<T, ChainSpec> ReceiptProvider for MockEthProvider<T, ChainSpec>
779where
780 T: NodePrimitives,
781 ChainSpec: Send + Sync + 'static,
782{
783 type Receipt = T::Receipt;
784
785 fn receipt(&self, _id: TxNumber) -> ProviderResult<Option<Self::Receipt>> {
786 Ok(None)
787 }
788
789 fn receipt_by_hash(&self, _hash: TxHash) -> ProviderResult<Option<Self::Receipt>> {
790 Ok(None)
791 }
792
793 fn receipts_by_block(
794 &self,
795 block: BlockHashOrNumber,
796 ) -> ProviderResult<Option<Vec<Self::Receipt>>> {
797 let receipts_lock = self.receipts.lock();
798
799 match block {
800 BlockHashOrNumber::Hash(hash) => {
801 let headers_lock = self.headers.lock();
803 if let Some(header) = headers_lock.get(&hash) {
804 Ok(receipts_lock.get(&header.number()).cloned())
805 } else {
806 Ok(None)
807 }
808 }
809 BlockHashOrNumber::Number(number) => Ok(receipts_lock.get(&number).cloned()),
810 }
811 }
812
813 fn receipts_by_tx_range(
814 &self,
815 _range: impl RangeBounds<TxNumber>,
816 ) -> ProviderResult<Vec<Self::Receipt>> {
817 Ok(vec![])
818 }
819
820 fn receipts_by_block_range(
821 &self,
822 block_range: RangeInclusive<BlockNumber>,
823 ) -> ProviderResult<Vec<Vec<Self::Receipt>>> {
824 let receipts_lock = self.receipts.lock();
825 let headers_lock = self.headers.lock();
826
827 let mut result = Vec::new();
828 for block_number in block_range {
829 if headers_lock.values().any(|header| header.number() == block_number) {
831 if let Some(block_receipts) = receipts_lock.get(&block_number) {
832 result.push(block_receipts.clone());
833 } else {
834 result.push(vec![]);
836 }
837 }
838 }
839
840 Ok(result)
841 }
842}
843
844impl<T, ChainSpec> ReceiptProviderIdExt for MockEthProvider<T, ChainSpec>
845where
846 T: NodePrimitives,
847 Self: ReceiptProvider + BlockIdReader,
848{
849}
850
851impl<T: NodePrimitives, ChainSpec: Send + Sync + 'static> BlockHashReader
852 for MockEthProvider<T, ChainSpec>
853{
854 fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>> {
855 let lock = self.headers.lock();
856 let hash =
857 lock.iter().find_map(|(hash, header)| (header.number() == number).then_some(*hash));
858 Ok(hash)
859 }
860
861 fn canonical_hashes_range(
862 &self,
863 start: BlockNumber,
864 end: BlockNumber,
865 ) -> ProviderResult<Vec<B256>> {
866 let lock = self.headers.lock();
867 let mut hashes: Vec<_> =
868 lock.iter().filter(|(_, header)| (start..end).contains(&header.number())).collect();
869
870 hashes.sort_by_key(|(_, header)| header.number());
871
872 Ok(hashes.into_iter().map(|(hash, _)| *hash).collect())
873 }
874}
875
876impl<T: NodePrimitives, ChainSpec: Send + Sync + 'static> BlockNumReader
877 for MockEthProvider<T, ChainSpec>
878{
879 fn chain_info(&self) -> ProviderResult<ChainInfo> {
880 let best_block_number = self.best_block_number()?;
881 let lock = self.headers.lock();
882
883 Ok(lock
884 .iter()
885 .find(|(_, header)| header.number() == best_block_number)
886 .map(|(hash, header)| ChainInfo { best_hash: *hash, best_number: header.number() })
887 .unwrap_or_default())
888 }
889
890 fn best_block_number(&self) -> ProviderResult<BlockNumber> {
891 let lock = self.headers.lock();
892 lock.iter()
893 .max_by_key(|h| h.1.number())
894 .map(|(_, header)| header.number())
895 .ok_or(ProviderError::BestBlockNotFound)
896 }
897
898 fn last_block_number(&self) -> ProviderResult<BlockNumber> {
899 self.best_block_number()
900 }
901
902 fn block_number(&self, hash: B256) -> ProviderResult<Option<alloy_primitives::BlockNumber>> {
903 let lock = self.headers.lock();
904 Ok(lock.get(&hash).map(|header| header.number()))
905 }
906}
907
908impl<T: NodePrimitives, ChainSpec: EthChainSpec + Send + Sync + 'static> BlockIdReader
909 for MockEthProvider<T, ChainSpec>
910{
911 fn pending_block_num_hash(&self) -> ProviderResult<Option<alloy_eips::BlockNumHash>> {
912 Ok(*self.pending_block_num_hash.lock())
913 }
914
915 fn safe_block_num_hash(&self) -> ProviderResult<Option<alloy_eips::BlockNumHash>> {
916 Ok(None)
917 }
918
919 fn finalized_block_num_hash(&self) -> ProviderResult<Option<alloy_eips::BlockNumHash>> {
920 Ok(None)
921 }
922}
923
924impl<T: NodePrimitives, ChainSpec: EthChainSpec + Send + Sync + 'static> BlockReader
926 for MockEthProvider<T, ChainSpec>
927{
928 type Block = T::Block;
929
930 fn find_block_by_hash(
931 &self,
932 hash: B256,
933 _source: BlockSource,
934 ) -> ProviderResult<Option<Self::Block>> {
935 self.block(hash.into())
936 }
937
938 fn block(&self, id: BlockHashOrNumber) -> ProviderResult<Option<Self::Block>> {
939 let lock = self.blocks.lock();
940 match id {
941 BlockHashOrNumber::Hash(hash) => Ok(lock.get(&hash).cloned()),
942 BlockHashOrNumber::Number(num) => {
943 Ok(lock.values().find(|b| b.header().number() == num).cloned())
944 }
945 }
946 }
947
948 fn pending_block(&self) -> ProviderResult<Option<Arc<RecoveredBlock<Self::Block>>>> {
949 Ok(None)
950 }
951
952 fn pending_block_and_receipts(
953 &self,
954 ) -> ProviderResult<Option<RecoveredBlockAndExecutionOutput<Self::Block, T::Receipt>>> {
955 Ok(None)
956 }
957
958 fn recovered_block(
959 &self,
960 id: BlockHashOrNumber,
961 _transaction_kind: TransactionVariant,
962 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
963 self.block(id)?
964 .map(|block| block.try_into_recovered().map_err(ProviderError::other))
965 .transpose()
966 }
967
968 fn sealed_block_with_senders(
969 &self,
970 id: BlockHashOrNumber,
971 transaction_kind: TransactionVariant,
972 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
973 self.recovered_block(id, transaction_kind)
974 }
975
976 fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Self::Block>> {
977 let lock = self.blocks.lock();
978
979 let mut blocks: Vec<_> = lock
980 .values()
981 .filter(|block| range.contains(&block.header().number()))
982 .cloned()
983 .collect();
984 blocks.sort_by_key(|block| block.header().number());
985
986 Ok(blocks)
987 }
988
989 fn block_with_senders_range(
990 &self,
991 _range: RangeInclusive<BlockNumber>,
992 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
993 Ok(vec![])
994 }
995
996 fn recovered_block_range(
997 &self,
998 _range: RangeInclusive<BlockNumber>,
999 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
1000 Ok(vec![])
1001 }
1002
1003 fn block_by_transaction_id(&self, _id: TxNumber) -> ProviderResult<Option<BlockNumber>> {
1004 Ok(None)
1005 }
1006}
1007
1008impl<T, ChainSpec> BlockReaderIdExt for MockEthProvider<T, ChainSpec>
1009where
1010 ChainSpec: EthChainSpec + Send + Sync + 'static,
1011 T: NodePrimitives,
1012{
1013 fn block_by_id(&self, id: BlockId) -> ProviderResult<Option<T::Block>> {
1014 match id {
1015 BlockId::Number(num) => self.block_by_number_or_tag(num),
1016 BlockId::Hash(hash) => self.block_by_hash(hash.block_hash),
1017 }
1018 }
1019
1020 fn sealed_header_by_id(
1021 &self,
1022 id: BlockId,
1023 ) -> ProviderResult<Option<SealedHeader<<T::Block as Block>::Header>>> {
1024 self.header_by_id(id)?.map_or_else(|| Ok(None), |h| Ok(Some(SealedHeader::seal_slow(h))))
1025 }
1026
1027 fn header_by_id(&self, id: BlockId) -> ProviderResult<Option<<T::Block as Block>::Header>> {
1028 match self.block_by_id(id)? {
1029 None => Ok(None),
1030 Some(block) => Ok(Some(block.into_header())),
1031 }
1032 }
1033}
1034
1035impl<T: NodePrimitives, ChainSpec: Send + Sync> AccountReader for MockEthProvider<T, ChainSpec> {
1036 fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>> {
1037 Ok(self.accounts.lock().get(address).cloned().map(|a| a.account))
1038 }
1039}
1040
1041impl<T: NodePrimitives, ChainSpec: Send + Sync> StageCheckpointReader
1042 for MockEthProvider<T, ChainSpec>
1043{
1044 fn get_stage_checkpoint(&self, id: StageId) -> ProviderResult<Option<StageCheckpoint>> {
1045 Ok(self.stage_checkpoints.lock().get(&id).copied())
1046 }
1047
1048 fn get_stage_checkpoint_progress(&self, _id: StageId) -> ProviderResult<Option<Vec<u8>>> {
1049 Ok(None)
1050 }
1051
1052 fn get_all_checkpoints(&self) -> ProviderResult<Vec<(String, StageCheckpoint)>> {
1053 Ok(self
1054 .stage_checkpoints
1055 .lock()
1056 .iter()
1057 .map(|(id, checkpoint)| (id.to_string(), *checkpoint))
1058 .collect())
1059 }
1060}
1061
1062impl<T: NodePrimitives, ChainSpec: Send + Sync> PruneCheckpointReader
1063 for MockEthProvider<T, ChainSpec>
1064{
1065 fn get_prune_checkpoint(
1066 &self,
1067 _segment: PruneSegment,
1068 ) -> ProviderResult<Option<PruneCheckpoint>> {
1069 Ok(None)
1070 }
1071
1072 fn get_prune_checkpoints(&self) -> ProviderResult<Vec<(PruneSegment, PruneCheckpoint)>> {
1073 Ok(vec![])
1074 }
1075}
1076
1077impl<T, ChainSpec> StateRootProvider for MockEthProvider<T, ChainSpec>
1078where
1079 T: NodePrimitives,
1080 ChainSpec: Send + Sync,
1081{
1082 fn state_root(&self, _state: HashedPostState) -> ProviderResult<B256> {
1083 Ok(self.state_roots.lock().pop().unwrap_or_default())
1084 }
1085
1086 fn state_root_from_nodes(&self, _input: TrieInput) -> ProviderResult<B256> {
1087 Ok(self.state_roots.lock().pop().unwrap_or_default())
1088 }
1089
1090 fn state_root_with_updates(
1091 &self,
1092 _state: HashedPostState,
1093 ) -> ProviderResult<(B256, TrieUpdates)> {
1094 let state_root = self.state_roots.lock().pop().unwrap_or_default();
1095 Ok((state_root, Default::default()))
1096 }
1097
1098 fn state_root_from_nodes_with_updates(
1099 &self,
1100 _input: TrieInput,
1101 ) -> ProviderResult<(B256, TrieUpdates)> {
1102 let state_root = self.state_roots.lock().pop().unwrap_or_default();
1103 Ok((state_root, Default::default()))
1104 }
1105}
1106
1107impl<T, ChainSpec> StorageRootProvider for MockEthProvider<T, ChainSpec>
1108where
1109 T: NodePrimitives,
1110 ChainSpec: Send + Sync,
1111{
1112 fn storage_root(
1113 &self,
1114 _address: Address,
1115 _hashed_storage: HashedStorage,
1116 ) -> ProviderResult<B256> {
1117 Ok(EMPTY_ROOT_HASH)
1118 }
1119
1120 fn storage_proof(
1121 &self,
1122 _address: Address,
1123 slot: B256,
1124 _hashed_storage: HashedStorage,
1125 ) -> ProviderResult<reth_trie::StorageProof> {
1126 Ok(StorageProof::new(slot))
1127 }
1128
1129 fn storage_multiproof(
1130 &self,
1131 _address: Address,
1132 _slots: &[B256],
1133 _hashed_storage: HashedStorage,
1134 ) -> ProviderResult<StorageMultiProof> {
1135 Ok(StorageMultiProof::empty())
1136 }
1137}
1138
1139impl<T, ChainSpec> StateProofProvider for MockEthProvider<T, ChainSpec>
1140where
1141 T: NodePrimitives,
1142 ChainSpec: Send + Sync,
1143{
1144 fn proof(
1145 &self,
1146 _input: TrieInput,
1147 address: Address,
1148 _slots: &[B256],
1149 ) -> ProviderResult<AccountProof> {
1150 Ok(AccountProof::new(address))
1151 }
1152
1153 fn multiproof(
1154 &self,
1155 _input: TrieInput,
1156 _targets: MultiProofTargets,
1157 ) -> ProviderResult<MultiProof> {
1158 Ok(MultiProof::default())
1159 }
1160
1161 fn multiproof_v2(
1162 &self,
1163 _input: TrieInput,
1164 _targets: reth_trie::MultiProofTargetsV2,
1165 ) -> ProviderResult<reth_trie::DecodedMultiProofV2> {
1166 Ok(reth_trie::DecodedMultiProofV2::default())
1167 }
1168
1169 fn witness(
1170 &self,
1171 _input: TrieInput,
1172 _target: HashedPostState,
1173 _mode: reth_trie::ExecutionWitnessMode,
1174 ) -> ProviderResult<Vec<Bytes>> {
1175 Ok(Vec::default())
1176 }
1177}
1178
1179impl<T: NodePrimitives, ChainSpec: EthChainSpec + 'static> HashedPostStateProvider
1180 for MockEthProvider<T, ChainSpec>
1181{
1182 fn hashed_post_state(
1183 &self,
1184 _bundle_state: &revm::database::BundleState,
1185 ) -> ProviderResult<HashedPostState> {
1186 Ok(HashedPostState::default())
1187 }
1188}
1189
1190impl<T, ChainSpec> StateProvider for MockEthProvider<T, ChainSpec>
1191where
1192 T: NodePrimitives,
1193 ChainSpec: EthChainSpec + Send + Sync + 'static,
1194{
1195 fn storage(
1196 &self,
1197 account: Address,
1198 storage_key: StorageKey,
1199 ) -> ProviderResult<Option<StorageValue>> {
1200 let lock = self.accounts.lock();
1201 Ok(lock.get(&account).and_then(|account| account.storage.get(&storage_key)).copied())
1202 }
1203}
1204
1205impl<T, ChainSpec> BytecodeReader for MockEthProvider<T, ChainSpec>
1206where
1207 T: NodePrimitives,
1208 ChainSpec: Send + Sync,
1209{
1210 fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult<Option<Bytecode>> {
1211 let lock = self.accounts.lock();
1212 Ok(lock.values().find_map(|account| {
1213 match (account.account.bytecode_hash.as_ref(), account.bytecode.as_ref()) {
1214 (Some(bytecode_hash), Some(bytecode)) if bytecode_hash == code_hash => {
1215 Some(bytecode.clone())
1216 }
1217 _ => None,
1218 }
1219 }))
1220 }
1221}
1222
1223impl<T: NodePrimitives, ChainSpec: Send + Sync> StorageSettingsCache
1224 for MockEthProvider<T, ChainSpec>
1225{
1226 fn cached_storage_settings(&self) -> StorageSettings {
1227 StorageSettings::default()
1228 }
1229
1230 fn set_storage_settings_cache(&self, _settings: StorageSettings) {}
1231}
1232
1233impl<T: NodePrimitives, ChainSpec: EthChainSpec + Send + Sync + 'static> StateProviderFactory
1234 for MockEthProvider<T, ChainSpec>
1235{
1236 type Primitives = T;
1237
1238 fn latest(&self) -> ProviderResult<StateProviderBox> {
1239 self.ensure_snap_state_reads_succeed()?;
1240 Ok(Box::new(self.clone()))
1241 }
1242
1243 fn state_with_block_appended(
1244 &self,
1245 _parent_hash: BlockHash,
1246 _block: ExecutedBlock<T>,
1247 ) -> ProviderResult<StateProviderBox> {
1248 Err(ProviderError::UnsupportedProvider)
1249 }
1250
1251 fn state_by_block_number_or_tag(
1252 &self,
1253 number_or_tag: BlockNumberOrTag,
1254 ) -> ProviderResult<StateProviderBox> {
1255 match number_or_tag {
1256 BlockNumberOrTag::Latest => self.latest(),
1257 BlockNumberOrTag::Finalized => {
1258 let hash =
1260 self.finalized_block_hash()?.ok_or(ProviderError::FinalizedBlockNotFound)?;
1261
1262 self.history_by_block_hash(hash)
1264 }
1265 BlockNumberOrTag::Safe => {
1266 let hash = self.safe_block_hash()?.ok_or(ProviderError::SafeBlockNotFound)?;
1268
1269 self.history_by_block_hash(hash)
1270 }
1271 BlockNumberOrTag::Earliest => {
1272 self.history_by_block_number(self.earliest_block_number()?)
1273 }
1274 BlockNumberOrTag::Pending => self.pending(),
1275 BlockNumberOrTag::Number(num) => self.history_by_block_number(num),
1276 }
1277 }
1278
1279 fn history_by_block_number(&self, _block: BlockNumber) -> ProviderResult<StateProviderBox> {
1280 Ok(Box::new(self.clone()))
1281 }
1282
1283 fn history_by_block_hash(&self, _block: BlockHash) -> ProviderResult<StateProviderBox> {
1284 Ok(Box::new(self.clone()))
1285 }
1286
1287 fn state_by_block_hash(&self, _block: BlockHash) -> ProviderResult<StateProviderBox> {
1288 Ok(Box::new(self.clone()))
1289 }
1290
1291 fn pending(&self) -> ProviderResult<StateProviderBox> {
1292 Ok(Box::new(self.clone()))
1293 }
1294
1295 fn pending_state_by_hash(&self, _block_hash: B256) -> ProviderResult<Option<StateProviderBox>> {
1296 Ok(Some(Box::new(self.clone())))
1297 }
1298
1299 fn maybe_pending(&self) -> ProviderResult<Option<StateProviderBox>> {
1300 Ok(Some(Box::new(self.clone())))
1301 }
1302}
1303
1304impl<T: NodePrimitives, ChainSpec: Send + Sync> BlockBodyIndicesProvider
1305 for MockEthProvider<T, ChainSpec>
1306{
1307 fn block_body_indices(&self, num: u64) -> ProviderResult<Option<StoredBlockBodyIndices>> {
1308 Ok(self.block_body_indices.lock().get(&num).copied())
1309 }
1310 fn block_body_indices_range(
1311 &self,
1312 _range: RangeInclusive<BlockNumber>,
1313 ) -> ProviderResult<Vec<StoredBlockBodyIndices>> {
1314 Ok(vec![])
1315 }
1316}
1317
1318impl<T: NodePrimitives, ChainSpec: Send + Sync> ChangeSetReader for MockEthProvider<T, ChainSpec> {
1319 fn account_block_changeset(
1320 &self,
1321 _block_number: BlockNumber,
1322 ) -> ProviderResult<Vec<AccountBeforeTx>> {
1323 Ok(Vec::default())
1324 }
1325
1326 fn get_account_before_block(
1327 &self,
1328 _block_number: BlockNumber,
1329 _address: Address,
1330 ) -> ProviderResult<Option<AccountBeforeTx>> {
1331 Ok(None)
1332 }
1333
1334 fn account_changesets_range(
1335 &self,
1336 _range: impl core::ops::RangeBounds<BlockNumber>,
1337 ) -> ProviderResult<Vec<(BlockNumber, AccountBeforeTx)>> {
1338 Ok(Vec::default())
1339 }
1340}
1341
1342impl<T: NodePrimitives, ChainSpec: Send + Sync> StorageChangeSetReader
1343 for MockEthProvider<T, ChainSpec>
1344{
1345 fn storage_changeset(
1346 &self,
1347 _block_number: BlockNumber,
1348 ) -> ProviderResult<Vec<(reth_db_api::models::BlockNumberAddress, StorageEntry)>> {
1349 Ok(Vec::default())
1350 }
1351
1352 fn get_storage_before_block(
1353 &self,
1354 _block_number: BlockNumber,
1355 _address: Address,
1356 _storage_key: B256,
1357 ) -> ProviderResult<Option<StorageEntry>> {
1358 Ok(None)
1359 }
1360
1361 fn storage_changesets_range(
1362 &self,
1363 _range: impl RangeBounds<BlockNumber>,
1364 ) -> ProviderResult<Vec<(reth_db_api::models::BlockNumberAddress, StorageEntry)>> {
1365 Ok(Vec::default())
1366 }
1367}
1368
1369impl<T: NodePrimitives, ChainSpec: Send + Sync> StateReader for MockEthProvider<T, ChainSpec> {
1370 type Receipt = T::Receipt;
1371
1372 fn get_state(
1373 &self,
1374 _block: BlockNumber,
1375 ) -> ProviderResult<Option<ExecutionOutcome<Self::Receipt>>> {
1376 Ok(None)
1377 }
1378}
1379
1380impl<T: NodePrimitives, ChainSpec: Send + Sync> CanonStateSubscriptions
1381 for MockEthProvider<T, ChainSpec>
1382{
1383 type Primitives = T;
1384
1385 fn subscribe_to_canonical_state(&self) -> CanonStateNotifications<T> {
1386 broadcast::channel(1).1
1387 }
1388}
1389
1390impl<T: NodePrimitives, ChainSpec: Send + Sync> NodePrimitivesProvider
1391 for MockEthProvider<T, ChainSpec>
1392{
1393 type Primitives = T;
1394}
1395
1396#[cfg(test)]
1397mod tests {
1398 use super::*;
1399 use alloy_consensus::Header;
1400 use alloy_primitives::BlockHash;
1401 use reth_ethereum_primitives::Receipt;
1402
1403 #[test]
1404 fn test_mock_provider_receipts() {
1405 let provider = MockEthProvider::<EthPrimitives>::new();
1406
1407 let block_hash = BlockHash::random();
1408 let block_number = 1u64;
1409 let header = Header { number: block_number, ..Default::default() };
1410
1411 let receipt1 = Receipt { cumulative_gas_used: 21000, success: true, ..Default::default() };
1412 let receipt2 = Receipt { cumulative_gas_used: 42000, success: true, ..Default::default() };
1413 let receipts = vec![receipt1, receipt2];
1414
1415 provider.add_header(block_hash, header);
1416 provider.add_receipts(block_number, receipts.clone());
1417
1418 let result = provider.receipts_by_block(block_hash.into()).unwrap();
1419 assert_eq!(result, Some(receipts.clone()));
1420
1421 let result = provider.receipts_by_block(block_number.into()).unwrap();
1422 assert_eq!(result, Some(receipts.clone()));
1423
1424 let range_result = provider.receipts_by_block_range(1..=1).unwrap();
1425 assert_eq!(range_result, vec![receipts]);
1426
1427 let non_existent = provider.receipts_by_block(BlockHash::random().into()).unwrap();
1428 assert_eq!(non_existent, None);
1429
1430 let empty_range = provider.receipts_by_block_range(10..=20).unwrap();
1431 assert_eq!(empty_range, Vec::<Vec<Receipt>>::new());
1432 }
1433
1434 #[test]
1435 fn test_mock_provider_receipts_multiple_blocks() {
1436 let provider = MockEthProvider::<EthPrimitives>::new();
1437
1438 let block1_hash = BlockHash::random();
1439 let block2_hash = BlockHash::random();
1440 let block1_number = 1u64;
1441 let block2_number = 2u64;
1442
1443 let header1 = Header { number: block1_number, ..Default::default() };
1444 let header2 = Header { number: block2_number, ..Default::default() };
1445
1446 let receipts1 =
1447 vec![Receipt { cumulative_gas_used: 21000, success: true, ..Default::default() }];
1448 let receipts2 =
1449 vec![Receipt { cumulative_gas_used: 42000, success: true, ..Default::default() }];
1450
1451 provider.add_header(block1_hash, header1);
1452 provider.add_header(block2_hash, header2);
1453 provider.add_receipts(block1_number, receipts1.clone());
1454 provider.add_receipts(block2_number, receipts2.clone());
1455
1456 let range_result = provider.receipts_by_block_range(1..=2).unwrap();
1457 assert_eq!(range_result.len(), 2);
1458 assert_eq!(range_result[0], receipts1);
1459 assert_eq!(range_result[1], receipts2);
1460
1461 let partial_range = provider.receipts_by_block_range(1..=1).unwrap();
1462 assert_eq!(partial_range.len(), 1);
1463 assert_eq!(partial_range[0], receipts1);
1464 }
1465}