reth_provider/providers/state/
historical.rs

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