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