Skip to main content

reth_provider/providers/state/
historical.rs

1use crate::{
2    AccountReader, BlockHashReader, ChangeSetReader, EitherReader, HashedPostStateProvider,
3    ProviderError, RocksDBProviderFactory, StateProvider, StateRootProvider,
4};
5use alloy_eips::merge::EPOCH_SLOTS;
6use alloy_primitives::{Address, BlockNumber, Bytes, StorageKey, StorageValue, B256};
7use reth_db_api::{
8    cursor::{DbCursorRO, DbDupCursorRO},
9    table::Table,
10    tables,
11    transaction::DbTx,
12    BlockNumberList,
13};
14use reth_primitives_traits::{Account, Bytecode};
15use reth_storage_api::{
16    BlockNumReader, BytecodeReader, DBProvider, NodePrimitivesProvider, StateProofProvider,
17    StorageChangeSetReader, StorageRootProvider, StorageSettingsCache,
18};
19use reth_storage_errors::provider::ProviderResult;
20use reth_trie::{
21    hashed_cursor::HashedPostStateCursorFactory,
22    proof::{Proof, StorageProof},
23    trie_cursor::InMemoryTrieCursorFactory,
24    updates::TrieUpdates,
25    witness::TrieWitness,
26    AccountProof, HashedPostState, HashedPostStateSorted, HashedStorage, KeccakKeyHasher,
27    MultiProof, MultiProofTargets, StateRoot, StorageMultiProof, StorageRoot, TrieInput,
28    TrieInputSorted,
29};
30use reth_trie_db::{
31    hashed_storage_from_reverts_with_provider, DatabaseProof, DatabaseStateRoot,
32    DatabaseStorageProof, DatabaseStorageRoot,
33};
34
35use std::fmt::Debug;
36
37type DbStateRoot<'a, TX, A> = StateRoot<
38    reth_trie_db::DatabaseTrieCursorFactory<&'a TX, A>,
39    reth_trie_db::DatabaseHashedCursorFactory<&'a TX>,
40>;
41type DbStorageRoot<'a, TX, A> = StorageRoot<
42    reth_trie_db::DatabaseTrieCursorFactory<&'a TX, A>,
43    reth_trie_db::DatabaseHashedCursorFactory<&'a TX>,
44>;
45type DbStorageProof<'a, TX, A> = StorageProof<
46    'static,
47    reth_trie_db::DatabaseTrieCursorFactory<&'a TX, A>,
48    reth_trie_db::DatabaseHashedCursorFactory<&'a TX>,
49>;
50type DbProof<'a, TX, A> = Proof<
51    reth_trie_db::DatabaseTrieCursorFactory<&'a TX, A>,
52    reth_trie_db::DatabaseHashedCursorFactory<&'a TX>,
53>;
54
55/// Result of a history lookup for an account or storage slot.
56///
57/// Indicates where to find the historical value for a given key at a specific block.
58#[derive(Debug, Eq, PartialEq)]
59pub enum HistoryInfo {
60    /// The key is written to, but only after our block (not yet written at the target block). Or
61    /// it has never been written.
62    NotYetWritten,
63    /// The chunk contains an entry for a write after our block at the given block number.
64    /// The value should be looked up in the changeset at this block.
65    InChangeset(u64),
66    /// The chunk does not contain an entry for a write after our block. This can only
67    /// happen if this is the last chunk, so we need to look in the plain state.
68    InPlainState,
69    /// The key may have been written, but due to pruning we may not have changesets and
70    /// history, so we need to make a plain state lookup.
71    MaybeInPlainState,
72}
73
74impl HistoryInfo {
75    /// Determines where to find the historical value based on computed shard lookup results.
76    ///
77    /// This is a pure function shared by both MDBX and `RocksDB` backends.
78    ///
79    /// # Arguments
80    /// * `found_block` - The block number from the shard lookup
81    /// * `is_before_first_write` - True if the target block is before the first write to this key.
82    ///   This should be computed as: `rank == 0 && found_block != Some(block_number) &&
83    ///   !has_previous_shard` where `has_previous_shard` comes from a lazy `cursor.prev()` check.
84    /// * `lowest_available` - Lowest block where history is available (pruning boundary)
85    pub const fn from_lookup(
86        found_block: Option<u64>,
87        is_before_first_write: bool,
88        lowest_available: Option<BlockNumber>,
89    ) -> Self {
90        if is_before_first_write {
91            if let (Some(_), Some(block_number)) = (lowest_available, found_block) {
92                // The key may have been written, but due to pruning we may not have changesets
93                // and history, so we need to make a changeset lookup.
94                return Self::InChangeset(block_number)
95            }
96            // The key is written to, but only after our block.
97            return Self::NotYetWritten
98        }
99
100        if let Some(block_number) = found_block {
101            // The chunk contains an entry for a write after our block, return it.
102            Self::InChangeset(block_number)
103        } else {
104            // The chunk does not contain an entry for a write after our block. This can only
105            // happen if this is the last chunk and so we need to look in the plain state.
106            Self::InPlainState
107        }
108    }
109}
110
111/// State provider for a given block number which takes a tx reference.
112///
113/// Historical state provider accesses the state at the start of the provided block number.
114/// It means that all changes made in the provided block number are not included.
115///
116/// Historical state provider reads the following tables:
117/// - [`tables::AccountsHistory`]
118/// - [`tables::Bytecodes`]
119/// - [`tables::StoragesHistory`]
120/// - [`tables::AccountChangeSets`]
121/// - [`tables::StorageChangeSets`]
122#[derive(Debug)]
123pub struct HistoricalStateProviderRef<'b, Provider> {
124    /// Database provider
125    provider: &'b Provider,
126    /// Block number is main index for the history state of accounts and storages.
127    block_number: BlockNumber,
128    /// Lowest blocks at which different parts of the state are available.
129    lowest_available_blocks: LowestAvailableBlocks,
130}
131
132impl<'b, Provider: DBProvider + ChangeSetReader + StorageChangeSetReader + BlockNumReader>
133    HistoricalStateProviderRef<'b, Provider>
134{
135    /// Create new `StateProvider` for historical block number
136    pub fn new(provider: &'b Provider, block_number: BlockNumber) -> Self {
137        Self { provider, block_number, lowest_available_blocks: Default::default() }
138    }
139
140    /// Create new `StateProvider` for historical block number and lowest block numbers at which
141    /// account & storage histories are available.
142    pub const fn new_with_lowest_available_blocks(
143        provider: &'b Provider,
144        block_number: BlockNumber,
145        lowest_available_blocks: LowestAvailableBlocks,
146    ) -> Self {
147        Self { provider, block_number, lowest_available_blocks }
148    }
149
150    /// Lookup an account in the `AccountsHistory` table using `EitherReader`.
151    pub fn account_history_lookup(&self, address: Address) -> ProviderResult<HistoryInfo>
152    where
153        Provider: StorageSettingsCache + RocksDBProviderFactory + NodePrimitivesProvider,
154    {
155        if !self.lowest_available_blocks.is_account_history_available(self.block_number) {
156            return Err(ProviderError::StateAtBlockPruned(self.block_number))
157        }
158
159        self.provider.with_rocksdb_snapshot(|rocksdb_ref| {
160            let mut reader = EitherReader::new_accounts_history(self.provider, rocksdb_ref)?;
161            reader.account_history_info(
162                address,
163                self.block_number,
164                self.lowest_available_blocks.account_history_block_number,
165            )
166        })
167    }
168
169    /// Lookup a storage key in the `StoragesHistory` table using `EitherReader`.
170    ///
171    /// `lookup_key` is always a plain (unhashed) storage key.
172    pub fn storage_history_lookup(
173        &self,
174        address: Address,
175        lookup_key: B256,
176    ) -> ProviderResult<HistoryInfo>
177    where
178        Provider: StorageSettingsCache + RocksDBProviderFactory + NodePrimitivesProvider,
179    {
180        if !self.lowest_available_blocks.is_storage_history_available(self.block_number) {
181            return Err(ProviderError::StateAtBlockPruned(self.block_number))
182        }
183
184        self.provider.with_rocksdb_snapshot(|rocksdb_ref| {
185            let mut reader = EitherReader::new_storages_history(self.provider, rocksdb_ref)?;
186            reader.storage_history_info(
187                address,
188                lookup_key,
189                self.block_number,
190                self.lowest_available_blocks.storage_history_block_number,
191            )
192        })
193    }
194
195    /// Resolves a storage value by looking up the given key in history, changesets, or
196    /// plain state.
197    ///
198    /// `lookup_key` is always a plain (unhashed) storage key.
199    fn storage_by_lookup_key(
200        &self,
201        address: Address,
202        lookup_key: B256,
203    ) -> ProviderResult<Option<StorageValue>>
204    where
205        Provider: StorageSettingsCache + RocksDBProviderFactory + NodePrimitivesProvider,
206    {
207        match self.storage_history_lookup(address, lookup_key)? {
208            HistoryInfo::NotYetWritten => Ok(None),
209            HistoryInfo::InChangeset(changeset_block_number) => self
210                .provider
211                .get_storage_before_block(changeset_block_number, address, lookup_key)?
212                .ok_or_else(|| ProviderError::StorageChangesetNotFound {
213                    block_number: changeset_block_number,
214                    address,
215                    storage_key: Box::new(lookup_key),
216                })
217                .map(|entry| entry.value)
218                .map(Some),
219            HistoryInfo::InPlainState | HistoryInfo::MaybeInPlainState => {
220                if self.provider.cached_storage_settings().use_hashed_state() {
221                    let hashed_address = alloy_primitives::keccak256(address);
222                    let hashed_slot = alloy_primitives::keccak256(lookup_key);
223                    Ok(self
224                        .tx()
225                        .cursor_dup_read::<tables::HashedStorages>()?
226                        .seek_by_key_subkey(hashed_address, hashed_slot)?
227                        .filter(|entry| entry.key == hashed_slot)
228                        .map(|entry| entry.value)
229                        .or(Some(StorageValue::ZERO)))
230                } else {
231                    Ok(self
232                        .tx()
233                        .cursor_dup_read::<tables::PlainStorageState>()?
234                        .seek_by_key_subkey(address, lookup_key)?
235                        .filter(|entry| entry.key == lookup_key)
236                        .map(|entry| entry.value)
237                        .or(Some(StorageValue::ZERO)))
238                }
239            }
240        }
241    }
242
243    /// Checks and returns `true` if distance to historical block exceeds the provided limit.
244    fn check_distance_against_limit(&self, limit: u64) -> ProviderResult<bool> {
245        let tip = self.provider.last_block_number()?;
246
247        Ok(tip.saturating_sub(self.block_number) > limit)
248    }
249
250    /// Retrieve revert hashed state for this history provider.
251    fn revert_state(&self) -> ProviderResult<HashedPostStateSorted>
252    where
253        Provider: StorageSettingsCache,
254    {
255        if !self.lowest_available_blocks.is_account_history_available(self.block_number) ||
256            !self.lowest_available_blocks.is_storage_history_available(self.block_number)
257        {
258            return Err(ProviderError::StateAtBlockPruned(self.block_number))
259        }
260
261        if self.check_distance_against_limit(EPOCH_SLOTS)? {
262            tracing::warn!(
263                target: "providers::historical_sp",
264                target = self.block_number,
265                "Attempt to calculate state root for an old block might result in OOM"
266            );
267        }
268
269        reth_trie_db::from_reverts_auto(self.provider, self.block_number..)
270    }
271
272    /// Retrieve revert hashed storage for this history provider and target address.
273    fn revert_storage(&self, address: Address) -> ProviderResult<HashedStorage>
274    where
275        Provider: StorageSettingsCache,
276    {
277        if !self.lowest_available_blocks.is_storage_history_available(self.block_number) {
278            return Err(ProviderError::StateAtBlockPruned(self.block_number))
279        }
280
281        if self.check_distance_against_limit(EPOCH_SLOTS * 10)? {
282            tracing::warn!(
283                target: "providers::historical_sp",
284                target = self.block_number,
285                "Attempt to calculate storage root for an old block might result in OOM"
286            );
287        }
288
289        hashed_storage_from_reverts_with_provider(self.provider, address, self.block_number)
290    }
291
292    /// Set the lowest block number at which the account history is available.
293    pub const fn with_lowest_available_account_history_block_number(
294        mut self,
295        block_number: BlockNumber,
296    ) -> Self {
297        self.lowest_available_blocks.account_history_block_number = Some(block_number);
298        self
299    }
300
301    /// Set the lowest block number at which the storage history is available.
302    pub const fn with_lowest_available_storage_history_block_number(
303        mut self,
304        block_number: BlockNumber,
305    ) -> Self {
306        self.lowest_available_blocks.storage_history_block_number = Some(block_number);
307        self
308    }
309}
310
311impl<Provider: DBProvider + BlockNumReader> HistoricalStateProviderRef<'_, Provider> {
312    fn tx(&self) -> &Provider::Tx {
313        self.provider.tx_ref()
314    }
315}
316
317impl<
318        Provider: DBProvider
319            + BlockNumReader
320            + ChangeSetReader
321            + StorageChangeSetReader
322            + StorageSettingsCache
323            + RocksDBProviderFactory
324            + NodePrimitivesProvider,
325    > AccountReader for HistoricalStateProviderRef<'_, Provider>
326{
327    /// Get basic account information.
328    fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>> {
329        match self.account_history_lookup(*address)? {
330            HistoryInfo::NotYetWritten => Ok(None),
331            HistoryInfo::InChangeset(changeset_block_number) => {
332                // Use ChangeSetReader trait method to get the account from changesets
333                self.provider
334                    .get_account_before_block(changeset_block_number, *address)?
335                    .ok_or(ProviderError::AccountChangesetNotFound {
336                        block_number: changeset_block_number,
337                        address: *address,
338                    })
339                    .map(|account_before| account_before.info)
340            }
341            HistoryInfo::InPlainState | HistoryInfo::MaybeInPlainState => {
342                if self.provider.cached_storage_settings().use_hashed_state() {
343                    let hashed_address = alloy_primitives::keccak256(address);
344                    Ok(self.tx().get_by_encoded_key::<tables::HashedAccounts>(&hashed_address)?)
345                } else {
346                    Ok(self.tx().get_by_encoded_key::<tables::PlainAccountState>(address)?)
347                }
348            }
349        }
350    }
351}
352
353impl<Provider: DBProvider + BlockNumReader + BlockHashReader> BlockHashReader
354    for HistoricalStateProviderRef<'_, Provider>
355{
356    /// Get block hash by number.
357    fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>> {
358        self.provider.block_hash(number)
359    }
360
361    fn canonical_hashes_range(
362        &self,
363        start: BlockNumber,
364        end: BlockNumber,
365    ) -> ProviderResult<Vec<B256>> {
366        self.provider.canonical_hashes_range(start, end)
367    }
368}
369
370impl<
371        Provider: DBProvider
372            + ChangeSetReader
373            + StorageChangeSetReader
374            + BlockNumReader
375            + StorageSettingsCache,
376    > StateRootProvider for HistoricalStateProviderRef<'_, Provider>
377{
378    fn state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> {
379        reth_trie_db::with_adapter!(self.provider, |A| {
380            let mut revert_state = self.revert_state()?;
381            let hashed_state_sorted = hashed_state.into_sorted();
382            revert_state.extend_ref_and_sort(&hashed_state_sorted);
383            Ok(<DbStateRoot<'_, _, A>>::overlay_root(self.tx(), &revert_state)?)
384        })
385    }
386
387    fn state_root_from_nodes(&self, input: TrieInput) -> ProviderResult<B256> {
388        reth_trie_db::with_adapter!(self.provider, |A| {
389            let mut input = input;
390            input.prepend(self.revert_state()?.into());
391            Ok(<DbStateRoot<'_, _, A>>::overlay_root_from_nodes(
392                self.tx(),
393                TrieInputSorted::from_unsorted(input),
394            )?)
395        })
396    }
397
398    fn state_root_with_updates(
399        &self,
400        hashed_state: HashedPostState,
401    ) -> ProviderResult<(B256, TrieUpdates)> {
402        reth_trie_db::with_adapter!(self.provider, |A| {
403            let mut revert_state = self.revert_state()?;
404            let hashed_state_sorted = hashed_state.into_sorted();
405            revert_state.extend_ref_and_sort(&hashed_state_sorted);
406            Ok(<DbStateRoot<'_, _, A>>::overlay_root_with_updates(self.tx(), &revert_state)?)
407        })
408    }
409
410    fn state_root_from_nodes_with_updates(
411        &self,
412        input: TrieInput,
413    ) -> ProviderResult<(B256, TrieUpdates)> {
414        reth_trie_db::with_adapter!(self.provider, |A| {
415            let mut input = input;
416            input.prepend(self.revert_state()?.into());
417            Ok(<DbStateRoot<'_, _, A>>::overlay_root_from_nodes_with_updates(
418                self.tx(),
419                TrieInputSorted::from_unsorted(input),
420            )?)
421        })
422    }
423}
424
425impl<
426        Provider: DBProvider
427            + ChangeSetReader
428            + StorageChangeSetReader
429            + BlockNumReader
430            + StorageSettingsCache,
431    > StorageRootProvider for HistoricalStateProviderRef<'_, Provider>
432{
433    fn storage_root(
434        &self,
435        address: Address,
436        hashed_storage: HashedStorage,
437    ) -> ProviderResult<B256> {
438        reth_trie_db::with_adapter!(self.provider, |A| {
439            let mut revert_storage = self.revert_storage(address)?;
440            revert_storage.extend(&hashed_storage);
441            <DbStorageRoot<'_, _, A>>::overlay_root(self.tx(), address, revert_storage)
442                .map_err(|err| ProviderError::Database(err.into()))
443        })
444    }
445
446    fn storage_proof(
447        &self,
448        address: Address,
449        slot: B256,
450        hashed_storage: HashedStorage,
451    ) -> ProviderResult<reth_trie::StorageProof> {
452        reth_trie_db::with_adapter!(self.provider, |A| {
453            let mut revert_storage = self.revert_storage(address)?;
454            revert_storage.extend(&hashed_storage);
455            <DbStorageProof<'_, _, A>>::overlay_storage_proof(
456                self.tx(),
457                address,
458                slot,
459                revert_storage,
460            )
461            .map_err(ProviderError::from)
462        })
463    }
464
465    fn storage_multiproof(
466        &self,
467        address: Address,
468        slots: &[B256],
469        hashed_storage: HashedStorage,
470    ) -> ProviderResult<StorageMultiProof> {
471        reth_trie_db::with_adapter!(self.provider, |A| {
472            let mut revert_storage = self.revert_storage(address)?;
473            revert_storage.extend(&hashed_storage);
474            <DbStorageProof<'_, _, A>>::overlay_storage_multiproof(
475                self.tx(),
476                address,
477                slots,
478                revert_storage,
479            )
480            .map_err(ProviderError::from)
481        })
482    }
483}
484
485impl<
486        Provider: DBProvider
487            + ChangeSetReader
488            + StorageChangeSetReader
489            + BlockNumReader
490            + StorageSettingsCache,
491    > StateProofProvider for HistoricalStateProviderRef<'_, Provider>
492{
493    /// Get account and storage proofs.
494    fn proof(
495        &self,
496        input: TrieInput,
497        address: Address,
498        slots: &[B256],
499    ) -> ProviderResult<AccountProof> {
500        reth_trie_db::with_adapter!(self.provider, |A| {
501            let mut input = input;
502            input.prepend(self.revert_state()?.into());
503            let proof = <DbProof<'_, _, A> as DatabaseProof>::from_tx(self.tx());
504            proof.overlay_account_proof(input, address, slots).map_err(ProviderError::from)
505        })
506    }
507
508    fn multiproof(
509        &self,
510        input: TrieInput,
511        targets: MultiProofTargets,
512    ) -> ProviderResult<MultiProof> {
513        reth_trie_db::with_adapter!(self.provider, |A| {
514            let mut input = input;
515            input.prepend(self.revert_state()?.into());
516            let proof = <DbProof<'_, _, A> as DatabaseProof>::from_tx(self.tx());
517            proof.overlay_multiproof(input, targets).map_err(ProviderError::from)
518        })
519    }
520
521    fn witness(&self, input: TrieInput, target: HashedPostState) -> ProviderResult<Vec<Bytes>> {
522        reth_trie_db::with_adapter!(self.provider, |A| {
523            let mut input = input;
524            input.prepend(self.revert_state()?.into());
525            let nodes_sorted = input.nodes.into_sorted();
526            let state_sorted = input.state.into_sorted();
527            TrieWitness::new(
528                InMemoryTrieCursorFactory::new(
529                    reth_trie_db::DatabaseTrieCursorFactory::<_, A>::new(self.tx()),
530                    &nodes_sorted,
531                ),
532                HashedPostStateCursorFactory::new(
533                    reth_trie_db::DatabaseHashedCursorFactory::new(self.tx()),
534                    &state_sorted,
535                ),
536            )
537            .with_prefix_sets_mut(input.prefix_sets)
538            .always_include_root_node()
539            .compute(target)
540            .map_err(ProviderError::from)
541            .map(|hm| hm.into_values().collect())
542        })
543    }
544}
545
546impl<Provider> HashedPostStateProvider for HistoricalStateProviderRef<'_, Provider> {
547    fn hashed_post_state(&self, bundle_state: &revm_database::BundleState) -> HashedPostState {
548        HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state())
549    }
550}
551
552impl<
553        Provider: DBProvider
554            + BlockNumReader
555            + BlockHashReader
556            + ChangeSetReader
557            + StorageChangeSetReader
558            + StorageSettingsCache
559            + RocksDBProviderFactory
560            + NodePrimitivesProvider,
561    > StateProvider for HistoricalStateProviderRef<'_, Provider>
562{
563    /// Expects a plain (unhashed) storage key slot.
564    fn storage(
565        &self,
566        address: Address,
567        storage_key: StorageKey,
568    ) -> ProviderResult<Option<StorageValue>> {
569        self.storage_by_lookup_key(address, storage_key)
570    }
571}
572
573impl<Provider: DBProvider + BlockNumReader> BytecodeReader
574    for HistoricalStateProviderRef<'_, Provider>
575{
576    /// Get account code by its hash
577    fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult<Option<Bytecode>> {
578        self.tx().get_by_encoded_key::<tables::Bytecodes>(code_hash).map_err(Into::into)
579    }
580}
581
582/// State provider for a given block number.
583/// For more detailed description, see [`HistoricalStateProviderRef`].
584#[derive(Debug)]
585pub struct HistoricalStateProvider<Provider> {
586    /// Database provider.
587    provider: Provider,
588    /// State at the block number is the main indexer of the state.
589    block_number: BlockNumber,
590    /// Lowest blocks at which different parts of the state are available.
591    lowest_available_blocks: LowestAvailableBlocks,
592}
593
594impl<Provider: DBProvider + ChangeSetReader + StorageChangeSetReader + BlockNumReader>
595    HistoricalStateProvider<Provider>
596{
597    /// Create new `StateProvider` for historical block number
598    pub fn new(provider: Provider, block_number: BlockNumber) -> Self {
599        Self { provider, block_number, lowest_available_blocks: Default::default() }
600    }
601
602    /// Set the lowest block number at which the account history is available.
603    pub const fn with_lowest_available_account_history_block_number(
604        mut self,
605        block_number: BlockNumber,
606    ) -> Self {
607        self.lowest_available_blocks.account_history_block_number = Some(block_number);
608        self
609    }
610
611    /// Set the lowest block number at which the storage history is available.
612    pub const fn with_lowest_available_storage_history_block_number(
613        mut self,
614        block_number: BlockNumber,
615    ) -> Self {
616        self.lowest_available_blocks.storage_history_block_number = Some(block_number);
617        self
618    }
619
620    /// Returns a new provider that takes the `TX` as reference
621    #[inline(always)]
622    const fn as_ref(&self) -> HistoricalStateProviderRef<'_, Provider> {
623        HistoricalStateProviderRef::new_with_lowest_available_blocks(
624            &self.provider,
625            self.block_number,
626            self.lowest_available_blocks,
627        )
628    }
629}
630
631// Delegates all provider impls to [HistoricalStateProviderRef]
632reth_storage_api::macros::delegate_provider_impls!(HistoricalStateProvider<Provider> where [Provider: DBProvider + BlockNumReader + BlockHashReader + ChangeSetReader + StorageChangeSetReader + StorageSettingsCache + RocksDBProviderFactory + NodePrimitivesProvider]);
633
634/// Lowest blocks at which different parts of the state are available.
635/// They may be [Some] if pruning is enabled.
636#[derive(Clone, Copy, Debug, Default)]
637pub struct LowestAvailableBlocks {
638    /// Lowest block number at which the account history is available. It may not be available if
639    /// [`reth_prune_types::PruneSegment::AccountHistory`] was pruned.
640    /// [`Option::None`] means all history is available.
641    pub account_history_block_number: Option<BlockNumber>,
642    /// Lowest block number at which the storage history is available. It may not be available if
643    /// [`reth_prune_types::PruneSegment::StorageHistory`] was pruned.
644    /// [`Option::None`] means all history is available.
645    pub storage_history_block_number: Option<BlockNumber>,
646}
647
648impl LowestAvailableBlocks {
649    /// Check if account history is available at the provided block number, i.e. lowest available
650    /// block number for account history is less than or equal to the provided block number.
651    pub fn is_account_history_available(&self, at: BlockNumber) -> bool {
652        self.account_history_block_number.map(|block_number| block_number <= at).unwrap_or(true)
653    }
654
655    /// Check if storage history is available at the provided block number, i.e. lowest available
656    /// block number for storage history is less than or equal to the provided block number.
657    pub fn is_storage_history_available(&self, at: BlockNumber) -> bool {
658        self.storage_history_block_number.map(|block_number| block_number <= at).unwrap_or(true)
659    }
660}
661
662/// Computes the rank and finds the next modification block in a history shard.
663///
664/// Given a `block_number`, this function returns:
665/// - `rank`: The number of entries strictly before `block_number` in the shard
666/// - `found_block`: The block number at position `rank` (i.e., the first block >= `block_number`
667///   where a modification occurred), or `None` if `rank` is out of bounds
668///
669/// The rank is adjusted when `block_number` exactly matches an entry in the shard,
670/// so that `found_block` always returns the modification at or after the target.
671///
672/// This logic is shared between MDBX cursor-based lookups and `RocksDB` iterator lookups.
673#[inline]
674pub fn compute_history_rank(
675    chunk: &reth_db_api::BlockNumberList,
676    block_number: BlockNumber,
677) -> (u64, Option<u64>) {
678    let mut rank = chunk.rank(block_number);
679    // `rank(block_number)` returns count of entries <= block_number.
680    // We want the first entry >= block_number, so if block_number is in the shard,
681    // we need to step back one position to point at it (not past it).
682    if rank.checked_sub(1).and_then(|r| chunk.select(r)) == Some(block_number) {
683        rank -= 1;
684    }
685    (rank, chunk.select(rank))
686}
687
688/// Checks if a previous shard lookup is needed to determine if we're before the first write.
689///
690/// Returns `true` when `rank == 0` (first entry in shard) and the found block doesn't match
691/// the target block number. In this case, we need to check if there's a previous shard.
692#[inline]
693pub fn needs_prev_shard_check(
694    rank: u64,
695    found_block: Option<u64>,
696    block_number: BlockNumber,
697) -> bool {
698    rank == 0 && found_block != Some(block_number)
699}
700
701/// Generic history lookup for sharded history tables.
702///
703/// Seeks to the shard containing `block_number`, verifies the key via `key_filter`,
704/// and checks previous shard to detect if we're before the first write.
705pub fn history_info<T, K, C>(
706    cursor: &mut C,
707    key: K,
708    block_number: BlockNumber,
709    key_filter: impl Fn(&K) -> bool,
710    lowest_available_block_number: Option<BlockNumber>,
711) -> ProviderResult<HistoryInfo>
712where
713    T: Table<Key = K, Value = BlockNumberList>,
714    C: DbCursorRO<T>,
715{
716    // Lookup the history chunk in the history index. If the key does not appear in the
717    // index, the first chunk for the next key will be returned so we filter out chunks that
718    // have a different key.
719    if let Some(chunk) = cursor.seek(key)?.filter(|(k, _)| key_filter(k)).map(|x| x.1) {
720        let (rank, found_block) = compute_history_rank(&chunk, block_number);
721
722        // If our block is before the first entry in the index chunk and this first entry
723        // doesn't equal to our block, it might be before the first write ever. To check, we
724        // look at the previous entry and check if the key is the same.
725        // This check is worth it, the `cursor.prev()` check is rarely triggered (the if will
726        // short-circuit) and when it passes we save a full seek into the changeset/plain state
727        // table.
728        let is_before_first_write = needs_prev_shard_check(rank, found_block, block_number) &&
729            !cursor.prev()?.is_some_and(|(k, _)| key_filter(&k));
730
731        Ok(HistoryInfo::from_lookup(
732            found_block,
733            is_before_first_write,
734            lowest_available_block_number,
735        ))
736    } else if lowest_available_block_number.is_some() {
737        // The key may have been written, but due to pruning we may not have changesets and
738        // history, so we need to make a plain state lookup.
739        Ok(HistoryInfo::MaybeInPlainState)
740    } else {
741        // The key has not been written to at all.
742        Ok(HistoryInfo::NotYetWritten)
743    }
744}
745
746#[cfg(test)]
747mod tests {
748    use super::needs_prev_shard_check;
749    use crate::{
750        providers::state::historical::{HistoryInfo, LowestAvailableBlocks},
751        test_utils::create_test_provider_factory,
752        AccountReader, HistoricalStateProvider, HistoricalStateProviderRef, RocksDBProviderFactory,
753        StateProvider,
754    };
755    use alloy_primitives::{address, b256, Address, B256, U256};
756    use reth_db_api::{
757        models::{storage_sharded_key::StorageShardedKey, AccountBeforeTx, ShardedKey},
758        tables,
759        transaction::{DbTx, DbTxMut},
760        BlockNumberList,
761    };
762    use reth_primitives_traits::{Account, StorageEntry};
763    use reth_storage_api::{
764        BlockHashReader, BlockNumReader, ChangeSetReader, DBProvider, DatabaseProviderFactory,
765        NodePrimitivesProvider, StorageChangeSetReader, StorageSettingsCache,
766    };
767    use reth_storage_errors::provider::ProviderError;
768
769    const ADDRESS: Address = address!("0x0000000000000000000000000000000000000001");
770    const HIGHER_ADDRESS: Address = address!("0x0000000000000000000000000000000000000005");
771    const STORAGE: B256 =
772        b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
773
774    const fn assert_state_provider<T: StateProvider>() {}
775    #[expect(dead_code)]
776    const fn assert_historical_state_provider<
777        T: DBProvider
778            + BlockNumReader
779            + BlockHashReader
780            + ChangeSetReader
781            + StorageChangeSetReader
782            + StorageSettingsCache
783            + RocksDBProviderFactory
784            + NodePrimitivesProvider,
785    >() {
786        assert_state_provider::<HistoricalStateProvider<T>>();
787    }
788
789    #[test]
790    fn history_provider_get_account() {
791        let factory = create_test_provider_factory();
792        let tx = factory.provider_rw().unwrap().into_tx();
793
794        tx.put::<tables::AccountsHistory>(
795            ShardedKey { key: ADDRESS, highest_block_number: 7 },
796            BlockNumberList::new([1, 3, 7]).unwrap(),
797        )
798        .unwrap();
799        tx.put::<tables::AccountsHistory>(
800            ShardedKey { key: ADDRESS, highest_block_number: u64::MAX },
801            BlockNumberList::new([10, 15]).unwrap(),
802        )
803        .unwrap();
804        tx.put::<tables::AccountsHistory>(
805            ShardedKey { key: HIGHER_ADDRESS, highest_block_number: u64::MAX },
806            BlockNumberList::new([4]).unwrap(),
807        )
808        .unwrap();
809
810        let acc_plain = Account { nonce: 100, balance: U256::ZERO, bytecode_hash: None };
811        let acc_at15 = Account { nonce: 15, balance: U256::ZERO, bytecode_hash: None };
812        let acc_at10 = Account { nonce: 10, balance: U256::ZERO, bytecode_hash: None };
813        let acc_at7 = Account { nonce: 7, balance: U256::ZERO, bytecode_hash: None };
814        let acc_at3 = Account { nonce: 3, balance: U256::ZERO, bytecode_hash: None };
815
816        let higher_acc_plain = Account { nonce: 4, balance: U256::ZERO, bytecode_hash: None };
817
818        // setup
819        tx.put::<tables::AccountChangeSets>(1, AccountBeforeTx { address: ADDRESS, info: None })
820            .unwrap();
821        tx.put::<tables::AccountChangeSets>(
822            3,
823            AccountBeforeTx { address: ADDRESS, info: Some(acc_at3) },
824        )
825        .unwrap();
826        tx.put::<tables::AccountChangeSets>(
827            4,
828            AccountBeforeTx { address: HIGHER_ADDRESS, info: None },
829        )
830        .unwrap();
831        tx.put::<tables::AccountChangeSets>(
832            7,
833            AccountBeforeTx { address: ADDRESS, info: Some(acc_at7) },
834        )
835        .unwrap();
836        tx.put::<tables::AccountChangeSets>(
837            10,
838            AccountBeforeTx { address: ADDRESS, info: Some(acc_at10) },
839        )
840        .unwrap();
841        tx.put::<tables::AccountChangeSets>(
842            15,
843            AccountBeforeTx { address: ADDRESS, info: Some(acc_at15) },
844        )
845        .unwrap();
846
847        // setup plain state
848        tx.put::<tables::PlainAccountState>(ADDRESS, acc_plain).unwrap();
849        tx.put::<tables::PlainAccountState>(HIGHER_ADDRESS, higher_acc_plain).unwrap();
850        tx.commit().unwrap();
851
852        let db = factory.provider().unwrap();
853
854        // run
855        assert!(matches!(
856            HistoricalStateProviderRef::new(&db, 1).basic_account(&ADDRESS),
857            Ok(None)
858        ));
859        assert!(matches!(
860            HistoricalStateProviderRef::new(&db, 2).basic_account(&ADDRESS),
861            Ok(Some(acc)) if acc == acc_at3
862        ));
863        assert!(matches!(
864            HistoricalStateProviderRef::new(&db, 3).basic_account(&ADDRESS),
865            Ok(Some(acc)) if acc == acc_at3
866        ));
867        assert!(matches!(
868            HistoricalStateProviderRef::new(&db, 4).basic_account(&ADDRESS),
869            Ok(Some(acc)) if acc == acc_at7
870        ));
871        assert!(matches!(
872            HistoricalStateProviderRef::new(&db, 7).basic_account(&ADDRESS),
873            Ok(Some(acc)) if acc == acc_at7
874        ));
875        assert!(matches!(
876            HistoricalStateProviderRef::new(&db, 9).basic_account(&ADDRESS),
877            Ok(Some(acc)) if acc == acc_at10
878        ));
879        assert!(matches!(
880            HistoricalStateProviderRef::new(&db, 10).basic_account(&ADDRESS),
881            Ok(Some(acc)) if acc == acc_at10
882        ));
883        assert!(matches!(
884            HistoricalStateProviderRef::new(&db, 11).basic_account(&ADDRESS),
885            Ok(Some(acc)) if acc == acc_at15
886        ));
887        assert!(matches!(
888            HistoricalStateProviderRef::new(&db, 16).basic_account(&ADDRESS),
889            Ok(Some(acc)) if acc == acc_plain
890        ));
891
892        assert!(matches!(
893            HistoricalStateProviderRef::new(&db, 1).basic_account(&HIGHER_ADDRESS),
894            Ok(None)
895        ));
896        assert!(matches!(
897            HistoricalStateProviderRef::new(&db, 1000).basic_account(&HIGHER_ADDRESS),
898            Ok(Some(acc)) if acc == higher_acc_plain
899        ));
900    }
901
902    #[test]
903    fn history_provider_get_storage() {
904        let factory = create_test_provider_factory();
905        let tx = factory.provider_rw().unwrap().into_tx();
906
907        tx.put::<tables::StoragesHistory>(
908            StorageShardedKey {
909                address: ADDRESS,
910                sharded_key: ShardedKey { key: STORAGE, highest_block_number: 7 },
911            },
912            BlockNumberList::new([3, 7]).unwrap(),
913        )
914        .unwrap();
915        tx.put::<tables::StoragesHistory>(
916            StorageShardedKey {
917                address: ADDRESS,
918                sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
919            },
920            BlockNumberList::new([10, 15]).unwrap(),
921        )
922        .unwrap();
923        tx.put::<tables::StoragesHistory>(
924            StorageShardedKey {
925                address: HIGHER_ADDRESS,
926                sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
927            },
928            BlockNumberList::new([4]).unwrap(),
929        )
930        .unwrap();
931
932        let higher_entry_plain = StorageEntry { key: STORAGE, value: U256::from(1000) };
933        let higher_entry_at4 = StorageEntry { key: STORAGE, value: U256::from(0) };
934        let entry_plain = StorageEntry { key: STORAGE, value: U256::from(100) };
935        let entry_at15 = StorageEntry { key: STORAGE, value: U256::from(15) };
936        let entry_at10 = StorageEntry { key: STORAGE, value: U256::from(10) };
937        let entry_at7 = StorageEntry { key: STORAGE, value: U256::from(7) };
938        let entry_at3 = StorageEntry { key: STORAGE, value: U256::from(0) };
939
940        // setup
941        tx.put::<tables::StorageChangeSets>((3, ADDRESS).into(), entry_at3).unwrap();
942        tx.put::<tables::StorageChangeSets>((4, HIGHER_ADDRESS).into(), higher_entry_at4).unwrap();
943        tx.put::<tables::StorageChangeSets>((7, ADDRESS).into(), entry_at7).unwrap();
944        tx.put::<tables::StorageChangeSets>((10, ADDRESS).into(), entry_at10).unwrap();
945        tx.put::<tables::StorageChangeSets>((15, ADDRESS).into(), entry_at15).unwrap();
946
947        // setup plain state
948        tx.put::<tables::PlainStorageState>(ADDRESS, entry_plain).unwrap();
949        tx.put::<tables::PlainStorageState>(HIGHER_ADDRESS, higher_entry_plain).unwrap();
950        tx.commit().unwrap();
951
952        let db = factory.provider().unwrap();
953
954        // run
955        assert!(matches!(
956            HistoricalStateProviderRef::new(&db, 0).storage(ADDRESS, STORAGE),
957            Ok(None)
958        ));
959        assert!(matches!(
960            HistoricalStateProviderRef::new(&db, 3).storage(ADDRESS, STORAGE),
961            Ok(Some(U256::ZERO))
962        ));
963        assert!(matches!(
964            HistoricalStateProviderRef::new(&db, 4).storage(ADDRESS, STORAGE),
965            Ok(Some(expected_value)) if expected_value == entry_at7.value
966        ));
967        assert!(matches!(
968            HistoricalStateProviderRef::new(&db, 7).storage(ADDRESS, STORAGE),
969            Ok(Some(expected_value)) if expected_value == entry_at7.value
970        ));
971        assert!(matches!(
972            HistoricalStateProviderRef::new(&db, 9).storage(ADDRESS, STORAGE),
973            Ok(Some(expected_value)) if expected_value == entry_at10.value
974        ));
975        assert!(matches!(
976            HistoricalStateProviderRef::new(&db, 10).storage(ADDRESS, STORAGE),
977            Ok(Some(expected_value)) if expected_value == entry_at10.value
978        ));
979        assert!(matches!(
980            HistoricalStateProviderRef::new(&db, 11).storage(ADDRESS, STORAGE),
981            Ok(Some(expected_value)) if expected_value == entry_at15.value
982        ));
983        assert!(matches!(
984            HistoricalStateProviderRef::new(&db, 16).storage(ADDRESS, STORAGE),
985            Ok(Some(expected_value)) if expected_value == entry_plain.value
986        ));
987        assert!(matches!(
988            HistoricalStateProviderRef::new(&db, 1).storage(HIGHER_ADDRESS, STORAGE),
989            Ok(None)
990        ));
991        assert!(matches!(
992            HistoricalStateProviderRef::new(&db, 1000).storage(HIGHER_ADDRESS, STORAGE),
993            Ok(Some(expected_value)) if expected_value == higher_entry_plain.value
994        ));
995    }
996
997    #[test]
998    fn history_provider_unavailable() {
999        let factory = create_test_provider_factory();
1000        let db = factory.database_provider_rw().unwrap();
1001
1002        // provider block_number < lowest available block number,
1003        // i.e. state at provider block is pruned
1004        let provider = HistoricalStateProviderRef::new_with_lowest_available_blocks(
1005            &db,
1006            2,
1007            LowestAvailableBlocks {
1008                account_history_block_number: Some(3),
1009                storage_history_block_number: Some(3),
1010            },
1011        );
1012        assert!(matches!(
1013            provider.account_history_lookup(ADDRESS),
1014            Err(ProviderError::StateAtBlockPruned(number)) if number == provider.block_number
1015        ));
1016        assert!(matches!(
1017            provider.storage_history_lookup(ADDRESS, STORAGE),
1018            Err(ProviderError::StateAtBlockPruned(number)) if number == provider.block_number
1019        ));
1020
1021        // provider block_number == lowest available block number,
1022        // i.e. state at provider block is available
1023        let provider = HistoricalStateProviderRef::new_with_lowest_available_blocks(
1024            &db,
1025            2,
1026            LowestAvailableBlocks {
1027                account_history_block_number: Some(2),
1028                storage_history_block_number: Some(2),
1029            },
1030        );
1031        assert!(matches!(
1032            provider.account_history_lookup(ADDRESS),
1033            Ok(HistoryInfo::MaybeInPlainState)
1034        ));
1035        assert!(matches!(
1036            provider.storage_history_lookup(ADDRESS, STORAGE),
1037            Ok(HistoryInfo::MaybeInPlainState)
1038        ));
1039
1040        // provider block_number == lowest available block number,
1041        // i.e. state at provider block is available
1042        let provider = HistoricalStateProviderRef::new_with_lowest_available_blocks(
1043            &db,
1044            2,
1045            LowestAvailableBlocks {
1046                account_history_block_number: Some(1),
1047                storage_history_block_number: Some(1),
1048            },
1049        );
1050        assert!(matches!(
1051            provider.account_history_lookup(ADDRESS),
1052            Ok(HistoryInfo::MaybeInPlainState)
1053        ));
1054        assert!(matches!(
1055            provider.storage_history_lookup(ADDRESS, STORAGE),
1056            Ok(HistoryInfo::MaybeInPlainState)
1057        ));
1058    }
1059
1060    #[test]
1061    fn test_history_info_from_lookup() {
1062        // Before first write, no pruning → not yet written
1063        assert_eq!(HistoryInfo::from_lookup(Some(10), true, None), HistoryInfo::NotYetWritten);
1064        assert_eq!(HistoryInfo::from_lookup(None, true, None), HistoryInfo::NotYetWritten);
1065
1066        // Before first write WITH pruning → check changeset (pruning may have removed history)
1067        assert_eq!(HistoryInfo::from_lookup(Some(10), true, Some(5)), HistoryInfo::InChangeset(10));
1068        assert_eq!(HistoryInfo::from_lookup(None, true, Some(5)), HistoryInfo::NotYetWritten);
1069
1070        // Not before first write → check changeset or plain state
1071        assert_eq!(HistoryInfo::from_lookup(Some(10), false, None), HistoryInfo::InChangeset(10));
1072        assert_eq!(HistoryInfo::from_lookup(None, false, None), HistoryInfo::InPlainState);
1073    }
1074
1075    #[test]
1076    fn history_provider_get_storage_legacy() {
1077        let factory = create_test_provider_factory();
1078
1079        assert!(!factory.provider().unwrap().cached_storage_settings().use_hashed_state());
1080
1081        let tx = factory.provider_rw().unwrap().into_tx();
1082
1083        tx.put::<tables::StoragesHistory>(
1084            StorageShardedKey {
1085                address: ADDRESS,
1086                sharded_key: ShardedKey { key: STORAGE, highest_block_number: 7 },
1087            },
1088            BlockNumberList::new([3, 7]).unwrap(),
1089        )
1090        .unwrap();
1091        tx.put::<tables::StoragesHistory>(
1092            StorageShardedKey {
1093                address: ADDRESS,
1094                sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
1095            },
1096            BlockNumberList::new([10, 15]).unwrap(),
1097        )
1098        .unwrap();
1099        tx.put::<tables::StoragesHistory>(
1100            StorageShardedKey {
1101                address: HIGHER_ADDRESS,
1102                sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
1103            },
1104            BlockNumberList::new([4]).unwrap(),
1105        )
1106        .unwrap();
1107
1108        let higher_entry_plain = StorageEntry { key: STORAGE, value: U256::from(1000) };
1109        let higher_entry_at4 = StorageEntry { key: STORAGE, value: U256::from(0) };
1110        let entry_plain = StorageEntry { key: STORAGE, value: U256::from(100) };
1111        let entry_at15 = StorageEntry { key: STORAGE, value: U256::from(15) };
1112        let entry_at10 = StorageEntry { key: STORAGE, value: U256::from(10) };
1113        let entry_at7 = StorageEntry { key: STORAGE, value: U256::from(7) };
1114        let entry_at3 = StorageEntry { key: STORAGE, value: U256::from(0) };
1115
1116        tx.put::<tables::StorageChangeSets>((3, ADDRESS).into(), entry_at3).unwrap();
1117        tx.put::<tables::StorageChangeSets>((4, HIGHER_ADDRESS).into(), higher_entry_at4).unwrap();
1118        tx.put::<tables::StorageChangeSets>((7, ADDRESS).into(), entry_at7).unwrap();
1119        tx.put::<tables::StorageChangeSets>((10, ADDRESS).into(), entry_at10).unwrap();
1120        tx.put::<tables::StorageChangeSets>((15, ADDRESS).into(), entry_at15).unwrap();
1121
1122        tx.put::<tables::PlainStorageState>(ADDRESS, entry_plain).unwrap();
1123        tx.put::<tables::PlainStorageState>(HIGHER_ADDRESS, higher_entry_plain).unwrap();
1124        tx.commit().unwrap();
1125
1126        let db = factory.provider().unwrap();
1127
1128        assert!(matches!(
1129            HistoricalStateProviderRef::new(&db, 0).storage(ADDRESS, STORAGE),
1130            Ok(None)
1131        ));
1132        assert!(matches!(
1133            HistoricalStateProviderRef::new(&db, 3).storage(ADDRESS, STORAGE),
1134            Ok(Some(U256::ZERO))
1135        ));
1136        assert!(matches!(
1137            HistoricalStateProviderRef::new(&db, 4).storage(ADDRESS, STORAGE),
1138            Ok(Some(expected_value)) if expected_value == entry_at7.value
1139        ));
1140        assert!(matches!(
1141            HistoricalStateProviderRef::new(&db, 7).storage(ADDRESS, STORAGE),
1142            Ok(Some(expected_value)) if expected_value == entry_at7.value
1143        ));
1144        assert!(matches!(
1145            HistoricalStateProviderRef::new(&db, 9).storage(ADDRESS, STORAGE),
1146            Ok(Some(expected_value)) if expected_value == entry_at10.value
1147        ));
1148        assert!(matches!(
1149            HistoricalStateProviderRef::new(&db, 10).storage(ADDRESS, STORAGE),
1150            Ok(Some(expected_value)) if expected_value == entry_at10.value
1151        ));
1152        assert!(matches!(
1153            HistoricalStateProviderRef::new(&db, 11).storage(ADDRESS, STORAGE),
1154            Ok(Some(expected_value)) if expected_value == entry_at15.value
1155        ));
1156        assert!(matches!(
1157            HistoricalStateProviderRef::new(&db, 16).storage(ADDRESS, STORAGE),
1158            Ok(Some(expected_value)) if expected_value == entry_plain.value
1159        ));
1160        assert!(matches!(
1161            HistoricalStateProviderRef::new(&db, 1).storage(HIGHER_ADDRESS, STORAGE),
1162            Ok(None)
1163        ));
1164        assert!(matches!(
1165            HistoricalStateProviderRef::new(&db, 1000).storage(HIGHER_ADDRESS, STORAGE),
1166            Ok(Some(expected_value)) if expected_value == higher_entry_plain.value
1167        ));
1168    }
1169
1170    #[test]
1171    fn history_provider_get_storage_hashed_state() {
1172        use crate::BlockWriter;
1173        use alloy_primitives::keccak256;
1174        use reth_db_api::models::StorageSettings;
1175        use reth_execution_types::ExecutionOutcome;
1176        use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};
1177        use revm_database::BundleState;
1178        use std::collections::HashMap;
1179
1180        let factory = create_test_provider_factory();
1181        factory.set_storage_settings_cache(StorageSettings::v2());
1182
1183        let slot = U256::from_be_bytes(*STORAGE);
1184        let account: revm_state::AccountInfo =
1185            Account { nonce: 1, balance: U256::from(1000), bytecode_hash: None }.into();
1186        let higher_account: revm_state::AccountInfo =
1187            Account { nonce: 1, balance: U256::from(2000), bytecode_hash: None }.into();
1188
1189        let mut rng = generators::rng();
1190        let blocks = random_block_range(
1191            &mut rng,
1192            0..=15,
1193            BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..1, ..Default::default() },
1194        );
1195
1196        let mut addr_storage = HashMap::default();
1197        addr_storage.insert(slot, (U256::ZERO, U256::from(100)));
1198        let mut higher_storage = HashMap::default();
1199        higher_storage.insert(slot, (U256::ZERO, U256::from(1000)));
1200
1201        type Revert = Vec<(Address, Option<Option<revm_state::AccountInfo>>, Vec<(U256, U256)>)>;
1202        let mut reverts: Vec<Revert> = vec![Vec::new(); 16];
1203
1204        reverts[3] = vec![(ADDRESS, Some(Some(account.clone())), vec![(slot, U256::ZERO)])];
1205        reverts[4] =
1206            vec![(HIGHER_ADDRESS, Some(Some(higher_account.clone())), vec![(slot, U256::ZERO)])];
1207        reverts[7] = vec![(ADDRESS, Some(Some(account.clone())), vec![(slot, U256::from(7))])];
1208        reverts[10] = vec![(ADDRESS, Some(Some(account.clone())), vec![(slot, U256::from(10))])];
1209        reverts[15] = vec![(ADDRESS, Some(Some(account.clone())), vec![(slot, U256::from(15))])];
1210
1211        let bundle = BundleState::new(
1212            [
1213                (ADDRESS, None, Some(account), addr_storage),
1214                (HIGHER_ADDRESS, None, Some(higher_account), higher_storage),
1215            ],
1216            reverts,
1217            [],
1218        );
1219
1220        let provider_rw = factory.provider_rw().unwrap();
1221        provider_rw
1222            .append_blocks_with_state(
1223                blocks
1224                    .into_iter()
1225                    .map(|b| b.try_recover().expect("failed to seal block with senders"))
1226                    .collect(),
1227                &ExecutionOutcome { bundle, first_block: 0, ..Default::default() },
1228                Default::default(),
1229            )
1230            .unwrap();
1231
1232        let hashed_address = keccak256(ADDRESS);
1233        let hashed_higher_address = keccak256(HIGHER_ADDRESS);
1234        let hashed_storage = keccak256(STORAGE);
1235
1236        provider_rw
1237            .tx_ref()
1238            .put::<tables::HashedStorages>(
1239                hashed_address,
1240                StorageEntry { key: hashed_storage, value: U256::from(100) },
1241            )
1242            .unwrap();
1243        provider_rw
1244            .tx_ref()
1245            .put::<tables::HashedStorages>(
1246                hashed_higher_address,
1247                StorageEntry { key: hashed_storage, value: U256::from(1000) },
1248            )
1249            .unwrap();
1250        provider_rw
1251            .tx_ref()
1252            .put::<tables::HashedAccounts>(
1253                hashed_address,
1254                Account { nonce: 1, balance: U256::from(1000), bytecode_hash: None },
1255            )
1256            .unwrap();
1257        provider_rw
1258            .tx_ref()
1259            .put::<tables::HashedAccounts>(
1260                hashed_higher_address,
1261                Account { nonce: 1, balance: U256::from(2000), bytecode_hash: None },
1262            )
1263            .unwrap();
1264        provider_rw.commit().unwrap();
1265
1266        let db = factory.provider().unwrap();
1267
1268        assert!(matches!(
1269            HistoricalStateProviderRef::new(&db, 0).storage(ADDRESS, STORAGE),
1270            Ok(None)
1271        ));
1272        assert!(matches!(
1273            HistoricalStateProviderRef::new(&db, 3).storage(ADDRESS, STORAGE),
1274            Ok(Some(U256::ZERO))
1275        ));
1276        assert!(matches!(
1277            HistoricalStateProviderRef::new(&db, 4).storage(ADDRESS, STORAGE),
1278            Ok(Some(v)) if v == U256::from(7)
1279        ));
1280        assert!(matches!(
1281            HistoricalStateProviderRef::new(&db, 7).storage(ADDRESS, STORAGE),
1282            Ok(Some(v)) if v == U256::from(7)
1283        ));
1284        assert!(matches!(
1285            HistoricalStateProviderRef::new(&db, 9).storage(ADDRESS, STORAGE),
1286            Ok(Some(v)) if v == U256::from(10)
1287        ));
1288        assert!(matches!(
1289            HistoricalStateProviderRef::new(&db, 10).storage(ADDRESS, STORAGE),
1290            Ok(Some(v)) if v == U256::from(10)
1291        ));
1292        assert!(matches!(
1293            HistoricalStateProviderRef::new(&db, 11).storage(ADDRESS, STORAGE),
1294            Ok(Some(v)) if v == U256::from(15)
1295        ));
1296        assert!(matches!(
1297            HistoricalStateProviderRef::new(&db, 16).storage(ADDRESS, STORAGE),
1298            Ok(Some(v)) if v == U256::from(100)
1299        ));
1300        assert!(matches!(
1301            HistoricalStateProviderRef::new(&db, 1).storage(HIGHER_ADDRESS, STORAGE),
1302            Ok(None)
1303        ));
1304        assert!(matches!(
1305            HistoricalStateProviderRef::new(&db, 1000).storage(HIGHER_ADDRESS, STORAGE),
1306            Ok(Some(v)) if v == U256::from(1000)
1307        ));
1308    }
1309
1310    #[test]
1311    fn test_needs_prev_shard_check() {
1312        // Only needs check when rank == 0 and found_block != block_number
1313        assert!(needs_prev_shard_check(0, Some(10), 5));
1314        assert!(needs_prev_shard_check(0, None, 5));
1315        assert!(!needs_prev_shard_check(0, Some(5), 5)); // found_block == block_number
1316        assert!(!needs_prev_shard_check(1, Some(10), 5)); // rank > 0
1317    }
1318}