Skip to main content

reth_provider/providers/state/
historical.rs

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