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