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