1use crate::{
2 traits::{BlockSource, ReceiptProvider},
3 AccountReader, BalProvider, BalStoreHandle, BlockHashReader, BlockIdReader, BlockNumReader,
4 BlockReader, BlockReaderIdExt, ChainSpecProvider, ChangeSetReader, HeaderProvider,
5 PruneCheckpointReader, RangeEnd, RangeResponse, RangeResult, ReceiptProviderIdExt,
6 StateProvider, StateProviderBox, StateProviderFactory, StateRangeProvider,
7 StateRangeProviderFactory, StateRangeView, StateReader, StateRootProvider, StorageRangeResult,
8 TransactionVariant, TransactionsProvider,
9};
10use alloy_consensus::{
11 constants::EMPTY_ROOT_HASH,
12 transaction::{TransactionMeta, TxHashRef},
13 BlockHeader,
14};
15use alloy_eips::{BlockHashOrNumber, BlockId, BlockNumHash, BlockNumberOrTag};
16use alloy_primitives::{
17 keccak256,
18 map::{AddressMap, B256Map, HashMap},
19 Address, BlockHash, BlockNumber, Bytes, StorageKey, StorageValue, TxHash, TxNumber, B256, U256,
20};
21use parking_lot::Mutex;
22use reth_chain_state::{CanonStateNotifications, CanonStateSubscriptions};
23use reth_chainspec::{ChainInfo, EthChainSpec};
24use reth_db::transaction::DbTx;
25use reth_db_api::{
26 mock::{DatabaseMock, TxMock},
27 models::{AccountBeforeTx, StorageSettings, StoredBlockBodyIndices},
28};
29use reth_ethereum_primitives::EthPrimitives;
30use reth_execution_types::ExecutionOutcome;
31use reth_primitives_traits::{
32 Account, Block, BlockBody, Bytecode, GotExpected, NodePrimitives, RecoveredBlock, SealedHeader,
33 SignerRecoverable, StorageEntry,
34};
35use reth_prune_types::{PruneCheckpoint, PruneModes, PruneSegment};
36use reth_stages_types::{StageCheckpoint, StageId};
37use reth_storage_api::{
38 BlockBodyIndicesProvider, BytecodeReader, DBProvider, DatabaseProviderFactory, DbTxProvider,
39 HashedPostStateProvider, HistoryInfo, HistoryReader, NodePrimitivesProvider,
40 StageCheckpointReader, StateProofProvider, StorageChangeSetReader, StorageRootProvider,
41 StorageSettingsCache,
42};
43use reth_storage_errors::provider::{ConsistentViewError, ProviderError, ProviderResult};
44use reth_trie::{
45 updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof,
46 MultiProofTargets, StorageMultiProof, StorageProof, TrieInput,
47};
48use std::{
49 collections::{BTreeMap, VecDeque},
50 fmt::Debug,
51 ops::{RangeBounds, RangeInclusive},
52 sync::{
53 atomic::{AtomicBool, AtomicUsize, Ordering},
54 Arc,
55 },
56};
57use tokio::sync::broadcast;
58
59#[derive(Debug)]
61pub struct MockEthProvider<T: NodePrimitives = EthPrimitives, ChainSpec = reth_chainspec::ChainSpec>
62{
63 pub blocks: Arc<Mutex<B256Map<T::Block>>>,
65 pub headers: Arc<Mutex<B256Map<<T::Block as Block>::Header>>>,
67 pub receipts: Arc<Mutex<HashMap<BlockNumber, Vec<T::Receipt>>>>,
69 pub accounts: Arc<Mutex<AddressMap<ExtendedAccount>>>,
71 pub chain_spec: Arc<ChainSpec>,
73 pub state_roots: Arc<Mutex<Vec<B256>>>,
75 pub block_body_indices: Arc<Mutex<HashMap<BlockNumber, StoredBlockBodyIndices>>>,
77 stage_checkpoints: Arc<Mutex<HashMap<StageId, StageCheckpoint>>>,
79 pending_block_num_hash: Arc<Mutex<Option<BlockNumHash>>>,
81 pub bal_store: BalStoreHandle,
83 database_provider_available: Arc<AtomicBool>,
85 snap_state_reads_fail: Arc<AtomicBool>,
87 snap_state_range_available: Arc<AtomicBool>,
89 snap_state_range_resolutions: Arc<AtomicUsize>,
91 snap_account_range: Arc<Mutex<MockAccountRange>>,
93 snap_storage_roots: Arc<Mutex<B256Map<B256>>>,
95 snap_storage_ranges: Arc<Mutex<VecDeque<MockStorageRangeOutcome>>>,
97 snap_storage_range_requests: Arc<Mutex<Vec<MockStorageRangeRequest>>>,
99 snap_account_proof: Arc<Mutex<Option<Vec<Bytes>>>>,
101 snap_storage_proof: Arc<Mutex<Option<Vec<Bytes>>>>,
103 tx: TxMock,
104 prune_modes: Arc<PruneModes>,
105}
106
107type MockAccountRange = Option<(Vec<(B256, Account)>, RangeEnd)>;
109#[derive(Debug, Clone)]
111enum MockStorageRangeOutcome {
112 Error,
114 AccountMissing,
116 Found(Vec<(B256, U256)>, RangeEnd),
118}
119type MockStorageRangeRequest = (B256, B256, B256, usize);
121
122impl<T: NodePrimitives, ChainSpec> Clone for MockEthProvider<T, ChainSpec>
123where
124 T::Block: Clone,
125{
126 fn clone(&self) -> Self {
127 Self {
128 blocks: self.blocks.clone(),
129 headers: self.headers.clone(),
130 receipts: self.receipts.clone(),
131 accounts: self.accounts.clone(),
132 chain_spec: self.chain_spec.clone(),
133 state_roots: self.state_roots.clone(),
134 block_body_indices: self.block_body_indices.clone(),
135 stage_checkpoints: self.stage_checkpoints.clone(),
136 pending_block_num_hash: self.pending_block_num_hash.clone(),
137 bal_store: self.bal_store.clone(),
138 database_provider_available: self.database_provider_available.clone(),
139 snap_state_reads_fail: self.snap_state_reads_fail.clone(),
140 snap_state_range_available: self.snap_state_range_available.clone(),
141 snap_state_range_resolutions: self.snap_state_range_resolutions.clone(),
142 snap_account_range: self.snap_account_range.clone(),
143 snap_storage_roots: self.snap_storage_roots.clone(),
144 snap_storage_ranges: self.snap_storage_ranges.clone(),
145 snap_storage_range_requests: self.snap_storage_range_requests.clone(),
146 snap_account_proof: self.snap_account_proof.clone(),
147 snap_storage_proof: self.snap_storage_proof.clone(),
148 tx: self.tx.clone(),
149 prune_modes: self.prune_modes.clone(),
150 }
151 }
152}
153
154impl<T: NodePrimitives> MockEthProvider<T, reth_chainspec::ChainSpec> {
155 pub fn new() -> Self {
157 Self {
158 blocks: Default::default(),
159 headers: Default::default(),
160 receipts: Default::default(),
161 accounts: Default::default(),
162 chain_spec: Arc::new(reth_chainspec::ChainSpecBuilder::mainnet().build()),
163 state_roots: Default::default(),
164 block_body_indices: Default::default(),
165 stage_checkpoints: Default::default(),
166 pending_block_num_hash: Default::default(),
167 bal_store: Default::default(),
168 database_provider_available: Default::default(),
169 snap_state_reads_fail: Default::default(),
170 snap_state_range_available: Default::default(),
171 snap_state_range_resolutions: Default::default(),
172 snap_account_range: Default::default(),
173 snap_storage_roots: Default::default(),
174 snap_storage_ranges: Default::default(),
175 snap_storage_range_requests: Default::default(),
176 snap_account_proof: Default::default(),
177 snap_storage_proof: Default::default(),
178 tx: Default::default(),
179 prune_modes: Default::default(),
180 }
181 }
182}
183
184impl<T: NodePrimitives, ChainSpec> MockEthProvider<T, ChainSpec> {
185 pub fn enable_database_provider(&self) {
187 self.database_provider_available.store(true, Ordering::Relaxed);
188 }
189
190 pub fn set_snap_state_reads_fail(&self, fail: bool) {
192 self.snap_state_reads_fail.store(fail, Ordering::Relaxed);
193 }
194
195 pub fn set_snap_account_range(&self, accounts: Vec<(B256, Account)>, end: RangeEnd) {
197 self.snap_state_range_available.store(true, Ordering::Relaxed);
198 *self.snap_account_range.lock() = Some((accounts, end));
199 }
200
201 pub fn set_snap_storage_root(&self, hashed_address: B256, storage_root: B256) {
203 self.snap_storage_roots.lock().insert(hashed_address, storage_root);
204 }
205
206 pub fn push_snap_storage_range(&self, slots: Vec<(B256, U256)>, end: RangeEnd) {
208 self.snap_state_range_available.store(true, Ordering::Relaxed);
209 self.snap_storage_ranges.lock().push_back(MockStorageRangeOutcome::Found(slots, end));
210 }
211
212 pub fn push_missing_snap_storage_account(&self) {
214 self.snap_state_range_available.store(true, Ordering::Relaxed);
215 self.snap_storage_ranges.lock().push_back(MockStorageRangeOutcome::AccountMissing);
216 }
217
218 pub fn push_unavailable_snap_storage_range(&self) {
220 self.snap_state_range_available.store(true, Ordering::Relaxed);
221 self.snap_storage_ranges.lock().push_back(MockStorageRangeOutcome::Error);
222 }
223
224 pub fn snap_storage_ranges_remaining(&self) -> usize {
226 self.snap_storage_ranges.lock().len()
227 }
228
229 pub fn snap_storage_range_requests(&self) -> Vec<(B256, B256, B256, usize)> {
231 self.snap_storage_range_requests.lock().clone()
232 }
233
234 pub fn snap_state_range_resolutions(&self) -> usize {
236 self.snap_state_range_resolutions.load(Ordering::Relaxed)
237 }
238
239 pub fn set_snap_account_proof(&self, proof: Option<Vec<Bytes>>) {
241 *self.snap_account_proof.lock() = proof;
242 }
243
244 pub fn set_snap_storage_proof(&self, proof: Option<Vec<Bytes>>) {
246 *self.snap_storage_proof.lock() = proof;
247 }
248
249 fn ensure_snap_state_reads_succeed(&self) -> ProviderResult<()> {
250 if self.snap_state_reads_fail.load(Ordering::Relaxed) {
251 return Err(ProviderError::BestBlockNotFound)
252 }
253 Ok(())
254 }
255
256 pub fn add_block(&self, hash: B256, block: T::Block) {
258 self.add_header(hash, block.header().clone());
259 self.blocks.lock().insert(hash, block);
260 }
261
262 pub fn extend_blocks(&self, iter: impl IntoIterator<Item = (B256, T::Block)>) {
264 for (hash, block) in iter {
265 self.add_block(hash, block)
266 }
267 }
268
269 pub fn add_header(&self, hash: B256, header: <T::Block as Block>::Header) {
271 self.headers.lock().insert(hash, header);
272 }
273
274 pub fn extend_headers(
276 &self,
277 iter: impl IntoIterator<Item = (B256, <T::Block as Block>::Header)>,
278 ) {
279 for (hash, header) in iter {
280 self.add_header(hash, header)
281 }
282 }
283
284 pub fn add_account(&self, address: Address, account: ExtendedAccount) {
286 self.accounts.lock().insert(address, account);
287 }
288
289 pub fn extend_accounts(&self, iter: impl IntoIterator<Item = (Address, ExtendedAccount)>) {
291 for (address, account) in iter {
292 self.add_account(address, account)
293 }
294 }
295
296 pub fn add_receipts(&self, block_number: BlockNumber, receipts: Vec<T::Receipt>) {
298 self.receipts.lock().insert(block_number, receipts);
299 }
300
301 pub fn extend_receipts(&self, iter: impl IntoIterator<Item = (BlockNumber, Vec<T::Receipt>)>) {
303 for (block_number, receipts) in iter {
304 self.add_receipts(block_number, receipts);
305 }
306 }
307
308 pub fn add_block_body_indices(
310 &self,
311 block_number: BlockNumber,
312 indices: StoredBlockBodyIndices,
313 ) {
314 self.block_body_indices.lock().insert(block_number, indices);
315 }
316
317 pub fn add_stage_checkpoint(&self, id: StageId, checkpoint: StageCheckpoint) {
319 self.stage_checkpoints.lock().insert(id, checkpoint);
320 }
321
322 pub fn set_pending_block_num_hash(&self, num_hash: Option<BlockNumHash>) {
324 *self.pending_block_num_hash.lock() = num_hash;
325 }
326
327 pub fn add_state_root(&self, state_root: B256) {
329 self.state_roots.lock().push(state_root);
330 }
331
332 pub fn with_chain_spec<C>(self, chain_spec: C) -> MockEthProvider<T, C> {
334 MockEthProvider {
335 blocks: self.blocks,
336 headers: self.headers,
337 receipts: self.receipts,
338 accounts: self.accounts,
339 chain_spec: Arc::new(chain_spec),
340 state_roots: self.state_roots,
341 block_body_indices: self.block_body_indices,
342 stage_checkpoints: self.stage_checkpoints,
343 pending_block_num_hash: self.pending_block_num_hash,
344 bal_store: self.bal_store,
345 database_provider_available: self.database_provider_available,
346 snap_state_reads_fail: self.snap_state_reads_fail,
347 snap_state_range_available: self.snap_state_range_available,
348 snap_state_range_resolutions: self.snap_state_range_resolutions,
349 snap_account_range: self.snap_account_range,
350 snap_storage_roots: self.snap_storage_roots,
351 snap_storage_ranges: self.snap_storage_ranges,
352 snap_storage_range_requests: self.snap_storage_range_requests,
353 snap_account_proof: self.snap_account_proof,
354 snap_storage_proof: self.snap_storage_proof,
355 tx: self.tx,
356 prune_modes: self.prune_modes,
357 }
358 }
359
360 pub fn with_genesis_block(self) -> Self
364 where
365 ChainSpec: EthChainSpec<Header = <T::Block as Block>::Header>,
366 <T::Block as Block>::Body: Default,
367 {
368 let genesis_hash = self.chain_spec.genesis_hash();
369 let genesis_header = self.chain_spec.genesis_header().clone();
370 let genesis_block = T::Block::new(genesis_header, Default::default());
371 self.add_block(genesis_hash, genesis_block);
372 self
373 }
374}
375
376impl Default for MockEthProvider {
377 fn default() -> Self {
378 Self::new()
379 }
380}
381
382impl<T: NodePrimitives, ChainSpec> BalProvider for MockEthProvider<T, ChainSpec> {
383 fn bal_store(&self) -> &BalStoreHandle {
384 &self.bal_store
385 }
386}
387
388impl<T, ChainSpec> StateRangeProviderFactory for MockEthProvider<T, ChainSpec>
389where
390 T: NodePrimitives,
391 T::Block: Clone,
392 ChainSpec: Send + Sync + 'static,
393{
394 fn state_range_provider(&self, _state_root: B256) -> ProviderResult<Option<StateRangeView>> {
395 self.snap_state_range_resolutions.fetch_add(1, Ordering::Relaxed);
396 self.ensure_snap_state_reads_succeed()?;
397 if !self.snap_state_range_available.load(Ordering::Relaxed) {
398 return Ok(None)
399 }
400 Ok(Some(Box::new(self.clone())))
401 }
402}
403
404impl<T: NodePrimitives, ChainSpec> StateRangeProvider for MockEthProvider<T, ChainSpec> {
405 fn account_range(
406 &self,
407 _start: B256,
408 _limit: B256,
409 _response_bytes: usize,
410 ) -> RangeResult<(B256, Account)> {
411 self.ensure_snap_state_reads_succeed()?;
412 let (items, end) =
413 self.snap_account_range.lock().clone().ok_or(ProviderError::BestBlockNotFound)?;
414 Ok(RangeResponse { items, end })
415 }
416
417 fn storage_root_by_hash(&self, hashed_address: B256) -> ProviderResult<B256> {
418 self.ensure_snap_state_reads_succeed()?;
419 self.snap_storage_roots
420 .lock()
421 .get(&hashed_address)
422 .copied()
423 .ok_or(ProviderError::BestBlockNotFound)
424 }
425
426 fn storage_range(
427 &self,
428 hashed_address: B256,
429 start: B256,
430 limit: B256,
431 response_bytes: usize,
432 ) -> StorageRangeResult {
433 self.ensure_snap_state_reads_succeed()?;
434 self.snap_storage_range_requests.lock().push((
435 hashed_address,
436 start,
437 limit,
438 response_bytes,
439 ));
440 let outcome =
441 self.snap_storage_ranges.lock().pop_front().ok_or(ProviderError::BestBlockNotFound)?;
442 match outcome {
443 MockStorageRangeOutcome::Error => Err(ProviderError::BestBlockNotFound),
444 MockStorageRangeOutcome::AccountMissing => Ok(None),
445 MockStorageRangeOutcome::Found(items, end) => Ok(Some(RangeResponse { items, end })),
446 }
447 }
448
449 fn account_range_proof(&self, _keys: &[B256]) -> ProviderResult<Vec<Bytes>> {
450 self.ensure_snap_state_reads_succeed()?;
451 self.snap_account_proof.lock().clone().ok_or(ProviderError::BestBlockNotFound)
452 }
453
454 fn storage_range_proof(
455 &self,
456 _hashed_address: B256,
457 _keys: &[B256],
458 ) -> ProviderResult<Vec<Bytes>> {
459 self.ensure_snap_state_reads_succeed()?;
460 self.snap_storage_proof.lock().clone().ok_or(ProviderError::BestBlockNotFound)
461 }
462}
463
464#[derive(Debug, Clone)]
466pub struct ExtendedAccount {
467 account: Account,
468 bytecode: Option<Bytecode>,
469 storage: HashMap<StorageKey, StorageValue>,
470}
471
472impl ExtendedAccount {
473 pub fn new(nonce: u64, balance: U256) -> Self {
475 Self {
476 account: Account { nonce, balance, bytecode_hash: None },
477 bytecode: None,
478 storage: Default::default(),
479 }
480 }
481
482 pub fn with_bytecode(mut self, bytecode: Bytes) -> Self {
484 let hash = keccak256(&bytecode);
485 self.account.bytecode_hash = Some(hash);
486 self.bytecode = Some(Bytecode::new_raw(bytecode));
487 self
488 }
489
490 pub fn extend_storage(
493 mut self,
494 storage: impl IntoIterator<Item = (StorageKey, StorageValue)>,
495 ) -> Self {
496 self.storage.extend(storage);
497 self
498 }
499}
500
501impl<T: NodePrimitives, ChainSpec: EthChainSpec + Clone + 'static> DatabaseProviderFactory
502 for MockEthProvider<T, ChainSpec>
503{
504 type DB = DatabaseMock;
505 type Provider = Self;
506 type ProviderRW = Self;
507
508 fn database_provider_ro(&self) -> ProviderResult<Self::Provider> {
509 if self.database_provider_available.load(Ordering::Relaxed) {
510 Ok(self.clone())
511 } else {
512 Err(ConsistentViewError::Syncing { best_block: GotExpected::new(0, 0) }.into())
513 }
514 }
515
516 fn database_provider_rw(&self) -> ProviderResult<Self::ProviderRW> {
517 if self.database_provider_available.load(Ordering::Relaxed) {
518 Ok(self.clone())
519 } else {
520 Err(ConsistentViewError::Syncing { best_block: GotExpected::new(0, 0) }.into())
521 }
522 }
523}
524
525impl<T: NodePrimitives, ChainSpec: EthChainSpec + 'static> HistoryReader
526 for MockEthProvider<T, ChainSpec>
527{
528 fn account_history_info(
529 &self,
530 _address: Address,
531 _block_number: BlockNumber,
532 _lowest_available_block_number: Option<BlockNumber>,
533 ) -> ProviderResult<HistoryInfo> {
534 Ok(HistoryInfo::InPlainState)
535 }
536
537 fn storage_history_info(
538 &self,
539 _address: Address,
540 _storage_key: B256,
541 _block_number: BlockNumber,
542 _lowest_available_block_number: Option<BlockNumber>,
543 ) -> ProviderResult<HistoryInfo> {
544 Ok(HistoryInfo::InPlainState)
545 }
546}
547
548impl<T: NodePrimitives, ChainSpec: EthChainSpec + 'static> DbTxProvider
549 for MockEthProvider<T, ChainSpec>
550{
551 type Tx = TxMock;
552
553 fn tx(&self) -> &Self::Tx {
554 &self.tx
555 }
556}
557
558impl<T: NodePrimitives, ChainSpec: EthChainSpec + 'static> DBProvider
559 for MockEthProvider<T, ChainSpec>
560{
561 fn tx_mut(&mut self) -> &mut Self::Tx {
562 &mut self.tx
563 }
564
565 fn into_tx(self) -> Self::Tx {
566 self.tx
567 }
568
569 fn commit(self) -> ProviderResult<()> {
570 Ok(self.tx.commit()?)
571 }
572
573 fn prune_modes_ref(&self) -> &PruneModes {
574 &self.prune_modes
575 }
576}
577
578impl<T: NodePrimitives, ChainSpec: EthChainSpec + Send + Sync + 'static> HeaderProvider
579 for MockEthProvider<T, ChainSpec>
580{
581 type Header = <T::Block as Block>::Header;
582
583 fn header(&self, block_hash: BlockHash) -> ProviderResult<Option<Self::Header>> {
584 let lock = self.headers.lock();
585 Ok(lock.get(&block_hash).cloned())
586 }
587
588 fn header_by_number(&self, num: u64) -> ProviderResult<Option<Self::Header>> {
589 let lock = self.headers.lock();
590 Ok(lock.values().find(|h| h.number() == num).cloned())
591 }
592
593 fn headers_range(
594 &self,
595 range: impl RangeBounds<BlockNumber>,
596 ) -> ProviderResult<Vec<Self::Header>> {
597 let lock = self.headers.lock();
598
599 let mut headers: Vec<_> =
600 lock.values().filter(|header| range.contains(&header.number())).cloned().collect();
601 headers.sort_by_key(|header| header.number());
602
603 Ok(headers)
604 }
605
606 fn sealed_header(
607 &self,
608 number: BlockNumber,
609 ) -> ProviderResult<Option<SealedHeader<Self::Header>>> {
610 Ok(self.header_by_number(number)?.map(SealedHeader::seal_slow))
611 }
612
613 fn sealed_headers_while(
614 &self,
615 range: impl RangeBounds<BlockNumber>,
616 mut predicate: impl FnMut(&SealedHeader<Self::Header>) -> bool,
617 ) -> ProviderResult<Vec<SealedHeader<Self::Header>>> {
618 Ok(self
619 .headers_range(range)?
620 .into_iter()
621 .map(SealedHeader::seal_slow)
622 .take_while(|h| predicate(h))
623 .collect())
624 }
625}
626
627impl<T, ChainSpec> ChainSpecProvider for MockEthProvider<T, ChainSpec>
628where
629 T: NodePrimitives,
630 ChainSpec: EthChainSpec + 'static + Debug + Send + Sync,
631{
632 type ChainSpec = ChainSpec;
633
634 fn chain_spec(&self) -> Arc<Self::ChainSpec> {
635 self.chain_spec.clone()
636 }
637}
638
639impl<T: NodePrimitives, ChainSpec: EthChainSpec + 'static> TransactionsProvider
640 for MockEthProvider<T, ChainSpec>
641{
642 type Transaction = T::SignedTx;
643
644 fn transaction_id(&self, tx_hash: TxHash) -> ProviderResult<Option<TxNumber>> {
645 let lock = self.blocks.lock();
646 let tx_number = lock
647 .values()
648 .flat_map(|block| block.body().transactions())
649 .position(|tx| *tx.tx_hash() == tx_hash)
650 .map(|pos| pos as TxNumber);
651
652 Ok(tx_number)
653 }
654
655 fn transaction_by_id(&self, id: TxNumber) -> ProviderResult<Option<Self::Transaction>> {
656 let lock = self.blocks.lock();
657 let transaction =
658 lock.values().flat_map(|block| block.body().transactions()).nth(id as usize).cloned();
659
660 Ok(transaction)
661 }
662
663 fn transaction_by_id_unhashed(
664 &self,
665 id: TxNumber,
666 ) -> ProviderResult<Option<Self::Transaction>> {
667 let lock = self.blocks.lock();
668 let transaction =
669 lock.values().flat_map(|block| block.body().transactions()).nth(id as usize).cloned();
670
671 Ok(transaction)
672 }
673
674 fn transaction_by_hash(&self, hash: TxHash) -> ProviderResult<Option<Self::Transaction>> {
675 Ok(self.blocks.lock().iter().find_map(|(_, block)| {
676 block.body().transactions_iter().find(|tx| *tx.tx_hash() == hash).cloned()
677 }))
678 }
679
680 fn transaction_by_hash_with_meta(
681 &self,
682 hash: TxHash,
683 ) -> ProviderResult<Option<(Self::Transaction, TransactionMeta)>> {
684 let lock = self.blocks.lock();
685 for (block_hash, block) in lock.iter() {
686 for (index, tx) in block.body().transactions_iter().enumerate() {
687 if *tx.tx_hash() == hash {
688 let meta = TransactionMeta {
689 tx_hash: hash,
690 index: index as u64,
691 block_hash: *block_hash,
692 block_number: block.header().number(),
693 base_fee: block.header().base_fee_per_gas(),
694 excess_blob_gas: block.header().excess_blob_gas(),
695 timestamp: block.header().timestamp(),
696 };
697 return Ok(Some((tx.clone(), meta)))
698 }
699 }
700 }
701 Ok(None)
702 }
703
704 fn transactions_by_block(
705 &self,
706 id: BlockHashOrNumber,
707 ) -> ProviderResult<Option<Vec<Self::Transaction>>> {
708 Ok(self.block(id)?.map(|b| b.body().clone_transactions()))
709 }
710
711 fn transactions_by_block_range(
712 &self,
713 range: impl RangeBounds<alloy_primitives::BlockNumber>,
714 ) -> ProviderResult<Vec<Vec<Self::Transaction>>> {
715 let mut map = BTreeMap::new();
717 for block in self.blocks.lock().values() {
718 if range.contains(&block.header().number()) {
719 map.insert(block.header().number(), block.body().clone_transactions());
720 }
721 }
722
723 Ok(map.into_values().collect())
724 }
725
726 fn transactions_by_tx_range(
727 &self,
728 range: impl RangeBounds<TxNumber>,
729 ) -> ProviderResult<Vec<Self::Transaction>> {
730 let lock = self.blocks.lock();
731 let transactions = lock
732 .values()
733 .flat_map(|block| block.body().transactions())
734 .enumerate()
735 .filter(|&(tx_number, _)| range.contains(&(tx_number as TxNumber)))
736 .map(|(_, tx)| tx.clone())
737 .collect();
738
739 Ok(transactions)
740 }
741
742 fn senders_by_tx_range(
743 &self,
744 range: impl RangeBounds<TxNumber>,
745 ) -> ProviderResult<Vec<Address>> {
746 let lock = self.blocks.lock();
747 let transactions = lock
748 .values()
749 .flat_map(|block| block.body().transactions())
750 .enumerate()
751 .filter_map(|(tx_number, tx)| {
752 if range.contains(&(tx_number as TxNumber)) {
753 tx.recover_signer().ok()
754 } else {
755 None
756 }
757 })
758 .collect();
759
760 Ok(transactions)
761 }
762
763 fn transaction_sender(&self, id: TxNumber) -> ProviderResult<Option<Address>> {
764 self.transaction_by_id(id).map(|tx_option| tx_option.map(|tx| tx.recover_signer().unwrap()))
765 }
766}
767
768impl<T, ChainSpec> ReceiptProvider for MockEthProvider<T, ChainSpec>
769where
770 T: NodePrimitives,
771 ChainSpec: Send + Sync + 'static,
772{
773 type Receipt = T::Receipt;
774
775 fn receipt(&self, _id: TxNumber) -> ProviderResult<Option<Self::Receipt>> {
776 Ok(None)
777 }
778
779 fn receipt_by_hash(&self, _hash: TxHash) -> ProviderResult<Option<Self::Receipt>> {
780 Ok(None)
781 }
782
783 fn receipts_by_block(
784 &self,
785 block: BlockHashOrNumber,
786 ) -> ProviderResult<Option<Vec<Self::Receipt>>> {
787 let receipts_lock = self.receipts.lock();
788
789 match block {
790 BlockHashOrNumber::Hash(hash) => {
791 let headers_lock = self.headers.lock();
793 if let Some(header) = headers_lock.get(&hash) {
794 Ok(receipts_lock.get(&header.number()).cloned())
795 } else {
796 Ok(None)
797 }
798 }
799 BlockHashOrNumber::Number(number) => Ok(receipts_lock.get(&number).cloned()),
800 }
801 }
802
803 fn receipts_by_tx_range(
804 &self,
805 _range: impl RangeBounds<TxNumber>,
806 ) -> ProviderResult<Vec<Self::Receipt>> {
807 Ok(vec![])
808 }
809
810 fn receipts_by_block_range(
811 &self,
812 block_range: RangeInclusive<BlockNumber>,
813 ) -> ProviderResult<Vec<Vec<Self::Receipt>>> {
814 let receipts_lock = self.receipts.lock();
815 let headers_lock = self.headers.lock();
816
817 let mut result = Vec::new();
818 for block_number in block_range {
819 if headers_lock.values().any(|header| header.number() == block_number) {
821 if let Some(block_receipts) = receipts_lock.get(&block_number) {
822 result.push(block_receipts.clone());
823 } else {
824 result.push(vec![]);
826 }
827 }
828 }
829
830 Ok(result)
831 }
832}
833
834impl<T, ChainSpec> ReceiptProviderIdExt for MockEthProvider<T, ChainSpec>
835where
836 T: NodePrimitives,
837 Self: ReceiptProvider + BlockIdReader,
838{
839}
840
841impl<T: NodePrimitives, ChainSpec: Send + Sync + 'static> BlockHashReader
842 for MockEthProvider<T, ChainSpec>
843{
844 fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>> {
845 let lock = self.headers.lock();
846 let hash =
847 lock.iter().find_map(|(hash, header)| (header.number() == number).then_some(*hash));
848 Ok(hash)
849 }
850
851 fn canonical_hashes_range(
852 &self,
853 start: BlockNumber,
854 end: BlockNumber,
855 ) -> ProviderResult<Vec<B256>> {
856 let lock = self.headers.lock();
857 let mut hashes: Vec<_> =
858 lock.iter().filter(|(_, header)| (start..end).contains(&header.number())).collect();
859
860 hashes.sort_by_key(|(_, header)| header.number());
861
862 Ok(hashes.into_iter().map(|(hash, _)| *hash).collect())
863 }
864}
865
866impl<T: NodePrimitives, ChainSpec: Send + Sync + 'static> BlockNumReader
867 for MockEthProvider<T, ChainSpec>
868{
869 fn chain_info(&self) -> ProviderResult<ChainInfo> {
870 let best_block_number = self.best_block_number()?;
871 let lock = self.headers.lock();
872
873 Ok(lock
874 .iter()
875 .find(|(_, header)| header.number() == best_block_number)
876 .map(|(hash, header)| ChainInfo { best_hash: *hash, best_number: header.number() })
877 .unwrap_or_default())
878 }
879
880 fn best_block_number(&self) -> ProviderResult<BlockNumber> {
881 let lock = self.headers.lock();
882 lock.iter()
883 .max_by_key(|h| h.1.number())
884 .map(|(_, header)| header.number())
885 .ok_or(ProviderError::BestBlockNotFound)
886 }
887
888 fn last_block_number(&self) -> ProviderResult<BlockNumber> {
889 self.best_block_number()
890 }
891
892 fn block_number(&self, hash: B256) -> ProviderResult<Option<alloy_primitives::BlockNumber>> {
893 let lock = self.headers.lock();
894 Ok(lock.get(&hash).map(|header| header.number()))
895 }
896}
897
898impl<T: NodePrimitives, ChainSpec: EthChainSpec + Send + Sync + 'static> BlockIdReader
899 for MockEthProvider<T, ChainSpec>
900{
901 fn pending_block_num_hash(&self) -> ProviderResult<Option<alloy_eips::BlockNumHash>> {
902 Ok(*self.pending_block_num_hash.lock())
903 }
904
905 fn safe_block_num_hash(&self) -> ProviderResult<Option<alloy_eips::BlockNumHash>> {
906 Ok(None)
907 }
908
909 fn finalized_block_num_hash(&self) -> ProviderResult<Option<alloy_eips::BlockNumHash>> {
910 Ok(None)
911 }
912}
913
914impl<T: NodePrimitives, ChainSpec: EthChainSpec + Send + Sync + 'static> BlockReader
916 for MockEthProvider<T, ChainSpec>
917{
918 type Block = T::Block;
919
920 fn find_block_by_hash(
921 &self,
922 hash: B256,
923 _source: BlockSource,
924 ) -> ProviderResult<Option<Self::Block>> {
925 self.block(hash.into())
926 }
927
928 fn block(&self, id: BlockHashOrNumber) -> ProviderResult<Option<Self::Block>> {
929 let lock = self.blocks.lock();
930 match id {
931 BlockHashOrNumber::Hash(hash) => Ok(lock.get(&hash).cloned()),
932 BlockHashOrNumber::Number(num) => {
933 Ok(lock.values().find(|b| b.header().number() == num).cloned())
934 }
935 }
936 }
937
938 fn pending_block(&self) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
939 Ok(None)
940 }
941
942 fn pending_block_and_receipts(
943 &self,
944 ) -> ProviderResult<Option<(RecoveredBlock<Self::Block>, Vec<T::Receipt>)>> {
945 Ok(None)
946 }
947
948 fn recovered_block(
949 &self,
950 _id: BlockHashOrNumber,
951 _transaction_kind: TransactionVariant,
952 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
953 Ok(None)
954 }
955
956 fn sealed_block_with_senders(
957 &self,
958 _id: BlockHashOrNumber,
959 _transaction_kind: TransactionVariant,
960 ) -> ProviderResult<Option<RecoveredBlock<Self::Block>>> {
961 Ok(None)
962 }
963
964 fn block_range(&self, range: RangeInclusive<BlockNumber>) -> ProviderResult<Vec<Self::Block>> {
965 let lock = self.blocks.lock();
966
967 let mut blocks: Vec<_> = lock
968 .values()
969 .filter(|block| range.contains(&block.header().number()))
970 .cloned()
971 .collect();
972 blocks.sort_by_key(|block| block.header().number());
973
974 Ok(blocks)
975 }
976
977 fn block_with_senders_range(
978 &self,
979 _range: RangeInclusive<BlockNumber>,
980 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
981 Ok(vec![])
982 }
983
984 fn recovered_block_range(
985 &self,
986 _range: RangeInclusive<BlockNumber>,
987 ) -> ProviderResult<Vec<RecoveredBlock<Self::Block>>> {
988 Ok(vec![])
989 }
990
991 fn block_by_transaction_id(&self, _id: TxNumber) -> ProviderResult<Option<BlockNumber>> {
992 Ok(None)
993 }
994}
995
996impl<T, ChainSpec> BlockReaderIdExt for MockEthProvider<T, ChainSpec>
997where
998 ChainSpec: EthChainSpec + Send + Sync + 'static,
999 T: NodePrimitives,
1000{
1001 fn block_by_id(&self, id: BlockId) -> ProviderResult<Option<T::Block>> {
1002 match id {
1003 BlockId::Number(num) => self.block_by_number_or_tag(num),
1004 BlockId::Hash(hash) => self.block_by_hash(hash.block_hash),
1005 }
1006 }
1007
1008 fn sealed_header_by_id(
1009 &self,
1010 id: BlockId,
1011 ) -> ProviderResult<Option<SealedHeader<<T::Block as Block>::Header>>> {
1012 self.header_by_id(id)?.map_or_else(|| Ok(None), |h| Ok(Some(SealedHeader::seal_slow(h))))
1013 }
1014
1015 fn header_by_id(&self, id: BlockId) -> ProviderResult<Option<<T::Block as Block>::Header>> {
1016 match self.block_by_id(id)? {
1017 None => Ok(None),
1018 Some(block) => Ok(Some(block.into_header())),
1019 }
1020 }
1021}
1022
1023impl<T: NodePrimitives, ChainSpec: Send + Sync> AccountReader for MockEthProvider<T, ChainSpec> {
1024 fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>> {
1025 Ok(self.accounts.lock().get(address).cloned().map(|a| a.account))
1026 }
1027}
1028
1029impl<T: NodePrimitives, ChainSpec: Send + Sync> StageCheckpointReader
1030 for MockEthProvider<T, ChainSpec>
1031{
1032 fn get_stage_checkpoint(&self, id: StageId) -> ProviderResult<Option<StageCheckpoint>> {
1033 Ok(self.stage_checkpoints.lock().get(&id).copied())
1034 }
1035
1036 fn get_stage_checkpoint_progress(&self, _id: StageId) -> ProviderResult<Option<Vec<u8>>> {
1037 Ok(None)
1038 }
1039
1040 fn get_all_checkpoints(&self) -> ProviderResult<Vec<(String, StageCheckpoint)>> {
1041 Ok(self
1042 .stage_checkpoints
1043 .lock()
1044 .iter()
1045 .map(|(id, checkpoint)| (id.to_string(), *checkpoint))
1046 .collect())
1047 }
1048}
1049
1050impl<T: NodePrimitives, ChainSpec: Send + Sync> PruneCheckpointReader
1051 for MockEthProvider<T, ChainSpec>
1052{
1053 fn get_prune_checkpoint(
1054 &self,
1055 _segment: PruneSegment,
1056 ) -> ProviderResult<Option<PruneCheckpoint>> {
1057 Ok(None)
1058 }
1059
1060 fn get_prune_checkpoints(&self) -> ProviderResult<Vec<(PruneSegment, PruneCheckpoint)>> {
1061 Ok(vec![])
1062 }
1063}
1064
1065impl<T, ChainSpec> StateRootProvider for MockEthProvider<T, ChainSpec>
1066where
1067 T: NodePrimitives,
1068 ChainSpec: Send + Sync,
1069{
1070 fn state_root(&self, _state: HashedPostState) -> ProviderResult<B256> {
1071 Ok(self.state_roots.lock().pop().unwrap_or_default())
1072 }
1073
1074 fn state_root_from_nodes(&self, _input: TrieInput) -> ProviderResult<B256> {
1075 Ok(self.state_roots.lock().pop().unwrap_or_default())
1076 }
1077
1078 fn state_root_with_updates(
1079 &self,
1080 _state: HashedPostState,
1081 ) -> ProviderResult<(B256, TrieUpdates)> {
1082 let state_root = self.state_roots.lock().pop().unwrap_or_default();
1083 Ok((state_root, Default::default()))
1084 }
1085
1086 fn state_root_from_nodes_with_updates(
1087 &self,
1088 _input: TrieInput,
1089 ) -> ProviderResult<(B256, TrieUpdates)> {
1090 let state_root = self.state_roots.lock().pop().unwrap_or_default();
1091 Ok((state_root, Default::default()))
1092 }
1093}
1094
1095impl<T, ChainSpec> StorageRootProvider for MockEthProvider<T, ChainSpec>
1096where
1097 T: NodePrimitives,
1098 ChainSpec: Send + Sync,
1099{
1100 fn storage_root(
1101 &self,
1102 _address: Address,
1103 _hashed_storage: HashedStorage,
1104 ) -> ProviderResult<B256> {
1105 Ok(EMPTY_ROOT_HASH)
1106 }
1107
1108 fn storage_proof(
1109 &self,
1110 _address: Address,
1111 slot: B256,
1112 _hashed_storage: HashedStorage,
1113 ) -> ProviderResult<reth_trie::StorageProof> {
1114 Ok(StorageProof::new(slot))
1115 }
1116
1117 fn storage_multiproof(
1118 &self,
1119 _address: Address,
1120 _slots: &[B256],
1121 _hashed_storage: HashedStorage,
1122 ) -> ProviderResult<StorageMultiProof> {
1123 Ok(StorageMultiProof::empty())
1124 }
1125}
1126
1127impl<T, ChainSpec> StateProofProvider for MockEthProvider<T, ChainSpec>
1128where
1129 T: NodePrimitives,
1130 ChainSpec: Send + Sync,
1131{
1132 fn proof(
1133 &self,
1134 _input: TrieInput,
1135 address: Address,
1136 _slots: &[B256],
1137 ) -> ProviderResult<AccountProof> {
1138 Ok(AccountProof::new(address))
1139 }
1140
1141 fn multiproof(
1142 &self,
1143 _input: TrieInput,
1144 _targets: MultiProofTargets,
1145 ) -> ProviderResult<MultiProof> {
1146 Ok(MultiProof::default())
1147 }
1148
1149 fn multiproof_v2(
1150 &self,
1151 _input: TrieInput,
1152 _targets: reth_trie::MultiProofTargetsV2,
1153 ) -> ProviderResult<reth_trie::DecodedMultiProofV2> {
1154 Ok(reth_trie::DecodedMultiProofV2::default())
1155 }
1156
1157 fn witness(
1158 &self,
1159 _input: TrieInput,
1160 _target: HashedPostState,
1161 _mode: reth_trie::ExecutionWitnessMode,
1162 ) -> ProviderResult<Vec<Bytes>> {
1163 Ok(Vec::default())
1164 }
1165}
1166
1167impl<T: NodePrimitives, ChainSpec: EthChainSpec + 'static> HashedPostStateProvider
1168 for MockEthProvider<T, ChainSpec>
1169{
1170 fn hashed_post_state(
1171 &self,
1172 _bundle_state: &revm::database::BundleState,
1173 ) -> ProviderResult<HashedPostState> {
1174 Ok(HashedPostState::default())
1175 }
1176}
1177
1178impl<T, ChainSpec> StateProvider for MockEthProvider<T, ChainSpec>
1179where
1180 T: NodePrimitives,
1181 ChainSpec: EthChainSpec + Send + Sync + 'static,
1182{
1183 fn storage(
1184 &self,
1185 account: Address,
1186 storage_key: StorageKey,
1187 ) -> ProviderResult<Option<StorageValue>> {
1188 let lock = self.accounts.lock();
1189 Ok(lock.get(&account).and_then(|account| account.storage.get(&storage_key)).copied())
1190 }
1191}
1192
1193impl<T, ChainSpec> BytecodeReader for MockEthProvider<T, ChainSpec>
1194where
1195 T: NodePrimitives,
1196 ChainSpec: Send + Sync,
1197{
1198 fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult<Option<Bytecode>> {
1199 let lock = self.accounts.lock();
1200 Ok(lock.values().find_map(|account| {
1201 match (account.account.bytecode_hash.as_ref(), account.bytecode.as_ref()) {
1202 (Some(bytecode_hash), Some(bytecode)) if bytecode_hash == code_hash => {
1203 Some(bytecode.clone())
1204 }
1205 _ => None,
1206 }
1207 }))
1208 }
1209}
1210
1211impl<T: NodePrimitives, ChainSpec: Send + Sync> StorageSettingsCache
1212 for MockEthProvider<T, ChainSpec>
1213{
1214 fn cached_storage_settings(&self) -> StorageSettings {
1215 StorageSettings::default()
1216 }
1217
1218 fn set_storage_settings_cache(&self, _settings: StorageSettings) {}
1219}
1220
1221impl<T: NodePrimitives, ChainSpec: EthChainSpec + Send + Sync + 'static> StateProviderFactory
1222 for MockEthProvider<T, ChainSpec>
1223{
1224 fn latest(&self) -> ProviderResult<StateProviderBox> {
1225 self.ensure_snap_state_reads_succeed()?;
1226 Ok(Box::new(self.clone()))
1227 }
1228
1229 fn state_by_block_number_or_tag(
1230 &self,
1231 number_or_tag: BlockNumberOrTag,
1232 ) -> ProviderResult<StateProviderBox> {
1233 match number_or_tag {
1234 BlockNumberOrTag::Latest => self.latest(),
1235 BlockNumberOrTag::Finalized => {
1236 let hash =
1238 self.finalized_block_hash()?.ok_or(ProviderError::FinalizedBlockNotFound)?;
1239
1240 self.history_by_block_hash(hash)
1242 }
1243 BlockNumberOrTag::Safe => {
1244 let hash = self.safe_block_hash()?.ok_or(ProviderError::SafeBlockNotFound)?;
1246
1247 self.history_by_block_hash(hash)
1248 }
1249 BlockNumberOrTag::Earliest => {
1250 self.history_by_block_number(self.earliest_block_number()?)
1251 }
1252 BlockNumberOrTag::Pending => self.pending(),
1253 BlockNumberOrTag::Number(num) => self.history_by_block_number(num),
1254 }
1255 }
1256
1257 fn history_by_block_number(&self, _block: BlockNumber) -> ProviderResult<StateProviderBox> {
1258 Ok(Box::new(self.clone()))
1259 }
1260
1261 fn history_by_block_hash(&self, _block: BlockHash) -> ProviderResult<StateProviderBox> {
1262 Ok(Box::new(self.clone()))
1263 }
1264
1265 fn state_by_block_hash(&self, _block: BlockHash) -> ProviderResult<StateProviderBox> {
1266 Ok(Box::new(self.clone()))
1267 }
1268
1269 fn pending(&self) -> ProviderResult<StateProviderBox> {
1270 Ok(Box::new(self.clone()))
1271 }
1272
1273 fn pending_state_by_hash(&self, _block_hash: B256) -> ProviderResult<Option<StateProviderBox>> {
1274 Ok(Some(Box::new(self.clone())))
1275 }
1276
1277 fn maybe_pending(&self) -> ProviderResult<Option<StateProviderBox>> {
1278 Ok(Some(Box::new(self.clone())))
1279 }
1280}
1281
1282impl<T: NodePrimitives, ChainSpec: Send + Sync> BlockBodyIndicesProvider
1283 for MockEthProvider<T, ChainSpec>
1284{
1285 fn block_body_indices(&self, num: u64) -> ProviderResult<Option<StoredBlockBodyIndices>> {
1286 Ok(self.block_body_indices.lock().get(&num).copied())
1287 }
1288 fn block_body_indices_range(
1289 &self,
1290 _range: RangeInclusive<BlockNumber>,
1291 ) -> ProviderResult<Vec<StoredBlockBodyIndices>> {
1292 Ok(vec![])
1293 }
1294}
1295
1296impl<T: NodePrimitives, ChainSpec: Send + Sync> ChangeSetReader for MockEthProvider<T, ChainSpec> {
1297 fn account_block_changeset(
1298 &self,
1299 _block_number: BlockNumber,
1300 ) -> ProviderResult<Vec<AccountBeforeTx>> {
1301 Ok(Vec::default())
1302 }
1303
1304 fn get_account_before_block(
1305 &self,
1306 _block_number: BlockNumber,
1307 _address: Address,
1308 ) -> ProviderResult<Option<AccountBeforeTx>> {
1309 Ok(None)
1310 }
1311
1312 fn account_changesets_range(
1313 &self,
1314 _range: impl core::ops::RangeBounds<BlockNumber>,
1315 ) -> ProviderResult<Vec<(BlockNumber, AccountBeforeTx)>> {
1316 Ok(Vec::default())
1317 }
1318}
1319
1320impl<T: NodePrimitives, ChainSpec: Send + Sync> StorageChangeSetReader
1321 for MockEthProvider<T, ChainSpec>
1322{
1323 fn storage_changeset(
1324 &self,
1325 _block_number: BlockNumber,
1326 ) -> ProviderResult<Vec<(reth_db_api::models::BlockNumberAddress, StorageEntry)>> {
1327 Ok(Vec::default())
1328 }
1329
1330 fn get_storage_before_block(
1331 &self,
1332 _block_number: BlockNumber,
1333 _address: Address,
1334 _storage_key: B256,
1335 ) -> ProviderResult<Option<StorageEntry>> {
1336 Ok(None)
1337 }
1338
1339 fn storage_changesets_range(
1340 &self,
1341 _range: impl RangeBounds<BlockNumber>,
1342 ) -> ProviderResult<Vec<(reth_db_api::models::BlockNumberAddress, StorageEntry)>> {
1343 Ok(Vec::default())
1344 }
1345}
1346
1347impl<T: NodePrimitives, ChainSpec: Send + Sync> StateReader for MockEthProvider<T, ChainSpec> {
1348 type Receipt = T::Receipt;
1349
1350 fn get_state(
1351 &self,
1352 _block: BlockNumber,
1353 ) -> ProviderResult<Option<ExecutionOutcome<Self::Receipt>>> {
1354 Ok(None)
1355 }
1356}
1357
1358impl<T: NodePrimitives, ChainSpec: Send + Sync> CanonStateSubscriptions
1359 for MockEthProvider<T, ChainSpec>
1360{
1361 fn subscribe_to_canonical_state(&self) -> CanonStateNotifications<T> {
1362 broadcast::channel(1).1
1363 }
1364}
1365
1366impl<T: NodePrimitives, ChainSpec: Send + Sync> NodePrimitivesProvider
1367 for MockEthProvider<T, ChainSpec>
1368{
1369 type Primitives = T;
1370}
1371
1372#[cfg(test)]
1373mod tests {
1374 use super::*;
1375 use alloy_consensus::Header;
1376 use alloy_primitives::BlockHash;
1377 use reth_ethereum_primitives::Receipt;
1378
1379 #[test]
1380 fn test_mock_provider_receipts() {
1381 let provider = MockEthProvider::<EthPrimitives>::new();
1382
1383 let block_hash = BlockHash::random();
1384 let block_number = 1u64;
1385 let header = Header { number: block_number, ..Default::default() };
1386
1387 let receipt1 = Receipt { cumulative_gas_used: 21000, success: true, ..Default::default() };
1388 let receipt2 = Receipt { cumulative_gas_used: 42000, success: true, ..Default::default() };
1389 let receipts = vec![receipt1, receipt2];
1390
1391 provider.add_header(block_hash, header);
1392 provider.add_receipts(block_number, receipts.clone());
1393
1394 let result = provider.receipts_by_block(block_hash.into()).unwrap();
1395 assert_eq!(result, Some(receipts.clone()));
1396
1397 let result = provider.receipts_by_block(block_number.into()).unwrap();
1398 assert_eq!(result, Some(receipts.clone()));
1399
1400 let range_result = provider.receipts_by_block_range(1..=1).unwrap();
1401 assert_eq!(range_result, vec![receipts]);
1402
1403 let non_existent = provider.receipts_by_block(BlockHash::random().into()).unwrap();
1404 assert_eq!(non_existent, None);
1405
1406 let empty_range = provider.receipts_by_block_range(10..=20).unwrap();
1407 assert_eq!(empty_range, Vec::<Vec<Receipt>>::new());
1408 }
1409
1410 #[test]
1411 fn test_mock_provider_receipts_multiple_blocks() {
1412 let provider = MockEthProvider::<EthPrimitives>::new();
1413
1414 let block1_hash = BlockHash::random();
1415 let block2_hash = BlockHash::random();
1416 let block1_number = 1u64;
1417 let block2_number = 2u64;
1418
1419 let header1 = Header { number: block1_number, ..Default::default() };
1420 let header2 = Header { number: block2_number, ..Default::default() };
1421
1422 let receipts1 =
1423 vec![Receipt { cumulative_gas_used: 21000, success: true, ..Default::default() }];
1424 let receipts2 =
1425 vec![Receipt { cumulative_gas_used: 42000, success: true, ..Default::default() }];
1426
1427 provider.add_header(block1_hash, header1);
1428 provider.add_header(block2_hash, header2);
1429 provider.add_receipts(block1_number, receipts1.clone());
1430 provider.add_receipts(block2_number, receipts2.clone());
1431
1432 let range_result = provider.receipts_by_block_range(1..=2).unwrap();
1433 assert_eq!(range_result.len(), 2);
1434 assert_eq!(range_result[0], receipts1);
1435 assert_eq!(range_result[1], receipts2);
1436
1437 let partial_range = provider.receipts_by_block_range(1..=1).unwrap();
1438 assert_eq!(partial_range.len(), 1);
1439 assert_eq!(partial_range[0], receipts1);
1440 }
1441}