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