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