Skip to main content

reth_provider/providers/state/
historical.rs

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