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