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