reth_trie_db/
storage.rs

1use crate::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory};
2use alloy_primitives::{keccak256, map::hash_map, Address, BlockNumber, B256};
3use reth_db_api::{
4    cursor::DbCursorRO, models::BlockNumberAddress, tables, transaction::DbTx, DatabaseError,
5};
6use reth_execution_errors::StorageRootError;
7use reth_storage_api::{BlockNumReader, StorageChangeSetReader};
8use reth_storage_errors::provider::ProviderResult;
9use reth_trie::{
10    hashed_cursor::HashedPostStateCursorFactory, HashedPostState, HashedStorage, StorageRoot,
11};
12
13#[cfg(feature = "metrics")]
14use reth_trie::metrics::TrieRootMetrics;
15
16/// Extends [`StorageRoot`] with operations specific for working with a database transaction.
17pub trait DatabaseStorageRoot<'a, TX> {
18    /// Create a new storage root calculator from database transaction and raw address.
19    fn from_tx(tx: &'a TX, address: Address) -> Self;
20
21    /// Create a new storage root calculator from database transaction and hashed address.
22    fn from_tx_hashed(tx: &'a TX, hashed_address: B256) -> Self;
23
24    /// Calculates the storage root for this [`HashedStorage`] and returns it.
25    fn overlay_root(
26        tx: &'a TX,
27        address: Address,
28        hashed_storage: HashedStorage,
29    ) -> Result<B256, StorageRootError>;
30}
31
32/// Extends [`HashedStorage`] with operations specific for working with a database transaction.
33pub trait DatabaseHashedStorage<TX>: Sized {
34    /// Initializes [`HashedStorage`] from reverts. Iterates over storage reverts from the specified
35    /// block up to the current tip and aggregates them into hashed storage in reverse.
36    fn from_reverts(tx: &TX, address: Address, from: BlockNumber) -> Result<Self, DatabaseError>;
37}
38
39/// Initializes [`HashedStorage`] from reverts using a provider.
40pub fn hashed_storage_from_reverts_with_provider<P>(
41    provider: &P,
42    address: Address,
43    from: BlockNumber,
44) -> ProviderResult<HashedStorage>
45where
46    P: StorageChangeSetReader + BlockNumReader,
47{
48    let mut storage = HashedStorage::new(false);
49    let tip = provider.last_block_number()?;
50
51    if from > tip {
52        return Ok(storage)
53    }
54
55    for (BlockNumberAddress((_, storage_address)), storage_change) in
56        provider.storage_changesets_range(from..=tip)?
57    {
58        if storage_address == address {
59            let hashed_slot = keccak256(storage_change.key);
60            if let hash_map::Entry::Vacant(entry) = storage.storage.entry(hashed_slot) {
61                entry.insert(storage_change.value);
62            }
63        }
64    }
65
66    Ok(storage)
67}
68
69impl<'a, TX: DbTx> DatabaseStorageRoot<'a, TX>
70    for StorageRoot<DatabaseTrieCursorFactory<&'a TX>, DatabaseHashedCursorFactory<&'a TX>>
71{
72    fn from_tx(tx: &'a TX, address: Address) -> Self {
73        Self::new(
74            DatabaseTrieCursorFactory::new(tx),
75            DatabaseHashedCursorFactory::new(tx),
76            address,
77            Default::default(),
78            #[cfg(feature = "metrics")]
79            TrieRootMetrics::new(reth_trie::TrieType::Storage),
80        )
81    }
82
83    fn from_tx_hashed(tx: &'a TX, hashed_address: B256) -> Self {
84        Self::new_hashed(
85            DatabaseTrieCursorFactory::new(tx),
86            DatabaseHashedCursorFactory::new(tx),
87            hashed_address,
88            Default::default(),
89            #[cfg(feature = "metrics")]
90            TrieRootMetrics::new(reth_trie::TrieType::Storage),
91        )
92    }
93
94    fn overlay_root(
95        tx: &'a TX,
96        address: Address,
97        hashed_storage: HashedStorage,
98    ) -> Result<B256, StorageRootError> {
99        let prefix_set = hashed_storage.construct_prefix_set().freeze();
100        let state_sorted =
101            HashedPostState::from_hashed_storage(keccak256(address), hashed_storage).into_sorted();
102        StorageRoot::new(
103            DatabaseTrieCursorFactory::new(tx),
104            HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted),
105            address,
106            prefix_set,
107            #[cfg(feature = "metrics")]
108            TrieRootMetrics::new(reth_trie::TrieType::Storage),
109        )
110        .root()
111    }
112}
113
114impl<TX: DbTx> DatabaseHashedStorage<TX> for HashedStorage {
115    fn from_reverts(tx: &TX, address: Address, from: BlockNumber) -> Result<Self, DatabaseError> {
116        let mut storage = Self::new(false);
117        let mut storage_changesets_cursor = tx.cursor_read::<tables::StorageChangeSets>()?;
118        for entry in storage_changesets_cursor.walk_range(BlockNumberAddress((from, address))..)? {
119            let (BlockNumberAddress((_, storage_address)), storage_change) = entry?;
120            if storage_address == address {
121                let hashed_slot = keccak256(storage_change.key);
122                if let hash_map::Entry::Vacant(entry) = storage.storage.entry(hashed_slot) {
123                    entry.insert(storage_change.value);
124                }
125            }
126        }
127        Ok(storage)
128    }
129}