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