reth_provider/test_utils/
mock.rs

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