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::{map::B256HashMap, Address, BlockNumber, Bytes, StorageKey, B256};
7use reth_db::{tables, BlockNumberList};
8use reth_db_api::{
9    cursor::{DbCursorRO, DbDupCursorRO},
10    models::{storage_sharded_key::StorageShardedKey, ShardedKey},
11    table::Table,
12    transaction::DbTx,
13};
14use reth_primitives::{Account, Bytecode};
15use reth_storage_api::{
16    BlockNumReader, DBProvider, StateCommitmentProvider, StateProofProvider, StorageRootProvider,
17};
18use reth_storage_errors::provider::ProviderResult;
19use reth_trie::{
20    proof::{Proof, StorageProof},
21    updates::TrieUpdates,
22    witness::TrieWitness,
23    AccountProof, HashedPostState, HashedStorage, MultiProof, MultiProofTargets, StateRoot,
24    StorageMultiProof, StorageRoot, TrieInput,
25};
26use reth_trie_db::{
27    DatabaseHashedPostState, DatabaseHashedStorage, DatabaseProof, DatabaseStateRoot,
28    DatabaseStorageProof, DatabaseStorageRoot, DatabaseTrieWitness, StateCommitment,
29};
30use revm::primitives::FlaggedStorage;
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,
262                })?
263                .info),
264            HistoryInfo::InPlainState | HistoryInfo::MaybeInPlainState => {
265                Ok(self.tx().get::<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(
387        &self,
388        mut input: TrieInput,
389        target: HashedPostState,
390    ) -> ProviderResult<B256HashMap<Bytes>> {
391        input.prepend(self.revert_state()?);
392        TrieWitness::overlay_witness(self.tx(), input, target).map_err(ProviderError::from)
393    }
394}
395
396impl<Provider: StateCommitmentProvider> HashedPostStateProvider
397    for HistoricalStateProviderRef<'_, Provider>
398{
399    fn hashed_post_state(&self, bundle_state: &revm::db::BundleState) -> HashedPostState {
400        HashedPostState::from_bundle_state::<
401            <Provider::StateCommitment as StateCommitment>::KeyHasher,
402        >(bundle_state.state())
403    }
404}
405
406impl<Provider: DBProvider + BlockNumReader + BlockHashReader + StateCommitmentProvider>
407    StateProvider for HistoricalStateProviderRef<'_, Provider>
408{
409    /// Get storage.
410    fn storage(
411        &self,
412        address: Address,
413        storage_key: StorageKey,
414    ) -> ProviderResult<Option<FlaggedStorage>> {
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                    .into(),
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.into())
435                .or(Some(FlaggedStorage::ZERO))),
436        }
437    }
438
439    /// Get account code by its hash
440    fn bytecode_by_hash(&self, code_hash: B256) -> ProviderResult<Option<Bytecode>> {
441        self.tx().get::<tables::Bytecodes>(code_hash).map_err(Into::into)
442    }
443}
444
445impl<Provider: StateCommitmentProvider> StateCommitmentProvider
446    for HistoricalStateProviderRef<'_, Provider>
447{
448    type StateCommitment = Provider::StateCommitment;
449}
450
451/// State provider for a given block number.
452/// For more detailed description, see [`HistoricalStateProviderRef`].
453#[derive(Debug)]
454pub struct HistoricalStateProvider<Provider> {
455    /// Database provider.
456    provider: Provider,
457    /// State at the block number is the main indexer of the state.
458    block_number: BlockNumber,
459    /// Lowest blocks at which different parts of the state are available.
460    lowest_available_blocks: LowestAvailableBlocks,
461}
462
463impl<Provider: DBProvider + BlockNumReader + StateCommitmentProvider>
464    HistoricalStateProvider<Provider>
465{
466    /// Create new `StateProvider` for historical block number
467    pub fn new(provider: Provider, block_number: BlockNumber) -> Self {
468        Self { provider, block_number, lowest_available_blocks: Default::default() }
469    }
470
471    /// Set the lowest block number at which the account history is available.
472    pub const fn with_lowest_available_account_history_block_number(
473        mut self,
474        block_number: BlockNumber,
475    ) -> Self {
476        self.lowest_available_blocks.account_history_block_number = Some(block_number);
477        self
478    }
479
480    /// Set the lowest block number at which the storage history is available.
481    pub const fn with_lowest_available_storage_history_block_number(
482        mut self,
483        block_number: BlockNumber,
484    ) -> Self {
485        self.lowest_available_blocks.storage_history_block_number = Some(block_number);
486        self
487    }
488
489    /// Returns a new provider that takes the `TX` as reference
490    #[inline(always)]
491    const fn as_ref(&self) -> HistoricalStateProviderRef<'_, Provider> {
492        HistoricalStateProviderRef::new_with_lowest_available_blocks(
493            &self.provider,
494            self.block_number,
495            self.lowest_available_blocks,
496        )
497    }
498}
499
500impl<Provider: StateCommitmentProvider> StateCommitmentProvider
501    for HistoricalStateProvider<Provider>
502{
503    type StateCommitment = Provider::StateCommitment;
504}
505
506// Delegates all provider impls to [HistoricalStateProviderRef]
507delegate_provider_impls!(HistoricalStateProvider<Provider> where [Provider: DBProvider + BlockNumReader + BlockHashReader + StateCommitmentProvider]);
508
509/// Lowest blocks at which different parts of the state are available.
510/// They may be [Some] if pruning is enabled.
511#[derive(Clone, Copy, Debug, Default)]
512pub struct LowestAvailableBlocks {
513    /// Lowest block number at which the account history is available. It may not be available if
514    /// [`reth_prune_types::PruneSegment::AccountHistory`] was pruned.
515    /// [`Option::None`] means all history is available.
516    pub account_history_block_number: Option<BlockNumber>,
517    /// Lowest block number at which the storage history is available. It may not be available if
518    /// [`reth_prune_types::PruneSegment::StorageHistory`] was pruned.
519    /// [`Option::None`] means all history is available.
520    pub storage_history_block_number: Option<BlockNumber>,
521}
522
523impl LowestAvailableBlocks {
524    /// Check if account history is available at the provided block number, i.e. lowest available
525    /// block number for account history is less than or equal to the provided block number.
526    pub fn is_account_history_available(&self, at: BlockNumber) -> bool {
527        self.account_history_block_number.map(|block_number| block_number <= at).unwrap_or(true)
528    }
529
530    /// Check if storage history is available at the provided block number, i.e. lowest available
531    /// block number for storage history is less than or equal to the provided block number.
532    pub fn is_storage_history_available(&self, at: BlockNumber) -> bool {
533        self.storage_history_block_number.map(|block_number| block_number <= at).unwrap_or(true)
534    }
535}
536
537#[cfg(test)]
538mod tests {
539    use crate::{
540        providers::state::historical::{HistoryInfo, LowestAvailableBlocks},
541        test_utils::create_test_provider_factory,
542        AccountReader, HistoricalStateProvider, HistoricalStateProviderRef, StateProvider,
543    };
544    use alloy_primitives::{address, b256, Address, B256, U256};
545    use reth_db::{tables, BlockNumberList};
546    use reth_db_api::{
547        models::{storage_sharded_key::StorageShardedKey, AccountBeforeTx, ShardedKey},
548        transaction::{DbTx, DbTxMut},
549    };
550    use reth_primitives::{Account, StorageEntry};
551    use reth_storage_api::{
552        BlockHashReader, BlockNumReader, DBProvider, DatabaseProviderFactory,
553        StateCommitmentProvider,
554    };
555    use reth_storage_errors::provider::ProviderError;
556    use revm::primitives::FlaggedStorage;
557
558    const ADDRESS: Address = address!("0000000000000000000000000000000000000001");
559    const HIGHER_ADDRESS: Address = address!("0000000000000000000000000000000000000005");
560    const STORAGE: B256 = b256!("0000000000000000000000000000000000000000000000000000000000000001");
561
562    const fn assert_state_provider<T: StateProvider>() {}
563    #[allow(dead_code)]
564    const fn assert_historical_state_provider<
565        T: DBProvider + BlockNumReader + BlockHashReader + StateCommitmentProvider,
566    >() {
567        assert_state_provider::<HistoricalStateProvider<T>>();
568    }
569
570    #[test]
571    fn history_provider_get_account() {
572        let factory = create_test_provider_factory();
573        let tx = factory.provider_rw().unwrap().into_tx();
574
575        tx.put::<tables::AccountsHistory>(
576            ShardedKey { key: ADDRESS, highest_block_number: 7 },
577            BlockNumberList::new([1, 3, 7]).unwrap(),
578        )
579        .unwrap();
580        tx.put::<tables::AccountsHistory>(
581            ShardedKey { key: ADDRESS, highest_block_number: u64::MAX },
582            BlockNumberList::new([10, 15]).unwrap(),
583        )
584        .unwrap();
585        tx.put::<tables::AccountsHistory>(
586            ShardedKey { key: HIGHER_ADDRESS, highest_block_number: u64::MAX },
587            BlockNumberList::new([4]).unwrap(),
588        )
589        .unwrap();
590
591        let acc_plain = Account { nonce: 100, balance: U256::ZERO, bytecode_hash: None };
592        let acc_at15 = Account { nonce: 15, balance: U256::ZERO, bytecode_hash: None };
593        let acc_at10 = Account { nonce: 10, balance: U256::ZERO, bytecode_hash: None };
594        let acc_at7 = Account { nonce: 7, balance: U256::ZERO, bytecode_hash: None };
595        let acc_at3 = Account { nonce: 3, balance: U256::ZERO, bytecode_hash: None };
596
597        let higher_acc_plain = Account { nonce: 4, balance: U256::ZERO, bytecode_hash: None };
598
599        // setup
600        tx.put::<tables::AccountChangeSets>(1, AccountBeforeTx { address: ADDRESS, info: None })
601            .unwrap();
602        tx.put::<tables::AccountChangeSets>(
603            3,
604            AccountBeforeTx { address: ADDRESS, info: Some(acc_at3) },
605        )
606        .unwrap();
607        tx.put::<tables::AccountChangeSets>(
608            4,
609            AccountBeforeTx { address: HIGHER_ADDRESS, info: None },
610        )
611        .unwrap();
612        tx.put::<tables::AccountChangeSets>(
613            7,
614            AccountBeforeTx { address: ADDRESS, info: Some(acc_at7) },
615        )
616        .unwrap();
617        tx.put::<tables::AccountChangeSets>(
618            10,
619            AccountBeforeTx { address: ADDRESS, info: Some(acc_at10) },
620        )
621        .unwrap();
622        tx.put::<tables::AccountChangeSets>(
623            15,
624            AccountBeforeTx { address: ADDRESS, info: Some(acc_at15) },
625        )
626        .unwrap();
627
628        // setup plain state
629        tx.put::<tables::PlainAccountState>(ADDRESS, acc_plain).unwrap();
630        tx.put::<tables::PlainAccountState>(HIGHER_ADDRESS, higher_acc_plain).unwrap();
631        tx.commit().unwrap();
632
633        let db = factory.provider().unwrap();
634
635        // run
636        assert_eq!(HistoricalStateProviderRef::new(&db, 1).basic_account(ADDRESS), Ok(None));
637        assert_eq!(
638            HistoricalStateProviderRef::new(&db, 2).basic_account(ADDRESS),
639            Ok(Some(acc_at3))
640        );
641        assert_eq!(
642            HistoricalStateProviderRef::new(&db, 3).basic_account(ADDRESS),
643            Ok(Some(acc_at3))
644        );
645        assert_eq!(
646            HistoricalStateProviderRef::new(&db, 4).basic_account(ADDRESS),
647            Ok(Some(acc_at7))
648        );
649        assert_eq!(
650            HistoricalStateProviderRef::new(&db, 7).basic_account(ADDRESS),
651            Ok(Some(acc_at7))
652        );
653        assert_eq!(
654            HistoricalStateProviderRef::new(&db, 9).basic_account(ADDRESS),
655            Ok(Some(acc_at10))
656        );
657        assert_eq!(
658            HistoricalStateProviderRef::new(&db, 10).basic_account(ADDRESS),
659            Ok(Some(acc_at10))
660        );
661        assert_eq!(
662            HistoricalStateProviderRef::new(&db, 11).basic_account(ADDRESS),
663            Ok(Some(acc_at15))
664        );
665        assert_eq!(
666            HistoricalStateProviderRef::new(&db, 16).basic_account(ADDRESS),
667            Ok(Some(acc_plain))
668        );
669
670        assert_eq!(HistoricalStateProviderRef::new(&db, 1).basic_account(HIGHER_ADDRESS), Ok(None));
671        assert_eq!(
672            HistoricalStateProviderRef::new(&db, 1000).basic_account(HIGHER_ADDRESS),
673            Ok(Some(higher_acc_plain))
674        );
675    }
676
677    #[test]
678    fn history_provider_get_storage() {
679        let factory = create_test_provider_factory();
680        let tx = factory.provider_rw().unwrap().into_tx();
681
682        tx.put::<tables::StoragesHistory>(
683            StorageShardedKey {
684                address: ADDRESS,
685                sharded_key: ShardedKey { key: STORAGE, highest_block_number: 7 },
686            },
687            BlockNumberList::new([3, 7]).unwrap(),
688        )
689        .unwrap();
690        tx.put::<tables::StoragesHistory>(
691            StorageShardedKey {
692                address: ADDRESS,
693                sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
694            },
695            BlockNumberList::new([10, 15]).unwrap(),
696        )
697        .unwrap();
698        tx.put::<tables::StoragesHistory>(
699            StorageShardedKey {
700                address: HIGHER_ADDRESS,
701                sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
702            },
703            BlockNumberList::new([4]).unwrap(),
704        )
705        .unwrap();
706
707        let higher_entry_plain =
708            StorageEntry { key: STORAGE, value: U256::from(1000), is_private: false };
709        let higher_entry_at4 =
710            StorageEntry { key: STORAGE, value: U256::from(0), is_private: false };
711        let entry_plain = StorageEntry { key: STORAGE, value: U256::from(100), is_private: false };
712        let entry_at15 = StorageEntry { key: STORAGE, value: U256::from(15), is_private: false };
713        let entry_at10 = StorageEntry { key: STORAGE, value: U256::from(10), is_private: false };
714        let entry_at7 = StorageEntry { key: STORAGE, value: U256::from(7), is_private: false };
715        let entry_at3 = StorageEntry { key: STORAGE, value: U256::from(0), is_private: false };
716
717        // setup
718        tx.put::<tables::StorageChangeSets>((3, ADDRESS).into(), entry_at3).unwrap();
719        tx.put::<tables::StorageChangeSets>((4, HIGHER_ADDRESS).into(), higher_entry_at4).unwrap();
720        tx.put::<tables::StorageChangeSets>((7, ADDRESS).into(), entry_at7).unwrap();
721        tx.put::<tables::StorageChangeSets>((10, ADDRESS).into(), entry_at10).unwrap();
722        tx.put::<tables::StorageChangeSets>((15, ADDRESS).into(), entry_at15).unwrap();
723
724        // setup plain state
725        tx.put::<tables::PlainStorageState>(ADDRESS, entry_plain).unwrap();
726        tx.put::<tables::PlainStorageState>(HIGHER_ADDRESS, higher_entry_plain).unwrap();
727        tx.commit().unwrap();
728
729        let db = factory.provider().unwrap();
730
731        // run
732        assert_eq!(HistoricalStateProviderRef::new(&db, 0).storage(ADDRESS, STORAGE), Ok(None));
733        assert_eq!(
734            HistoricalStateProviderRef::new(&db, 3).storage(ADDRESS, STORAGE),
735            Ok(Some(FlaggedStorage::ZERO))
736        );
737        assert_eq!(
738            HistoricalStateProviderRef::new(&db, 4).storage(ADDRESS, STORAGE),
739            Ok(Some(FlaggedStorage::new_from_value(entry_at7.value)))
740        );
741        assert_eq!(
742            HistoricalStateProviderRef::new(&db, 7).storage(ADDRESS, STORAGE),
743            Ok(Some(FlaggedStorage::new_from_value(entry_at7.value)))
744        );
745        assert_eq!(
746            HistoricalStateProviderRef::new(&db, 9).storage(ADDRESS, STORAGE),
747            Ok(Some(FlaggedStorage::new_from_value(entry_at10.value)))
748        );
749        assert_eq!(
750            HistoricalStateProviderRef::new(&db, 10).storage(ADDRESS, STORAGE),
751            Ok(Some(FlaggedStorage::new_from_value(entry_at10.value)))
752        );
753        assert_eq!(
754            HistoricalStateProviderRef::new(&db, 11).storage(ADDRESS, STORAGE),
755            Ok(Some(FlaggedStorage::new_from_value(entry_at15.value)))
756        );
757        assert_eq!(
758            HistoricalStateProviderRef::new(&db, 16).storage(ADDRESS, STORAGE),
759            Ok(Some(FlaggedStorage::new_from_value(entry_plain.value)))
760        );
761        assert_eq!(
762            HistoricalStateProviderRef::new(&db, 1).storage(HIGHER_ADDRESS, STORAGE),
763            Ok(None)
764        );
765        assert_eq!(
766            HistoricalStateProviderRef::new(&db, 1000).storage(HIGHER_ADDRESS, STORAGE),
767            Ok(Some(FlaggedStorage::new_from_value(higher_entry_plain.value)))
768        );
769    }
770
771    #[test]
772    fn history_provider_unavailable() {
773        let factory = create_test_provider_factory();
774        let db = factory.database_provider_rw().unwrap();
775
776        // provider block_number < lowest available block number,
777        // i.e. state at provider block is pruned
778        let provider = HistoricalStateProviderRef::new_with_lowest_available_blocks(
779            &db,
780            2,
781            LowestAvailableBlocks {
782                account_history_block_number: Some(3),
783                storage_history_block_number: Some(3),
784            },
785        );
786        assert_eq!(
787            provider.account_history_lookup(ADDRESS),
788            Err(ProviderError::StateAtBlockPruned(provider.block_number))
789        );
790        assert_eq!(
791            provider.storage_history_lookup(ADDRESS, STORAGE),
792            Err(ProviderError::StateAtBlockPruned(provider.block_number))
793        );
794
795        // provider block_number == lowest available block number,
796        // i.e. state at provider block is available
797        let provider = HistoricalStateProviderRef::new_with_lowest_available_blocks(
798            &db,
799            2,
800            LowestAvailableBlocks {
801                account_history_block_number: Some(2),
802                storage_history_block_number: Some(2),
803            },
804        );
805        assert_eq!(provider.account_history_lookup(ADDRESS), Ok(HistoryInfo::MaybeInPlainState));
806        assert_eq!(
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_eq!(provider.account_history_lookup(ADDRESS), Ok(HistoryInfo::MaybeInPlainState));
822        assert_eq!(
823            provider.storage_history_lookup(ADDRESS, STORAGE),
824            Ok(HistoryInfo::MaybeInPlainState)
825        );
826    }
827}