reth_provider/providers/state/
historical.rs

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