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::{masked::MaskedTrieCursorFactory, 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_tx(|rocks_tx_ref| {
160            let mut reader = EitherReader::new_accounts_history(self.provider, rocks_tx_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_tx(|rocks_tx_ref| {
185            let mut reader = EitherReader::new_storages_history(self.provider, rocks_tx_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                MaskedTrieCursorFactory::new(
529                    InMemoryTrieCursorFactory::new(
530                        reth_trie_db::DatabaseTrieCursorFactory::<_, A>::new(self.tx()),
531                        &nodes_sorted,
532                    ),
533                    input.prefix_sets.freeze(),
534                ),
535                HashedPostStateCursorFactory::new(
536                    reth_trie_db::DatabaseHashedCursorFactory::new(self.tx()),
537                    &state_sorted,
538                ),
539            )
540            .always_include_root_node()
541            .compute(target)
542            .map_err(ProviderError::from)
543            .map(|hm| hm.into_values().collect())
544        })
545    }
546}
547
548impl<Provider> HashedPostStateProvider for HistoricalStateProviderRef<'_, Provider> {
549    fn hashed_post_state(&self, bundle_state: &revm_database::BundleState) -> HashedPostState {
550        HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state())
551    }
552}
553
554impl<
555        Provider: DBProvider
556            + BlockNumReader
557            + BlockHashReader
558            + ChangeSetReader
559            + StorageChangeSetReader
560            + StorageSettingsCache
561            + RocksDBProviderFactory
562            + NodePrimitivesProvider,
563    > StateProvider for HistoricalStateProviderRef<'_, Provider>
564{
565    /// Expects a plain (unhashed) storage key slot.
566    fn storage(
567        &self,
568        address: Address,
569        storage_key: StorageKey,
570    ) -> ProviderResult<Option<StorageValue>> {
571        self.storage_by_lookup_key(address, storage_key)
572    }
573}
574
575impl<Provider: DBProvider + BlockNumReader> BytecodeReader
576    for HistoricalStateProviderRef<'_, Provider>
577{
578    /// Get account code by its hash
579    fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult<Option<Bytecode>> {
580        self.tx().get_by_encoded_key::<tables::Bytecodes>(code_hash).map_err(Into::into)
581    }
582}
583
584/// State provider for a given block number.
585/// For more detailed description, see [`HistoricalStateProviderRef`].
586#[derive(Debug)]
587pub struct HistoricalStateProvider<Provider> {
588    /// Database provider.
589    provider: Provider,
590    /// State at the block number is the main indexer of the state.
591    block_number: BlockNumber,
592    /// Lowest blocks at which different parts of the state are available.
593    lowest_available_blocks: LowestAvailableBlocks,
594}
595
596impl<Provider: DBProvider + ChangeSetReader + StorageChangeSetReader + BlockNumReader>
597    HistoricalStateProvider<Provider>
598{
599    /// Create new `StateProvider` for historical block number
600    pub fn new(provider: Provider, block_number: BlockNumber) -> Self {
601        Self { provider, block_number, lowest_available_blocks: Default::default() }
602    }
603
604    /// Set the lowest block number at which the account history is available.
605    pub const fn with_lowest_available_account_history_block_number(
606        mut self,
607        block_number: BlockNumber,
608    ) -> Self {
609        self.lowest_available_blocks.account_history_block_number = Some(block_number);
610        self
611    }
612
613    /// Set the lowest block number at which the storage history is available.
614    pub const fn with_lowest_available_storage_history_block_number(
615        mut self,
616        block_number: BlockNumber,
617    ) -> Self {
618        self.lowest_available_blocks.storage_history_block_number = Some(block_number);
619        self
620    }
621
622    /// Returns a new provider that takes the `TX` as reference
623    #[inline(always)]
624    const fn as_ref(&self) -> HistoricalStateProviderRef<'_, Provider> {
625        HistoricalStateProviderRef::new_with_lowest_available_blocks(
626            &self.provider,
627            self.block_number,
628            self.lowest_available_blocks,
629        )
630    }
631}
632
633// Delegates all provider impls to [HistoricalStateProviderRef]
634reth_storage_api::macros::delegate_provider_impls!(HistoricalStateProvider<Provider> where [Provider: DBProvider + BlockNumReader + BlockHashReader + ChangeSetReader + StorageChangeSetReader + StorageSettingsCache + RocksDBProviderFactory + NodePrimitivesProvider]);
635
636/// Lowest blocks at which different parts of the state are available.
637/// They may be [Some] if pruning is enabled.
638#[derive(Clone, Copy, Debug, Default)]
639pub struct LowestAvailableBlocks {
640    /// Lowest block number at which the account history is available. It may not be available if
641    /// [`reth_prune_types::PruneSegment::AccountHistory`] was pruned.
642    /// [`Option::None`] means all history is available.
643    pub account_history_block_number: Option<BlockNumber>,
644    /// Lowest block number at which the storage history is available. It may not be available if
645    /// [`reth_prune_types::PruneSegment::StorageHistory`] was pruned.
646    /// [`Option::None`] means all history is available.
647    pub storage_history_block_number: Option<BlockNumber>,
648}
649
650impl LowestAvailableBlocks {
651    /// Check if account history is available at the provided block number, i.e. lowest available
652    /// block number for account history is less than or equal to the provided block number.
653    pub fn is_account_history_available(&self, at: BlockNumber) -> bool {
654        self.account_history_block_number.map(|block_number| block_number <= at).unwrap_or(true)
655    }
656
657    /// Check if storage history is available at the provided block number, i.e. lowest available
658    /// block number for storage history is less than or equal to the provided block number.
659    pub fn is_storage_history_available(&self, at: BlockNumber) -> bool {
660        self.storage_history_block_number.map(|block_number| block_number <= at).unwrap_or(true)
661    }
662}
663
664/// Computes the rank and finds the next modification block in a history shard.
665///
666/// Given a `block_number`, this function returns:
667/// - `rank`: The number of entries strictly before `block_number` in the shard
668/// - `found_block`: The block number at position `rank` (i.e., the first block >= `block_number`
669///   where a modification occurred), or `None` if `rank` is out of bounds
670///
671/// The rank is adjusted when `block_number` exactly matches an entry in the shard,
672/// so that `found_block` always returns the modification at or after the target.
673///
674/// This logic is shared between MDBX cursor-based lookups and `RocksDB` iterator lookups.
675#[inline]
676pub fn compute_history_rank(
677    chunk: &reth_db_api::BlockNumberList,
678    block_number: BlockNumber,
679) -> (u64, Option<u64>) {
680    let mut rank = chunk.rank(block_number);
681    // `rank(block_number)` returns count of entries <= block_number.
682    // We want the first entry >= block_number, so if block_number is in the shard,
683    // we need to step back one position to point at it (not past it).
684    if rank.checked_sub(1).and_then(|r| chunk.select(r)) == Some(block_number) {
685        rank -= 1;
686    }
687    (rank, chunk.select(rank))
688}
689
690/// Checks if a previous shard lookup is needed to determine if we're before the first write.
691///
692/// Returns `true` when `rank == 0` (first entry in shard) and the found block doesn't match
693/// the target block number. In this case, we need to check if there's a previous shard.
694#[inline]
695pub fn needs_prev_shard_check(
696    rank: u64,
697    found_block: Option<u64>,
698    block_number: BlockNumber,
699) -> bool {
700    rank == 0 && found_block != Some(block_number)
701}
702
703/// Generic history lookup for sharded history tables.
704///
705/// Seeks to the shard containing `block_number`, verifies the key via `key_filter`,
706/// and checks previous shard to detect if we're before the first write.
707pub fn history_info<T, K, C>(
708    cursor: &mut C,
709    key: K,
710    block_number: BlockNumber,
711    key_filter: impl Fn(&K) -> bool,
712    lowest_available_block_number: Option<BlockNumber>,
713) -> ProviderResult<HistoryInfo>
714where
715    T: Table<Key = K, Value = BlockNumberList>,
716    C: DbCursorRO<T>,
717{
718    // Lookup the history chunk in the history index. If the key does not appear in the
719    // index, the first chunk for the next key will be returned so we filter out chunks that
720    // have a different key.
721    if let Some(chunk) = cursor.seek(key)?.filter(|(k, _)| key_filter(k)).map(|x| x.1) {
722        let (rank, found_block) = compute_history_rank(&chunk, block_number);
723
724        // If our block is before the first entry in the index chunk and this first entry
725        // doesn't equal to our block, it might be before the first write ever. To check, we
726        // look at the previous entry and check if the key is the same.
727        // This check is worth it, the `cursor.prev()` check is rarely triggered (the if will
728        // short-circuit) and when it passes we save a full seek into the changeset/plain state
729        // table.
730        let is_before_first_write = needs_prev_shard_check(rank, found_block, block_number) &&
731            !cursor.prev()?.is_some_and(|(k, _)| key_filter(&k));
732
733        Ok(HistoryInfo::from_lookup(
734            found_block,
735            is_before_first_write,
736            lowest_available_block_number,
737        ))
738    } else if lowest_available_block_number.is_some() {
739        // The key may have been written, but due to pruning we may not have changesets and
740        // history, so we need to make a plain state lookup.
741        Ok(HistoryInfo::MaybeInPlainState)
742    } else {
743        // The key has not been written to at all.
744        Ok(HistoryInfo::NotYetWritten)
745    }
746}
747
748#[cfg(test)]
749mod tests {
750    use super::needs_prev_shard_check;
751    use crate::{
752        providers::state::historical::{HistoryInfo, LowestAvailableBlocks},
753        test_utils::create_test_provider_factory,
754        AccountReader, HistoricalStateProvider, HistoricalStateProviderRef, RocksDBProviderFactory,
755        StateProvider,
756    };
757    use alloy_primitives::{address, b256, Address, B256, U256};
758    use reth_db_api::{
759        models::{storage_sharded_key::StorageShardedKey, AccountBeforeTx, ShardedKey},
760        tables,
761        transaction::{DbTx, DbTxMut},
762        BlockNumberList,
763    };
764    use reth_primitives_traits::{Account, StorageEntry};
765    use reth_storage_api::{
766        BlockHashReader, BlockNumReader, ChangeSetReader, DBProvider, DatabaseProviderFactory,
767        NodePrimitivesProvider, StorageChangeSetReader, StorageSettingsCache,
768    };
769    use reth_storage_errors::provider::ProviderError;
770
771    const ADDRESS: Address = address!("0x0000000000000000000000000000000000000001");
772    const HIGHER_ADDRESS: Address = address!("0x0000000000000000000000000000000000000005");
773    const STORAGE: B256 =
774        b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
775
776    const fn assert_state_provider<T: StateProvider>() {}
777    #[expect(dead_code)]
778    const fn assert_historical_state_provider<
779        T: DBProvider
780            + BlockNumReader
781            + BlockHashReader
782            + ChangeSetReader
783            + StorageChangeSetReader
784            + StorageSettingsCache
785            + RocksDBProviderFactory
786            + NodePrimitivesProvider,
787    >() {
788        assert_state_provider::<HistoricalStateProvider<T>>();
789    }
790
791    #[test]
792    fn history_provider_get_account() {
793        let factory = create_test_provider_factory();
794        let tx = factory.provider_rw().unwrap().into_tx();
795
796        tx.put::<tables::AccountsHistory>(
797            ShardedKey { key: ADDRESS, highest_block_number: 7 },
798            BlockNumberList::new([1, 3, 7]).unwrap(),
799        )
800        .unwrap();
801        tx.put::<tables::AccountsHistory>(
802            ShardedKey { key: ADDRESS, highest_block_number: u64::MAX },
803            BlockNumberList::new([10, 15]).unwrap(),
804        )
805        .unwrap();
806        tx.put::<tables::AccountsHistory>(
807            ShardedKey { key: HIGHER_ADDRESS, highest_block_number: u64::MAX },
808            BlockNumberList::new([4]).unwrap(),
809        )
810        .unwrap();
811
812        let acc_plain = Account { nonce: 100, balance: U256::ZERO, bytecode_hash: None };
813        let acc_at15 = Account { nonce: 15, balance: U256::ZERO, bytecode_hash: None };
814        let acc_at10 = Account { nonce: 10, balance: U256::ZERO, bytecode_hash: None };
815        let acc_at7 = Account { nonce: 7, balance: U256::ZERO, bytecode_hash: None };
816        let acc_at3 = Account { nonce: 3, balance: U256::ZERO, bytecode_hash: None };
817
818        let higher_acc_plain = Account { nonce: 4, balance: U256::ZERO, bytecode_hash: None };
819
820        // setup
821        tx.put::<tables::AccountChangeSets>(1, AccountBeforeTx { address: ADDRESS, info: None })
822            .unwrap();
823        tx.put::<tables::AccountChangeSets>(
824            3,
825            AccountBeforeTx { address: ADDRESS, info: Some(acc_at3) },
826        )
827        .unwrap();
828        tx.put::<tables::AccountChangeSets>(
829            4,
830            AccountBeforeTx { address: HIGHER_ADDRESS, info: None },
831        )
832        .unwrap();
833        tx.put::<tables::AccountChangeSets>(
834            7,
835            AccountBeforeTx { address: ADDRESS, info: Some(acc_at7) },
836        )
837        .unwrap();
838        tx.put::<tables::AccountChangeSets>(
839            10,
840            AccountBeforeTx { address: ADDRESS, info: Some(acc_at10) },
841        )
842        .unwrap();
843        tx.put::<tables::AccountChangeSets>(
844            15,
845            AccountBeforeTx { address: ADDRESS, info: Some(acc_at15) },
846        )
847        .unwrap();
848
849        // setup plain state
850        tx.put::<tables::PlainAccountState>(ADDRESS, acc_plain).unwrap();
851        tx.put::<tables::PlainAccountState>(HIGHER_ADDRESS, higher_acc_plain).unwrap();
852        tx.commit().unwrap();
853
854        let db = factory.provider().unwrap();
855
856        // run
857        assert!(matches!(
858            HistoricalStateProviderRef::new(&db, 1).basic_account(&ADDRESS),
859            Ok(None)
860        ));
861        assert!(matches!(
862            HistoricalStateProviderRef::new(&db, 2).basic_account(&ADDRESS),
863            Ok(Some(acc)) if acc == acc_at3
864        ));
865        assert!(matches!(
866            HistoricalStateProviderRef::new(&db, 3).basic_account(&ADDRESS),
867            Ok(Some(acc)) if acc == acc_at3
868        ));
869        assert!(matches!(
870            HistoricalStateProviderRef::new(&db, 4).basic_account(&ADDRESS),
871            Ok(Some(acc)) if acc == acc_at7
872        ));
873        assert!(matches!(
874            HistoricalStateProviderRef::new(&db, 7).basic_account(&ADDRESS),
875            Ok(Some(acc)) if acc == acc_at7
876        ));
877        assert!(matches!(
878            HistoricalStateProviderRef::new(&db, 9).basic_account(&ADDRESS),
879            Ok(Some(acc)) if acc == acc_at10
880        ));
881        assert!(matches!(
882            HistoricalStateProviderRef::new(&db, 10).basic_account(&ADDRESS),
883            Ok(Some(acc)) if acc == acc_at10
884        ));
885        assert!(matches!(
886            HistoricalStateProviderRef::new(&db, 11).basic_account(&ADDRESS),
887            Ok(Some(acc)) if acc == acc_at15
888        ));
889        assert!(matches!(
890            HistoricalStateProviderRef::new(&db, 16).basic_account(&ADDRESS),
891            Ok(Some(acc)) if acc == acc_plain
892        ));
893
894        assert!(matches!(
895            HistoricalStateProviderRef::new(&db, 1).basic_account(&HIGHER_ADDRESS),
896            Ok(None)
897        ));
898        assert!(matches!(
899            HistoricalStateProviderRef::new(&db, 1000).basic_account(&HIGHER_ADDRESS),
900            Ok(Some(acc)) if acc == higher_acc_plain
901        ));
902    }
903
904    #[test]
905    fn history_provider_get_storage() {
906        let factory = create_test_provider_factory();
907        let tx = factory.provider_rw().unwrap().into_tx();
908
909        tx.put::<tables::StoragesHistory>(
910            StorageShardedKey {
911                address: ADDRESS,
912                sharded_key: ShardedKey { key: STORAGE, highest_block_number: 7 },
913            },
914            BlockNumberList::new([3, 7]).unwrap(),
915        )
916        .unwrap();
917        tx.put::<tables::StoragesHistory>(
918            StorageShardedKey {
919                address: ADDRESS,
920                sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
921            },
922            BlockNumberList::new([10, 15]).unwrap(),
923        )
924        .unwrap();
925        tx.put::<tables::StoragesHistory>(
926            StorageShardedKey {
927                address: HIGHER_ADDRESS,
928                sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
929            },
930            BlockNumberList::new([4]).unwrap(),
931        )
932        .unwrap();
933
934        let higher_entry_plain = StorageEntry { key: STORAGE, value: U256::from(1000) };
935        let higher_entry_at4 = StorageEntry { key: STORAGE, value: U256::from(0) };
936        let entry_plain = StorageEntry { key: STORAGE, value: U256::from(100) };
937        let entry_at15 = StorageEntry { key: STORAGE, value: U256::from(15) };
938        let entry_at10 = StorageEntry { key: STORAGE, value: U256::from(10) };
939        let entry_at7 = StorageEntry { key: STORAGE, value: U256::from(7) };
940        let entry_at3 = StorageEntry { key: STORAGE, value: U256::from(0) };
941
942        // setup
943        tx.put::<tables::StorageChangeSets>((3, ADDRESS).into(), entry_at3).unwrap();
944        tx.put::<tables::StorageChangeSets>((4, HIGHER_ADDRESS).into(), higher_entry_at4).unwrap();
945        tx.put::<tables::StorageChangeSets>((7, ADDRESS).into(), entry_at7).unwrap();
946        tx.put::<tables::StorageChangeSets>((10, ADDRESS).into(), entry_at10).unwrap();
947        tx.put::<tables::StorageChangeSets>((15, ADDRESS).into(), entry_at15).unwrap();
948
949        // setup plain state
950        tx.put::<tables::PlainStorageState>(ADDRESS, entry_plain).unwrap();
951        tx.put::<tables::PlainStorageState>(HIGHER_ADDRESS, higher_entry_plain).unwrap();
952        tx.commit().unwrap();
953
954        let db = factory.provider().unwrap();
955
956        // run
957        assert!(matches!(
958            HistoricalStateProviderRef::new(&db, 0).storage(ADDRESS, STORAGE),
959            Ok(None)
960        ));
961        assert!(matches!(
962            HistoricalStateProviderRef::new(&db, 3).storage(ADDRESS, STORAGE),
963            Ok(Some(U256::ZERO))
964        ));
965        assert!(matches!(
966            HistoricalStateProviderRef::new(&db, 4).storage(ADDRESS, STORAGE),
967            Ok(Some(expected_value)) if expected_value == entry_at7.value
968        ));
969        assert!(matches!(
970            HistoricalStateProviderRef::new(&db, 7).storage(ADDRESS, STORAGE),
971            Ok(Some(expected_value)) if expected_value == entry_at7.value
972        ));
973        assert!(matches!(
974            HistoricalStateProviderRef::new(&db, 9).storage(ADDRESS, STORAGE),
975            Ok(Some(expected_value)) if expected_value == entry_at10.value
976        ));
977        assert!(matches!(
978            HistoricalStateProviderRef::new(&db, 10).storage(ADDRESS, STORAGE),
979            Ok(Some(expected_value)) if expected_value == entry_at10.value
980        ));
981        assert!(matches!(
982            HistoricalStateProviderRef::new(&db, 11).storage(ADDRESS, STORAGE),
983            Ok(Some(expected_value)) if expected_value == entry_at15.value
984        ));
985        assert!(matches!(
986            HistoricalStateProviderRef::new(&db, 16).storage(ADDRESS, STORAGE),
987            Ok(Some(expected_value)) if expected_value == entry_plain.value
988        ));
989        assert!(matches!(
990            HistoricalStateProviderRef::new(&db, 1).storage(HIGHER_ADDRESS, STORAGE),
991            Ok(None)
992        ));
993        assert!(matches!(
994            HistoricalStateProviderRef::new(&db, 1000).storage(HIGHER_ADDRESS, STORAGE),
995            Ok(Some(expected_value)) if expected_value == higher_entry_plain.value
996        ));
997    }
998
999    #[test]
1000    fn history_provider_unavailable() {
1001        let factory = create_test_provider_factory();
1002        let db = factory.database_provider_rw().unwrap();
1003
1004        // provider block_number < lowest available block number,
1005        // i.e. state at provider block is pruned
1006        let provider = HistoricalStateProviderRef::new_with_lowest_available_blocks(
1007            &db,
1008            2,
1009            LowestAvailableBlocks {
1010                account_history_block_number: Some(3),
1011                storage_history_block_number: Some(3),
1012            },
1013        );
1014        assert!(matches!(
1015            provider.account_history_lookup(ADDRESS),
1016            Err(ProviderError::StateAtBlockPruned(number)) if number == provider.block_number
1017        ));
1018        assert!(matches!(
1019            provider.storage_history_lookup(ADDRESS, STORAGE),
1020            Err(ProviderError::StateAtBlockPruned(number)) if number == provider.block_number
1021        ));
1022
1023        // provider block_number == lowest available block number,
1024        // i.e. state at provider block is available
1025        let provider = HistoricalStateProviderRef::new_with_lowest_available_blocks(
1026            &db,
1027            2,
1028            LowestAvailableBlocks {
1029                account_history_block_number: Some(2),
1030                storage_history_block_number: Some(2),
1031            },
1032        );
1033        assert!(matches!(
1034            provider.account_history_lookup(ADDRESS),
1035            Ok(HistoryInfo::MaybeInPlainState)
1036        ));
1037        assert!(matches!(
1038            provider.storage_history_lookup(ADDRESS, STORAGE),
1039            Ok(HistoryInfo::MaybeInPlainState)
1040        ));
1041
1042        // provider block_number == lowest available block number,
1043        // i.e. state at provider block is available
1044        let provider = HistoricalStateProviderRef::new_with_lowest_available_blocks(
1045            &db,
1046            2,
1047            LowestAvailableBlocks {
1048                account_history_block_number: Some(1),
1049                storage_history_block_number: Some(1),
1050            },
1051        );
1052        assert!(matches!(
1053            provider.account_history_lookup(ADDRESS),
1054            Ok(HistoryInfo::MaybeInPlainState)
1055        ));
1056        assert!(matches!(
1057            provider.storage_history_lookup(ADDRESS, STORAGE),
1058            Ok(HistoryInfo::MaybeInPlainState)
1059        ));
1060    }
1061
1062    #[test]
1063    fn test_history_info_from_lookup() {
1064        // Before first write, no pruning → not yet written
1065        assert_eq!(HistoryInfo::from_lookup(Some(10), true, None), HistoryInfo::NotYetWritten);
1066        assert_eq!(HistoryInfo::from_lookup(None, true, None), HistoryInfo::NotYetWritten);
1067
1068        // Before first write WITH pruning → check changeset (pruning may have removed history)
1069        assert_eq!(HistoryInfo::from_lookup(Some(10), true, Some(5)), HistoryInfo::InChangeset(10));
1070        assert_eq!(HistoryInfo::from_lookup(None, true, Some(5)), HistoryInfo::NotYetWritten);
1071
1072        // Not before first write → check changeset or plain state
1073        assert_eq!(HistoryInfo::from_lookup(Some(10), false, None), HistoryInfo::InChangeset(10));
1074        assert_eq!(HistoryInfo::from_lookup(None, false, None), HistoryInfo::InPlainState);
1075    }
1076
1077    #[test]
1078    fn history_provider_get_storage_legacy() {
1079        let factory = create_test_provider_factory();
1080
1081        assert!(!factory.provider().unwrap().cached_storage_settings().use_hashed_state());
1082
1083        let tx = factory.provider_rw().unwrap().into_tx();
1084
1085        tx.put::<tables::StoragesHistory>(
1086            StorageShardedKey {
1087                address: ADDRESS,
1088                sharded_key: ShardedKey { key: STORAGE, highest_block_number: 7 },
1089            },
1090            BlockNumberList::new([3, 7]).unwrap(),
1091        )
1092        .unwrap();
1093        tx.put::<tables::StoragesHistory>(
1094            StorageShardedKey {
1095                address: ADDRESS,
1096                sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
1097            },
1098            BlockNumberList::new([10, 15]).unwrap(),
1099        )
1100        .unwrap();
1101        tx.put::<tables::StoragesHistory>(
1102            StorageShardedKey {
1103                address: HIGHER_ADDRESS,
1104                sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
1105            },
1106            BlockNumberList::new([4]).unwrap(),
1107        )
1108        .unwrap();
1109
1110        let higher_entry_plain = StorageEntry { key: STORAGE, value: U256::from(1000) };
1111        let higher_entry_at4 = StorageEntry { key: STORAGE, value: U256::from(0) };
1112        let entry_plain = StorageEntry { key: STORAGE, value: U256::from(100) };
1113        let entry_at15 = StorageEntry { key: STORAGE, value: U256::from(15) };
1114        let entry_at10 = StorageEntry { key: STORAGE, value: U256::from(10) };
1115        let entry_at7 = StorageEntry { key: STORAGE, value: U256::from(7) };
1116        let entry_at3 = StorageEntry { key: STORAGE, value: U256::from(0) };
1117
1118        tx.put::<tables::StorageChangeSets>((3, ADDRESS).into(), entry_at3).unwrap();
1119        tx.put::<tables::StorageChangeSets>((4, HIGHER_ADDRESS).into(), higher_entry_at4).unwrap();
1120        tx.put::<tables::StorageChangeSets>((7, ADDRESS).into(), entry_at7).unwrap();
1121        tx.put::<tables::StorageChangeSets>((10, ADDRESS).into(), entry_at10).unwrap();
1122        tx.put::<tables::StorageChangeSets>((15, ADDRESS).into(), entry_at15).unwrap();
1123
1124        tx.put::<tables::PlainStorageState>(ADDRESS, entry_plain).unwrap();
1125        tx.put::<tables::PlainStorageState>(HIGHER_ADDRESS, higher_entry_plain).unwrap();
1126        tx.commit().unwrap();
1127
1128        let db = factory.provider().unwrap();
1129
1130        assert!(matches!(
1131            HistoricalStateProviderRef::new(&db, 0).storage(ADDRESS, STORAGE),
1132            Ok(None)
1133        ));
1134        assert!(matches!(
1135            HistoricalStateProviderRef::new(&db, 3).storage(ADDRESS, STORAGE),
1136            Ok(Some(U256::ZERO))
1137        ));
1138        assert!(matches!(
1139            HistoricalStateProviderRef::new(&db, 4).storage(ADDRESS, STORAGE),
1140            Ok(Some(expected_value)) if expected_value == entry_at7.value
1141        ));
1142        assert!(matches!(
1143            HistoricalStateProviderRef::new(&db, 7).storage(ADDRESS, STORAGE),
1144            Ok(Some(expected_value)) if expected_value == entry_at7.value
1145        ));
1146        assert!(matches!(
1147            HistoricalStateProviderRef::new(&db, 9).storage(ADDRESS, STORAGE),
1148            Ok(Some(expected_value)) if expected_value == entry_at10.value
1149        ));
1150        assert!(matches!(
1151            HistoricalStateProviderRef::new(&db, 10).storage(ADDRESS, STORAGE),
1152            Ok(Some(expected_value)) if expected_value == entry_at10.value
1153        ));
1154        assert!(matches!(
1155            HistoricalStateProviderRef::new(&db, 11).storage(ADDRESS, STORAGE),
1156            Ok(Some(expected_value)) if expected_value == entry_at15.value
1157        ));
1158        assert!(matches!(
1159            HistoricalStateProviderRef::new(&db, 16).storage(ADDRESS, STORAGE),
1160            Ok(Some(expected_value)) if expected_value == entry_plain.value
1161        ));
1162        assert!(matches!(
1163            HistoricalStateProviderRef::new(&db, 1).storage(HIGHER_ADDRESS, STORAGE),
1164            Ok(None)
1165        ));
1166        assert!(matches!(
1167            HistoricalStateProviderRef::new(&db, 1000).storage(HIGHER_ADDRESS, STORAGE),
1168            Ok(Some(expected_value)) if expected_value == higher_entry_plain.value
1169        ));
1170    }
1171
1172    #[test]
1173    #[cfg(all(unix, feature = "rocksdb"))]
1174    fn history_provider_get_storage_hashed_state() {
1175        use crate::BlockWriter;
1176        use alloy_primitives::keccak256;
1177        use reth_db_api::models::StorageSettings;
1178        use reth_execution_types::ExecutionOutcome;
1179        use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};
1180        use revm_database::BundleState;
1181        use std::collections::HashMap;
1182
1183        let factory = create_test_provider_factory();
1184        factory.set_storage_settings_cache(StorageSettings::v2());
1185
1186        let slot = U256::from_be_bytes(*STORAGE);
1187        let account: revm_state::AccountInfo =
1188            Account { nonce: 1, balance: U256::from(1000), bytecode_hash: None }.into();
1189        let higher_account: revm_state::AccountInfo =
1190            Account { nonce: 1, balance: U256::from(2000), bytecode_hash: None }.into();
1191
1192        let mut rng = generators::rng();
1193        let blocks = random_block_range(
1194            &mut rng,
1195            0..=15,
1196            BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..1, ..Default::default() },
1197        );
1198
1199        let mut addr_storage = HashMap::default();
1200        addr_storage.insert(slot, (U256::ZERO, U256::from(100)));
1201        let mut higher_storage = HashMap::default();
1202        higher_storage.insert(slot, (U256::ZERO, U256::from(1000)));
1203
1204        type Revert = Vec<(Address, Option<Option<revm_state::AccountInfo>>, Vec<(U256, U256)>)>;
1205        let mut reverts: Vec<Revert> = vec![Vec::new(); 16];
1206
1207        reverts[3] = vec![(ADDRESS, Some(Some(account.clone())), vec![(slot, U256::ZERO)])];
1208        reverts[4] =
1209            vec![(HIGHER_ADDRESS, Some(Some(higher_account.clone())), vec![(slot, U256::ZERO)])];
1210        reverts[7] = vec![(ADDRESS, Some(Some(account.clone())), vec![(slot, U256::from(7))])];
1211        reverts[10] = vec![(ADDRESS, Some(Some(account.clone())), vec![(slot, U256::from(10))])];
1212        reverts[15] = vec![(ADDRESS, Some(Some(account.clone())), vec![(slot, U256::from(15))])];
1213
1214        let bundle = BundleState::new(
1215            [
1216                (ADDRESS, None, Some(account), addr_storage),
1217                (HIGHER_ADDRESS, None, Some(higher_account), higher_storage),
1218            ],
1219            reverts,
1220            [],
1221        );
1222
1223        let provider_rw = factory.provider_rw().unwrap();
1224        provider_rw
1225            .append_blocks_with_state(
1226                blocks
1227                    .into_iter()
1228                    .map(|b| b.try_recover().expect("failed to seal block with senders"))
1229                    .collect(),
1230                &ExecutionOutcome { bundle, first_block: 0, ..Default::default() },
1231                Default::default(),
1232            )
1233            .unwrap();
1234
1235        let hashed_address = keccak256(ADDRESS);
1236        let hashed_higher_address = keccak256(HIGHER_ADDRESS);
1237        let hashed_storage = keccak256(STORAGE);
1238
1239        provider_rw
1240            .tx_ref()
1241            .put::<tables::HashedStorages>(
1242                hashed_address,
1243                StorageEntry { key: hashed_storage, value: U256::from(100) },
1244            )
1245            .unwrap();
1246        provider_rw
1247            .tx_ref()
1248            .put::<tables::HashedStorages>(
1249                hashed_higher_address,
1250                StorageEntry { key: hashed_storage, value: U256::from(1000) },
1251            )
1252            .unwrap();
1253        provider_rw
1254            .tx_ref()
1255            .put::<tables::HashedAccounts>(
1256                hashed_address,
1257                Account { nonce: 1, balance: U256::from(1000), bytecode_hash: None },
1258            )
1259            .unwrap();
1260        provider_rw
1261            .tx_ref()
1262            .put::<tables::HashedAccounts>(
1263                hashed_higher_address,
1264                Account { nonce: 1, balance: U256::from(2000), bytecode_hash: None },
1265            )
1266            .unwrap();
1267        provider_rw.commit().unwrap();
1268
1269        let db = factory.provider().unwrap();
1270
1271        assert!(matches!(
1272            HistoricalStateProviderRef::new(&db, 0).storage(ADDRESS, STORAGE),
1273            Ok(None)
1274        ));
1275        assert!(matches!(
1276            HistoricalStateProviderRef::new(&db, 3).storage(ADDRESS, STORAGE),
1277            Ok(Some(U256::ZERO))
1278        ));
1279        assert!(matches!(
1280            HistoricalStateProviderRef::new(&db, 4).storage(ADDRESS, STORAGE),
1281            Ok(Some(v)) if v == U256::from(7)
1282        ));
1283        assert!(matches!(
1284            HistoricalStateProviderRef::new(&db, 7).storage(ADDRESS, STORAGE),
1285            Ok(Some(v)) if v == U256::from(7)
1286        ));
1287        assert!(matches!(
1288            HistoricalStateProviderRef::new(&db, 9).storage(ADDRESS, STORAGE),
1289            Ok(Some(v)) if v == U256::from(10)
1290        ));
1291        assert!(matches!(
1292            HistoricalStateProviderRef::new(&db, 10).storage(ADDRESS, STORAGE),
1293            Ok(Some(v)) if v == U256::from(10)
1294        ));
1295        assert!(matches!(
1296            HistoricalStateProviderRef::new(&db, 11).storage(ADDRESS, STORAGE),
1297            Ok(Some(v)) if v == U256::from(15)
1298        ));
1299        assert!(matches!(
1300            HistoricalStateProviderRef::new(&db, 16).storage(ADDRESS, STORAGE),
1301            Ok(Some(v)) if v == U256::from(100)
1302        ));
1303        assert!(matches!(
1304            HistoricalStateProviderRef::new(&db, 1).storage(HIGHER_ADDRESS, STORAGE),
1305            Ok(None)
1306        ));
1307        assert!(matches!(
1308            HistoricalStateProviderRef::new(&db, 1000).storage(HIGHER_ADDRESS, STORAGE),
1309            Ok(Some(v)) if v == U256::from(1000)
1310        ));
1311    }
1312
1313    #[test]
1314    fn test_needs_prev_shard_check() {
1315        // Only needs check when rank == 0 and found_block != block_number
1316        assert!(needs_prev_shard_check(0, Some(10), 5));
1317        assert!(needs_prev_shard_check(0, None, 5));
1318        assert!(!needs_prev_shard_check(0, Some(5), 5)); // found_block == block_number
1319        assert!(!needs_prev_shard_check(1, Some(10), 5)); // rank > 0
1320    }
1321}