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