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