Skip to main content

reth_provider/test_utils/
mock.rs

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