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