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