Skip to main content

reth_trie_db/
storage.rs

1use crate::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory};
2use alloy_primitives::{keccak256, map::hash_map, Address, BlockNumber, B256};
3use reth_db_api::{models::BlockNumberAddress, transaction::DbTx};
4use reth_execution_errors::StorageRootError;
5use reth_storage_api::{BlockNumReader, StorageChangeSetReader};
6use reth_storage_errors::provider::ProviderResult;
7use reth_trie::{
8    hashed_cursor::HashedPostStateCursorFactory, HashedPostState, HashedStorage, StorageRoot,
9};
10
11#[cfg(feature = "metrics")]
12use reth_trie::metrics::TrieRootMetrics;
13
14/// Extends [`StorageRoot`] with operations specific for working with a database transaction.
15pub trait DatabaseStorageRoot<'a, TX> {
16    /// Create a new storage root calculator from database transaction and raw address.
17    fn from_tx(tx: &'a TX, address: Address) -> Self;
18
19    /// Create a new storage root calculator from database transaction and hashed address.
20    fn from_tx_hashed(tx: &'a TX, hashed_address: B256) -> Self;
21
22    /// Calculates the storage root for this [`HashedStorage`] and returns it.
23    fn overlay_root(
24        tx: &'a TX,
25        address: Address,
26        hashed_storage: HashedStorage,
27    ) -> Result<B256, StorageRootError>;
28}
29
30/// Initializes [`HashedStorage`] from reverts using a provider.
31pub fn hashed_storage_from_reverts_with_provider<P>(
32    provider: &P,
33    address: Address,
34    from: BlockNumber,
35) -> ProviderResult<HashedStorage>
36where
37    P: StorageChangeSetReader + BlockNumReader,
38{
39    let mut storage = HashedStorage::new(false);
40    let tip = provider.last_block_number()?;
41
42    if from > tip {
43        return Ok(storage)
44    }
45
46    for (BlockNumberAddress((_, storage_address)), storage_change) in
47        provider.storage_changesets_range(from..=tip)?
48    {
49        if storage_address == address {
50            let hashed_slot = storage_change.key.to_hashed();
51            if let hash_map::Entry::Vacant(entry) = storage.storage.entry(hashed_slot) {
52                entry.insert(storage_change.value);
53            }
54        }
55    }
56
57    Ok(storage)
58}
59
60impl<'a, TX: DbTx> DatabaseStorageRoot<'a, TX>
61    for StorageRoot<DatabaseTrieCursorFactory<&'a TX>, DatabaseHashedCursorFactory<&'a TX>>
62{
63    fn from_tx(tx: &'a TX, address: Address) -> Self {
64        Self::new(
65            DatabaseTrieCursorFactory::new(tx),
66            DatabaseHashedCursorFactory::new(tx),
67            address,
68            Default::default(),
69            #[cfg(feature = "metrics")]
70            TrieRootMetrics::new(reth_trie::TrieType::Storage),
71        )
72    }
73
74    fn from_tx_hashed(tx: &'a TX, hashed_address: B256) -> Self {
75        Self::new_hashed(
76            DatabaseTrieCursorFactory::new(tx),
77            DatabaseHashedCursorFactory::new(tx),
78            hashed_address,
79            Default::default(),
80            #[cfg(feature = "metrics")]
81            TrieRootMetrics::new(reth_trie::TrieType::Storage),
82        )
83    }
84
85    fn overlay_root(
86        tx: &'a TX,
87        address: Address,
88        hashed_storage: HashedStorage,
89    ) -> Result<B256, StorageRootError> {
90        let prefix_set = hashed_storage.construct_prefix_set().freeze();
91        let state_sorted =
92            HashedPostState::from_hashed_storage(keccak256(address), hashed_storage).into_sorted();
93        StorageRoot::new(
94            DatabaseTrieCursorFactory::new(tx),
95            HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted),
96            address,
97            prefix_set,
98            #[cfg(feature = "metrics")]
99            TrieRootMetrics::new(reth_trie::TrieType::Storage),
100        )
101        .root()
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108    use alloy_consensus::Header;
109    use alloy_primitives::U256;
110    use reth_db_api::{models::BlockNumberAddress, tables, transaction::DbTxMut};
111    use reth_primitives_traits::StorageEntry;
112    use reth_provider::{
113        test_utils::create_test_provider_factory, StaticFileProviderFactory, StaticFileSegment,
114        StaticFileWriter, StorageSettingsCache,
115    };
116
117    fn append_storage_changesets_to_static_files(
118        factory: &impl StaticFileProviderFactory<
119            Primitives: reth_primitives_traits::NodePrimitives<BlockHeader = Header>,
120        >,
121        changesets: Vec<(u64, Vec<reth_db_api::models::StorageBeforeTx>)>,
122    ) {
123        let sf = factory.static_file_provider();
124        let mut writer = sf.latest_writer(StaticFileSegment::StorageChangeSets).unwrap();
125        for (block_number, changeset) in changesets {
126            writer.append_storage_changeset(changeset, block_number).unwrap();
127        }
128        writer.commit().unwrap();
129    }
130
131    fn append_headers_to_static_files(
132        factory: &impl StaticFileProviderFactory<
133            Primitives: reth_primitives_traits::NodePrimitives<BlockHeader = Header>,
134        >,
135        up_to_block: u64,
136    ) {
137        let sf = factory.static_file_provider();
138        let mut writer = sf.latest_writer(StaticFileSegment::Headers).unwrap();
139        let mut header = Header::default();
140        for num in 0..=up_to_block {
141            header.number = num;
142            writer.append_header(&header, &B256::ZERO).unwrap();
143        }
144        writer.commit().unwrap();
145    }
146
147    #[test]
148    fn test_hashed_storage_from_reverts_legacy() {
149        let factory = create_test_provider_factory();
150        let provider = factory.provider_rw().unwrap();
151
152        assert!(!provider.cached_storage_settings().use_hashed_state());
153
154        let address = Address::with_last_byte(42);
155        let slot1 = B256::from(U256::from(100));
156        let slot2 = B256::from(U256::from(200));
157
158        append_headers_to_static_files(&factory, 5);
159
160        provider
161            .tx_ref()
162            .put::<tables::StorageChangeSets>(
163                BlockNumberAddress((1, address)),
164                StorageEntry { key: slot1, value: U256::from(10) },
165            )
166            .unwrap();
167        provider
168            .tx_ref()
169            .put::<tables::StorageChangeSets>(
170                BlockNumberAddress((2, address)),
171                StorageEntry { key: slot2, value: U256::from(20) },
172            )
173            .unwrap();
174        provider
175            .tx_ref()
176            .put::<tables::StorageChangeSets>(
177                BlockNumberAddress((3, address)),
178                StorageEntry { key: slot1, value: U256::from(999) },
179            )
180            .unwrap();
181
182        let result = hashed_storage_from_reverts_with_provider(&*provider, address, 1).unwrap();
183
184        let hashed_slot1 = keccak256(slot1);
185        let hashed_slot2 = keccak256(slot2);
186
187        assert_eq!(result.storage.len(), 2);
188        assert_eq!(result.storage.get(&hashed_slot1), Some(&U256::from(10)));
189        assert_eq!(result.storage.get(&hashed_slot2), Some(&U256::from(20)));
190    }
191
192    #[test]
193    fn test_hashed_storage_from_reverts_hashed_state() {
194        use reth_db_api::models::{StorageBeforeTx, StorageSettings};
195
196        let factory = create_test_provider_factory();
197
198        factory.set_storage_settings_cache(StorageSettings::v2());
199
200        let provider = factory.provider_rw().unwrap();
201        assert!(provider.cached_storage_settings().use_hashed_state());
202        assert!(provider.cached_storage_settings().is_v2());
203
204        let address = Address::with_last_byte(42);
205        let plain_slot1 = B256::from(U256::from(100));
206        let plain_slot2 = B256::from(U256::from(200));
207        let hashed_slot1 = keccak256(plain_slot1);
208        let hashed_slot2 = keccak256(plain_slot2);
209
210        append_headers_to_static_files(&factory, 5);
211
212        append_storage_changesets_to_static_files(
213            &factory,
214            vec![
215                (0, vec![]),
216                (1, vec![StorageBeforeTx { address, key: hashed_slot1, value: U256::from(10) }]),
217                (2, vec![StorageBeforeTx { address, key: hashed_slot2, value: U256::from(20) }]),
218                (3, vec![StorageBeforeTx { address, key: hashed_slot1, value: U256::from(999) }]),
219            ],
220        );
221
222        let result = hashed_storage_from_reverts_with_provider(&*provider, address, 1).unwrap();
223
224        assert_eq!(result.storage.len(), 2);
225        assert_eq!(result.storage.get(&hashed_slot1), Some(&U256::from(10)));
226        assert_eq!(result.storage.get(&hashed_slot2), Some(&U256::from(20)));
227    }
228}