Skip to main content

reth_provider/test_utils/
mock.rs

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