reth_provider/providers/state/
historical.rs

1use crate::{
2    providers::state::macros::delegate_provider_impls, AccountReader, BlockHashReader,
3    HashedPostStateProvider, ProviderError, 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    models::{storage_sharded_key::StorageShardedKey, ShardedKey},
10    table::Table,
11    tables,
12    transaction::DbTx,
13    BlockNumberList,
14};
15use reth_primitives_traits::{Account, Bytecode};
16use reth_storage_api::{
17    BlockNumReader, DBProvider, StateCommitmentProvider, StateProofProvider, StorageRootProvider,
18};
19use reth_storage_errors::provider::ProviderResult;
20use reth_trie::{
21    proof::{Proof, StorageProof},
22    updates::TrieUpdates,
23    witness::TrieWitness,
24    AccountProof, HashedPostState, HashedStorage, MultiProof, MultiProofTargets, StateRoot,
25    StorageMultiProof, StorageRoot, TrieInput,
26};
27use reth_trie_db::{
28    DatabaseHashedPostState, DatabaseHashedStorage, DatabaseProof, DatabaseStateRoot,
29    DatabaseStorageProof, DatabaseStorageRoot, DatabaseTrieWitness, StateCommitment,
30};
31use std::fmt::Debug;
32
33/// State provider for a given block number which takes a tx reference.
34///
35/// Historical state provider accesses the state at the start of the provided block number.
36/// It means that all changes made in the provided block number are not included.
37///
38/// Historical state provider reads the following tables:
39/// - [`tables::AccountsHistory`]
40/// - [`tables::Bytecodes`]
41/// - [`tables::StoragesHistory`]
42/// - [`tables::AccountChangeSets`]
43/// - [`tables::StorageChangeSets`]
44#[derive(Debug)]
45pub struct HistoricalStateProviderRef<'b, Provider> {
46    /// Database provider
47    provider: &'b Provider,
48    /// Block number is main index for the history state of accounts and storages.
49    block_number: BlockNumber,
50    /// Lowest blocks at which different parts of the state are available.
51    lowest_available_blocks: LowestAvailableBlocks,
52}
53
54#[derive(Debug, Eq, PartialEq)]
55pub enum HistoryInfo {
56    NotYetWritten,
57    InChangeset(u64),
58    InPlainState,
59    MaybeInPlainState,
60}
61
62impl<'b, Provider: DBProvider + BlockNumReader + StateCommitmentProvider>
63    HistoricalStateProviderRef<'b, Provider>
64{
65    /// Create new `StateProvider` for historical block number
66    pub fn new(provider: &'b Provider, block_number: BlockNumber) -> Self {
67        Self { provider, block_number, lowest_available_blocks: Default::default() }
68    }
69
70    /// Create new `StateProvider` for historical block number and lowest block numbers at which
71    /// account & storage histories are available.
72    pub const fn new_with_lowest_available_blocks(
73        provider: &'b Provider,
74        block_number: BlockNumber,
75        lowest_available_blocks: LowestAvailableBlocks,
76    ) -> Self {
77        Self { provider, block_number, lowest_available_blocks }
78    }
79
80    /// Lookup an account in the `AccountsHistory` table
81    pub fn account_history_lookup(&self, address: Address) -> ProviderResult<HistoryInfo> {
82        if !self.lowest_available_blocks.is_account_history_available(self.block_number) {
83            return Err(ProviderError::StateAtBlockPruned(self.block_number))
84        }
85
86        // history key to search IntegerList of block number changesets.
87        let history_key = ShardedKey::new(address, self.block_number);
88        self.history_info::<tables::AccountsHistory, _>(
89            history_key,
90            |key| key.key == address,
91            self.lowest_available_blocks.account_history_block_number,
92        )
93    }
94
95    /// Lookup a storage key in the `StoragesHistory` table
96    pub fn storage_history_lookup(
97        &self,
98        address: Address,
99        storage_key: StorageKey,
100    ) -> ProviderResult<HistoryInfo> {
101        if !self.lowest_available_blocks.is_storage_history_available(self.block_number) {
102            return Err(ProviderError::StateAtBlockPruned(self.block_number))
103        }
104
105        // history key to search IntegerList of block number changesets.
106        let history_key = StorageShardedKey::new(address, storage_key, self.block_number);
107        self.history_info::<tables::StoragesHistory, _>(
108            history_key,
109            |key| key.address == address && key.sharded_key.key == storage_key,
110            self.lowest_available_blocks.storage_history_block_number,
111        )
112    }
113
114    /// Checks and returns `true` if distance to historical block exceeds the provided limit.
115    fn check_distance_against_limit(&self, limit: u64) -> ProviderResult<bool> {
116        let tip = self.provider.last_block_number()?;
117
118        Ok(tip.saturating_sub(self.block_number) > limit)
119    }
120
121    /// Retrieve revert hashed state for this history provider.
122    fn revert_state(&self) -> ProviderResult<HashedPostState> {
123        if !self.lowest_available_blocks.is_account_history_available(self.block_number) ||
124            !self.lowest_available_blocks.is_storage_history_available(self.block_number)
125        {
126            return Err(ProviderError::StateAtBlockPruned(self.block_number))
127        }
128
129        if self.check_distance_against_limit(EPOCH_SLOTS)? {
130            tracing::warn!(
131                target: "provider::historical_sp",
132                target = self.block_number,
133                "Attempt to calculate state root for an old block might result in OOM"
134            );
135        }
136
137        Ok(HashedPostState::from_reverts::<
138            <Provider::StateCommitment as StateCommitment>::KeyHasher,
139        >(self.tx(), self.block_number)?)
140    }
141
142    /// Retrieve revert hashed storage for this history provider and target address.
143    fn revert_storage(&self, address: Address) -> ProviderResult<HashedStorage> {
144        if !self.lowest_available_blocks.is_storage_history_available(self.block_number) {
145            return Err(ProviderError::StateAtBlockPruned(self.block_number))
146        }
147
148        if self.check_distance_against_limit(EPOCH_SLOTS * 10)? {
149            tracing::warn!(
150                target: "provider::historical_sp",
151                target = self.block_number,
152                "Attempt to calculate storage root for an old block might result in OOM"
153            );
154        }
155
156        Ok(HashedStorage::from_reverts(self.tx(), address, self.block_number)?)
157    }
158
159    fn history_info<T, K>(
160        &self,
161        key: K,
162        key_filter: impl Fn(&K) -> bool,
163        lowest_available_block_number: Option<BlockNumber>,
164    ) -> ProviderResult<HistoryInfo>
165    where
166        T: Table<Key = K, Value = BlockNumberList>,
167    {
168        let mut cursor = self.tx().cursor_read::<T>()?;
169
170        // Lookup the history chunk in the history index. If they key does not appear in the
171        // index, the first chunk for the next key will be returned so we filter out chunks that
172        // have a different key.
173        if let Some(chunk) = cursor.seek(key)?.filter(|(key, _)| key_filter(key)).map(|x| x.1 .0) {
174            // Get the rank of the first entry before or equal to our block.
175            let mut rank = chunk.rank(self.block_number);
176
177            // Adjust the rank, so that we have the rank of the first entry strictly before our
178            // block (not equal to it).
179            if rank.checked_sub(1).and_then(|rank| chunk.select(rank)) == Some(self.block_number) {
180                rank -= 1
181            };
182
183            let block_number = chunk.select(rank);
184
185            // If our block is before the first entry in the index chunk and this first entry
186            // doesn't equal to our block, it might be before the first write ever. To check, we
187            // look at the previous entry and check if the key is the same.
188            // This check is worth it, the `cursor.prev()` check is rarely triggered (the if will
189            // short-circuit) and when it passes we save a full seek into the changeset/plain state
190            // table.
191            if rank == 0 &&
192                block_number != Some(self.block_number) &&
193                !cursor.prev()?.is_some_and(|(key, _)| key_filter(&key))
194            {
195                if let (Some(_), Some(block_number)) = (lowest_available_block_number, block_number)
196                {
197                    // The key may have been written, but due to pruning we may not have changesets
198                    // and history, so we need to make a changeset lookup.
199                    Ok(HistoryInfo::InChangeset(block_number))
200                } else {
201                    // The key is written to, but only after our block.
202                    Ok(HistoryInfo::NotYetWritten)
203                }
204            } else if let Some(block_number) = block_number {
205                // The chunk contains an entry for a write after our block, return it.
206                Ok(HistoryInfo::InChangeset(block_number))
207            } else {
208                // The chunk does not contain an entry for a write after our block. This can only
209                // happen if this is the last chunk and so we need to look in the plain state.
210                Ok(HistoryInfo::InPlainState)
211            }
212        } else if lowest_available_block_number.is_some() {
213            // The key may have been written, but due to pruning we may not have changesets and
214            // history, so we need to make a plain state lookup.
215            Ok(HistoryInfo::MaybeInPlainState)
216        } else {
217            // The key has not been written to at all.
218            Ok(HistoryInfo::NotYetWritten)
219        }
220    }
221
222    /// Set the lowest block number at which the account history is available.
223    pub const fn with_lowest_available_account_history_block_number(
224        mut self,
225        block_number: BlockNumber,
226    ) -> Self {
227        self.lowest_available_blocks.account_history_block_number = Some(block_number);
228        self
229    }
230
231    /// Set the lowest block number at which the storage history is available.
232    pub const fn with_lowest_available_storage_history_block_number(
233        mut self,
234        block_number: BlockNumber,
235    ) -> Self {
236        self.lowest_available_blocks.storage_history_block_number = Some(block_number);
237        self
238    }
239}
240
241impl<Provider: DBProvider + BlockNumReader> HistoricalStateProviderRef<'_, Provider> {
242    fn tx(&self) -> &Provider::Tx {
243        self.provider.tx_ref()
244    }
245}
246
247impl<Provider: DBProvider + BlockNumReader + StateCommitmentProvider> AccountReader
248    for HistoricalStateProviderRef<'_, Provider>
249{
250    /// Get basic account information.
251    fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>> {
252        match self.account_history_lookup(*address)? {
253            HistoryInfo::NotYetWritten => Ok(None),
254            HistoryInfo::InChangeset(changeset_block_number) => Ok(self
255                .tx()
256                .cursor_dup_read::<tables::AccountChangeSets>()?
257                .seek_by_key_subkey(changeset_block_number, *address)?
258                .filter(|acc| &acc.address == address)
259                .ok_or(ProviderError::AccountChangesetNotFound {
260                    block_number: changeset_block_number,
261                    address: *address,
262                })?
263                .info),
264            HistoryInfo::InPlainState | HistoryInfo::MaybeInPlainState => {
265                Ok(self.tx().get_by_encoded_key::<tables::PlainAccountState>(address)?)
266            }
267        }
268    }
269}
270
271impl<Provider: DBProvider + BlockNumReader + BlockHashReader> BlockHashReader
272    for HistoricalStateProviderRef<'_, Provider>
273{
274    /// Get block hash by number.
275    fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>> {
276        self.provider.block_hash(number)
277    }
278
279    fn canonical_hashes_range(
280        &self,
281        start: BlockNumber,
282        end: BlockNumber,
283    ) -> ProviderResult<Vec<B256>> {
284        self.provider.canonical_hashes_range(start, end)
285    }
286}
287
288impl<Provider: DBProvider + BlockNumReader + StateCommitmentProvider> StateRootProvider
289    for HistoricalStateProviderRef<'_, Provider>
290{
291    fn state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> {
292        let mut revert_state = self.revert_state()?;
293        revert_state.extend(hashed_state);
294        StateRoot::overlay_root(self.tx(), revert_state)
295            .map_err(|err| ProviderError::Database(err.into()))
296    }
297
298    fn state_root_from_nodes(&self, mut input: TrieInput) -> ProviderResult<B256> {
299        input.prepend(self.revert_state()?);
300        StateRoot::overlay_root_from_nodes(self.tx(), input)
301            .map_err(|err| ProviderError::Database(err.into()))
302    }
303
304    fn state_root_with_updates(
305        &self,
306        hashed_state: HashedPostState,
307    ) -> ProviderResult<(B256, TrieUpdates)> {
308        let mut revert_state = self.revert_state()?;
309        revert_state.extend(hashed_state);
310        StateRoot::overlay_root_with_updates(self.tx(), revert_state)
311            .map_err(|err| ProviderError::Database(err.into()))
312    }
313
314    fn state_root_from_nodes_with_updates(
315        &self,
316        mut input: TrieInput,
317    ) -> ProviderResult<(B256, TrieUpdates)> {
318        input.prepend(self.revert_state()?);
319        StateRoot::overlay_root_from_nodes_with_updates(self.tx(), input)
320            .map_err(|err| ProviderError::Database(err.into()))
321    }
322}
323
324impl<Provider: DBProvider + BlockNumReader + StateCommitmentProvider> StorageRootProvider
325    for HistoricalStateProviderRef<'_, Provider>
326{
327    fn storage_root(
328        &self,
329        address: Address,
330        hashed_storage: HashedStorage,
331    ) -> ProviderResult<B256> {
332        let mut revert_storage = self.revert_storage(address)?;
333        revert_storage.extend(&hashed_storage);
334        StorageRoot::overlay_root(self.tx(), address, revert_storage)
335            .map_err(|err| ProviderError::Database(err.into()))
336    }
337
338    fn storage_proof(
339        &self,
340        address: Address,
341        slot: B256,
342        hashed_storage: HashedStorage,
343    ) -> ProviderResult<reth_trie::StorageProof> {
344        let mut revert_storage = self.revert_storage(address)?;
345        revert_storage.extend(&hashed_storage);
346        StorageProof::overlay_storage_proof(self.tx(), address, slot, revert_storage)
347            .map_err(ProviderError::from)
348    }
349
350    fn storage_multiproof(
351        &self,
352        address: Address,
353        slots: &[B256],
354        hashed_storage: HashedStorage,
355    ) -> ProviderResult<StorageMultiProof> {
356        let mut revert_storage = self.revert_storage(address)?;
357        revert_storage.extend(&hashed_storage);
358        StorageProof::overlay_storage_multiproof(self.tx(), address, slots, revert_storage)
359            .map_err(ProviderError::from)
360    }
361}
362
363impl<Provider: DBProvider + BlockNumReader + StateCommitmentProvider> StateProofProvider
364    for HistoricalStateProviderRef<'_, Provider>
365{
366    /// Get account and storage proofs.
367    fn proof(
368        &self,
369        mut input: TrieInput,
370        address: Address,
371        slots: &[B256],
372    ) -> ProviderResult<AccountProof> {
373        input.prepend(self.revert_state()?);
374        Proof::overlay_account_proof(self.tx(), input, address, slots).map_err(ProviderError::from)
375    }
376
377    fn multiproof(
378        &self,
379        mut input: TrieInput,
380        targets: MultiProofTargets,
381    ) -> ProviderResult<MultiProof> {
382        input.prepend(self.revert_state()?);
383        Proof::overlay_multiproof(self.tx(), input, targets).map_err(ProviderError::from)
384    }
385
386    fn witness(&self, mut input: TrieInput, target: HashedPostState) -> ProviderResult<Vec<Bytes>> {
387        input.prepend(self.revert_state()?);
388        TrieWitness::overlay_witness(self.tx(), input, target)
389            .map_err(ProviderError::from)
390            .map(|hm| hm.into_values().collect())
391    }
392}
393
394impl<Provider: StateCommitmentProvider> HashedPostStateProvider
395    for HistoricalStateProviderRef<'_, Provider>
396{
397    fn hashed_post_state(&self, bundle_state: &revm_database::BundleState) -> HashedPostState {
398        HashedPostState::from_bundle_state::<
399            <Provider::StateCommitment as StateCommitment>::KeyHasher,
400        >(bundle_state.state())
401    }
402}
403
404impl<Provider: DBProvider + BlockNumReader + BlockHashReader + StateCommitmentProvider>
405    StateProvider for HistoricalStateProviderRef<'_, Provider>
406{
407    /// Get storage.
408    fn storage(
409        &self,
410        address: Address,
411        storage_key: StorageKey,
412    ) -> ProviderResult<Option<StorageValue>> {
413        match self.storage_history_lookup(address, storage_key)? {
414            HistoryInfo::NotYetWritten => Ok(None),
415            HistoryInfo::InChangeset(changeset_block_number) => Ok(Some(
416                self.tx()
417                    .cursor_dup_read::<tables::StorageChangeSets>()?
418                    .seek_by_key_subkey((changeset_block_number, address).into(), storage_key)?
419                    .filter(|entry| entry.key == storage_key)
420                    .ok_or_else(|| ProviderError::StorageChangesetNotFound {
421                        block_number: changeset_block_number,
422                        address,
423                        storage_key: Box::new(storage_key),
424                    })?
425                    .value,
426            )),
427            HistoryInfo::InPlainState | HistoryInfo::MaybeInPlainState => Ok(self
428                .tx()
429                .cursor_dup_read::<tables::PlainStorageState>()?
430                .seek_by_key_subkey(address, storage_key)?
431                .filter(|entry| entry.key == storage_key)
432                .map(|entry| entry.value)
433                .or(Some(StorageValue::ZERO))),
434        }
435    }
436
437    /// Get account code by its hash
438    fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult<Option<Bytecode>> {
439        self.tx().get_by_encoded_key::<tables::Bytecodes>(code_hash).map_err(Into::into)
440    }
441}
442
443impl<Provider: StateCommitmentProvider> StateCommitmentProvider
444    for HistoricalStateProviderRef<'_, Provider>
445{
446    type StateCommitment = Provider::StateCommitment;
447}
448
449/// State provider for a given block number.
450/// For more detailed description, see [`HistoricalStateProviderRef`].
451#[derive(Debug)]
452pub struct HistoricalStateProvider<Provider> {
453    /// Database provider.
454    provider: Provider,
455    /// State at the block number is the main indexer of the state.
456    block_number: BlockNumber,
457    /// Lowest blocks at which different parts of the state are available.
458    lowest_available_blocks: LowestAvailableBlocks,
459}
460
461impl<Provider: DBProvider + BlockNumReader + StateCommitmentProvider>
462    HistoricalStateProvider<Provider>
463{
464    /// Create new `StateProvider` for historical block number
465    pub fn new(provider: Provider, block_number: BlockNumber) -> Self {
466        Self { provider, block_number, lowest_available_blocks: Default::default() }
467    }
468
469    /// Set the lowest block number at which the account history is available.
470    pub const fn with_lowest_available_account_history_block_number(
471        mut self,
472        block_number: BlockNumber,
473    ) -> Self {
474        self.lowest_available_blocks.account_history_block_number = Some(block_number);
475        self
476    }
477
478    /// Set the lowest block number at which the storage history is available.
479    pub const fn with_lowest_available_storage_history_block_number(
480        mut self,
481        block_number: BlockNumber,
482    ) -> Self {
483        self.lowest_available_blocks.storage_history_block_number = Some(block_number);
484        self
485    }
486
487    /// Returns a new provider that takes the `TX` as reference
488    #[inline(always)]
489    const fn as_ref(&self) -> HistoricalStateProviderRef<'_, Provider> {
490        HistoricalStateProviderRef::new_with_lowest_available_blocks(
491            &self.provider,
492            self.block_number,
493            self.lowest_available_blocks,
494        )
495    }
496}
497
498impl<Provider: StateCommitmentProvider> StateCommitmentProvider
499    for HistoricalStateProvider<Provider>
500{
501    type StateCommitment = Provider::StateCommitment;
502}
503
504// Delegates all provider impls to [HistoricalStateProviderRef]
505delegate_provider_impls!(HistoricalStateProvider<Provider> where [Provider: DBProvider + BlockNumReader + BlockHashReader + StateCommitmentProvider]);
506
507/// Lowest blocks at which different parts of the state are available.
508/// They may be [Some] if pruning is enabled.
509#[derive(Clone, Copy, Debug, Default)]
510pub struct LowestAvailableBlocks {
511    /// Lowest block number at which the account history is available. It may not be available if
512    /// [`reth_prune_types::PruneSegment::AccountHistory`] was pruned.
513    /// [`Option::None`] means all history is available.
514    pub account_history_block_number: Option<BlockNumber>,
515    /// Lowest block number at which the storage history is available. It may not be available if
516    /// [`reth_prune_types::PruneSegment::StorageHistory`] was pruned.
517    /// [`Option::None`] means all history is available.
518    pub storage_history_block_number: Option<BlockNumber>,
519}
520
521impl LowestAvailableBlocks {
522    /// Check if account history is available at the provided block number, i.e. lowest available
523    /// block number for account history is less than or equal to the provided block number.
524    pub fn is_account_history_available(&self, at: BlockNumber) -> bool {
525        self.account_history_block_number.map(|block_number| block_number <= at).unwrap_or(true)
526    }
527
528    /// Check if storage history is available at the provided block number, i.e. lowest available
529    /// block number for storage history is less than or equal to the provided block number.
530    pub fn is_storage_history_available(&self, at: BlockNumber) -> bool {
531        self.storage_history_block_number.map(|block_number| block_number <= at).unwrap_or(true)
532    }
533}
534
535#[cfg(test)]
536mod tests {
537    use crate::{
538        providers::state::historical::{HistoryInfo, LowestAvailableBlocks},
539        test_utils::create_test_provider_factory,
540        AccountReader, HistoricalStateProvider, HistoricalStateProviderRef, StateProvider,
541    };
542    use alloy_primitives::{address, b256, Address, B256, U256};
543    use reth_db_api::{
544        models::{storage_sharded_key::StorageShardedKey, AccountBeforeTx, ShardedKey},
545        tables,
546        transaction::{DbTx, DbTxMut},
547        BlockNumberList,
548    };
549    use reth_primitives_traits::{Account, StorageEntry};
550    use reth_storage_api::{
551        BlockHashReader, BlockNumReader, DBProvider, DatabaseProviderFactory,
552        StateCommitmentProvider,
553    };
554    use reth_storage_errors::provider::ProviderError;
555
556    const ADDRESS: Address = address!("0x0000000000000000000000000000000000000001");
557    const HIGHER_ADDRESS: Address = address!("0x0000000000000000000000000000000000000005");
558    const STORAGE: B256 =
559        b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
560
561    const fn assert_state_provider<T: StateProvider>() {}
562    #[expect(dead_code)]
563    const fn assert_historical_state_provider<
564        T: DBProvider + BlockNumReader + BlockHashReader + StateCommitmentProvider,
565    >() {
566        assert_state_provider::<HistoricalStateProvider<T>>();
567    }
568
569    #[test]
570    fn history_provider_get_account() {
571        let factory = create_test_provider_factory();
572        let tx = factory.provider_rw().unwrap().into_tx();
573
574        tx.put::<tables::AccountsHistory>(
575            ShardedKey { key: ADDRESS, highest_block_number: 7 },
576            BlockNumberList::new([1, 3, 7]).unwrap(),
577        )
578        .unwrap();
579        tx.put::<tables::AccountsHistory>(
580            ShardedKey { key: ADDRESS, highest_block_number: u64::MAX },
581            BlockNumberList::new([10, 15]).unwrap(),
582        )
583        .unwrap();
584        tx.put::<tables::AccountsHistory>(
585            ShardedKey { key: HIGHER_ADDRESS, highest_block_number: u64::MAX },
586            BlockNumberList::new([4]).unwrap(),
587        )
588        .unwrap();
589
590        let acc_plain = Account { nonce: 100, balance: U256::ZERO, bytecode_hash: None };
591        let acc_at15 = Account { nonce: 15, balance: U256::ZERO, bytecode_hash: None };
592        let acc_at10 = Account { nonce: 10, balance: U256::ZERO, bytecode_hash: None };
593        let acc_at7 = Account { nonce: 7, balance: U256::ZERO, bytecode_hash: None };
594        let acc_at3 = Account { nonce: 3, balance: U256::ZERO, bytecode_hash: None };
595
596        let higher_acc_plain = Account { nonce: 4, balance: U256::ZERO, bytecode_hash: None };
597
598        // setup
599        tx.put::<tables::AccountChangeSets>(1, AccountBeforeTx { address: ADDRESS, info: None })
600            .unwrap();
601        tx.put::<tables::AccountChangeSets>(
602            3,
603            AccountBeforeTx { address: ADDRESS, info: Some(acc_at3) },
604        )
605        .unwrap();
606        tx.put::<tables::AccountChangeSets>(
607            4,
608            AccountBeforeTx { address: HIGHER_ADDRESS, info: None },
609        )
610        .unwrap();
611        tx.put::<tables::AccountChangeSets>(
612            7,
613            AccountBeforeTx { address: ADDRESS, info: Some(acc_at7) },
614        )
615        .unwrap();
616        tx.put::<tables::AccountChangeSets>(
617            10,
618            AccountBeforeTx { address: ADDRESS, info: Some(acc_at10) },
619        )
620        .unwrap();
621        tx.put::<tables::AccountChangeSets>(
622            15,
623            AccountBeforeTx { address: ADDRESS, info: Some(acc_at15) },
624        )
625        .unwrap();
626
627        // setup plain state
628        tx.put::<tables::PlainAccountState>(ADDRESS, acc_plain).unwrap();
629        tx.put::<tables::PlainAccountState>(HIGHER_ADDRESS, higher_acc_plain).unwrap();
630        tx.commit().unwrap();
631
632        let db = factory.provider().unwrap();
633
634        // run
635        assert!(matches!(
636            HistoricalStateProviderRef::new(&db, 1).basic_account(&ADDRESS),
637            Ok(None)
638        ));
639        assert!(matches!(
640            HistoricalStateProviderRef::new(&db, 2).basic_account(&ADDRESS),
641            Ok(Some(acc)) if acc == acc_at3
642        ));
643        assert!(matches!(
644            HistoricalStateProviderRef::new(&db, 3).basic_account(&ADDRESS),
645            Ok(Some(acc)) if acc == acc_at3
646        ));
647        assert!(matches!(
648            HistoricalStateProviderRef::new(&db, 4).basic_account(&ADDRESS),
649            Ok(Some(acc)) if acc == acc_at7
650        ));
651        assert!(matches!(
652            HistoricalStateProviderRef::new(&db, 7).basic_account(&ADDRESS),
653            Ok(Some(acc)) if acc == acc_at7
654        ));
655        assert!(matches!(
656            HistoricalStateProviderRef::new(&db, 9).basic_account(&ADDRESS),
657            Ok(Some(acc)) if acc == acc_at10
658        ));
659        assert!(matches!(
660            HistoricalStateProviderRef::new(&db, 10).basic_account(&ADDRESS),
661            Ok(Some(acc)) if acc == acc_at10
662        ));
663        assert!(matches!(
664            HistoricalStateProviderRef::new(&db, 11).basic_account(&ADDRESS),
665            Ok(Some(acc)) if acc == acc_at15
666        ));
667        assert!(matches!(
668            HistoricalStateProviderRef::new(&db, 16).basic_account(&ADDRESS),
669            Ok(Some(acc)) if acc == acc_plain
670        ));
671
672        assert!(matches!(
673            HistoricalStateProviderRef::new(&db, 1).basic_account(&HIGHER_ADDRESS),
674            Ok(None)
675        ));
676        assert!(matches!(
677            HistoricalStateProviderRef::new(&db, 1000).basic_account(&HIGHER_ADDRESS),
678            Ok(Some(acc)) if acc == higher_acc_plain
679        ));
680    }
681
682    #[test]
683    fn history_provider_get_storage() {
684        let factory = create_test_provider_factory();
685        let tx = factory.provider_rw().unwrap().into_tx();
686
687        tx.put::<tables::StoragesHistory>(
688            StorageShardedKey {
689                address: ADDRESS,
690                sharded_key: ShardedKey { key: STORAGE, highest_block_number: 7 },
691            },
692            BlockNumberList::new([3, 7]).unwrap(),
693        )
694        .unwrap();
695        tx.put::<tables::StoragesHistory>(
696            StorageShardedKey {
697                address: ADDRESS,
698                sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
699            },
700            BlockNumberList::new([10, 15]).unwrap(),
701        )
702        .unwrap();
703        tx.put::<tables::StoragesHistory>(
704            StorageShardedKey {
705                address: HIGHER_ADDRESS,
706                sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
707            },
708            BlockNumberList::new([4]).unwrap(),
709        )
710        .unwrap();
711
712        let higher_entry_plain = StorageEntry { key: STORAGE, value: U256::from(1000) };
713        let higher_entry_at4 = StorageEntry { key: STORAGE, value: U256::from(0) };
714        let entry_plain = StorageEntry { key: STORAGE, value: U256::from(100) };
715        let entry_at15 = StorageEntry { key: STORAGE, value: U256::from(15) };
716        let entry_at10 = StorageEntry { key: STORAGE, value: U256::from(10) };
717        let entry_at7 = StorageEntry { key: STORAGE, value: U256::from(7) };
718        let entry_at3 = StorageEntry { key: STORAGE, value: U256::from(0) };
719
720        // setup
721        tx.put::<tables::StorageChangeSets>((3, ADDRESS).into(), entry_at3).unwrap();
722        tx.put::<tables::StorageChangeSets>((4, HIGHER_ADDRESS).into(), higher_entry_at4).unwrap();
723        tx.put::<tables::StorageChangeSets>((7, ADDRESS).into(), entry_at7).unwrap();
724        tx.put::<tables::StorageChangeSets>((10, ADDRESS).into(), entry_at10).unwrap();
725        tx.put::<tables::StorageChangeSets>((15, ADDRESS).into(), entry_at15).unwrap();
726
727        // setup plain state
728        tx.put::<tables::PlainStorageState>(ADDRESS, entry_plain).unwrap();
729        tx.put::<tables::PlainStorageState>(HIGHER_ADDRESS, higher_entry_plain).unwrap();
730        tx.commit().unwrap();
731
732        let db = factory.provider().unwrap();
733
734        // run
735        assert!(matches!(
736            HistoricalStateProviderRef::new(&db, 0).storage(ADDRESS, STORAGE),
737            Ok(None)
738        ));
739        assert!(matches!(
740            HistoricalStateProviderRef::new(&db, 3).storage(ADDRESS, STORAGE),
741            Ok(Some(U256::ZERO))
742        ));
743        assert!(matches!(
744            HistoricalStateProviderRef::new(&db, 4).storage(ADDRESS, STORAGE),
745            Ok(Some(expected_value)) if expected_value == entry_at7.value
746        ));
747        assert!(matches!(
748            HistoricalStateProviderRef::new(&db, 7).storage(ADDRESS, STORAGE),
749            Ok(Some(expected_value)) if expected_value == entry_at7.value
750        ));
751        assert!(matches!(
752            HistoricalStateProviderRef::new(&db, 9).storage(ADDRESS, STORAGE),
753            Ok(Some(expected_value)) if expected_value == entry_at10.value
754        ));
755        assert!(matches!(
756            HistoricalStateProviderRef::new(&db, 10).storage(ADDRESS, STORAGE),
757            Ok(Some(expected_value)) if expected_value == entry_at10.value
758        ));
759        assert!(matches!(
760            HistoricalStateProviderRef::new(&db, 11).storage(ADDRESS, STORAGE),
761            Ok(Some(expected_value)) if expected_value == entry_at15.value
762        ));
763        assert!(matches!(
764            HistoricalStateProviderRef::new(&db, 16).storage(ADDRESS, STORAGE),
765            Ok(Some(expected_value)) if expected_value == entry_plain.value
766        ));
767        assert!(matches!(
768            HistoricalStateProviderRef::new(&db, 1).storage(HIGHER_ADDRESS, STORAGE),
769            Ok(None)
770        ));
771        assert!(matches!(
772            HistoricalStateProviderRef::new(&db, 1000).storage(HIGHER_ADDRESS, STORAGE),
773            Ok(Some(expected_value)) if expected_value == higher_entry_plain.value
774        ));
775    }
776
777    #[test]
778    fn history_provider_unavailable() {
779        let factory = create_test_provider_factory();
780        let db = factory.database_provider_rw().unwrap();
781
782        // provider block_number < lowest available block number,
783        // i.e. state at provider block is pruned
784        let provider = HistoricalStateProviderRef::new_with_lowest_available_blocks(
785            &db,
786            2,
787            LowestAvailableBlocks {
788                account_history_block_number: Some(3),
789                storage_history_block_number: Some(3),
790            },
791        );
792        assert!(matches!(
793            provider.account_history_lookup(ADDRESS),
794            Err(ProviderError::StateAtBlockPruned(number)) if number == provider.block_number
795        ));
796        assert!(matches!(
797            provider.storage_history_lookup(ADDRESS, STORAGE),
798            Err(ProviderError::StateAtBlockPruned(number)) if number == provider.block_number
799        ));
800
801        // provider block_number == lowest available block number,
802        // i.e. state at provider block is available
803        let provider = HistoricalStateProviderRef::new_with_lowest_available_blocks(
804            &db,
805            2,
806            LowestAvailableBlocks {
807                account_history_block_number: Some(2),
808                storage_history_block_number: Some(2),
809            },
810        );
811        assert!(matches!(
812            provider.account_history_lookup(ADDRESS),
813            Ok(HistoryInfo::MaybeInPlainState)
814        ));
815        assert!(matches!(
816            provider.storage_history_lookup(ADDRESS, STORAGE),
817            Ok(HistoryInfo::MaybeInPlainState)
818        ));
819
820        // provider block_number == lowest available block number,
821        // i.e. state at provider block is available
822        let provider = HistoricalStateProviderRef::new_with_lowest_available_blocks(
823            &db,
824            2,
825            LowestAvailableBlocks {
826                account_history_block_number: Some(1),
827                storage_history_block_number: Some(1),
828            },
829        );
830        assert!(matches!(
831            provider.account_history_lookup(ADDRESS),
832            Ok(HistoryInfo::MaybeInPlainState)
833        ));
834        assert!(matches!(
835            provider.storage_history_lookup(ADDRESS, STORAGE),
836            Ok(HistoryInfo::MaybeInPlainState)
837        ));
838    }
839}