reth_provider/providers/state/
historical.rs

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