Skip to main content

reth_provider/providers/state/
latest.rs

1use crate::{
2    AccountReader, BlockHashReader, HashedPostStateProvider, StateProvider, StateRootProvider,
3};
4use alloy_primitives::{Address, BlockNumber, Bytes, StorageKey, StorageValue, B256};
5use reth_db_api::{cursor::DbDupCursorRO, tables, transaction::DbTx};
6use reth_primitives_traits::{Account, Bytecode};
7use reth_storage_api::{
8    BytecodeReader, DBProvider, StateProofProvider, StorageRootProvider, StorageSettingsCache,
9};
10use reth_storage_errors::provider::{ProviderError, ProviderResult};
11use reth_trie::{
12    hashed_cursor::HashedPostStateCursorFactory,
13    proof::{Proof, StorageProof},
14    trie_cursor::{masked::MaskedTrieCursorFactory, InMemoryTrieCursorFactory},
15    updates::TrieUpdates,
16    witness::TrieWitness,
17    AccountProof, HashedPostState, HashedStorage, KeccakKeyHasher, MultiProof, MultiProofTargets,
18    StateRoot, StorageMultiProof, StorageRoot, TrieInput, TrieInputSorted,
19};
20use reth_trie_db::{DatabaseProof, DatabaseStateRoot, DatabaseStorageProof, DatabaseStorageRoot};
21
22type DbStateRoot<'a, TX, A> = StateRoot<
23    reth_trie_db::DatabaseTrieCursorFactory<&'a TX, A>,
24    reth_trie_db::DatabaseHashedCursorFactory<&'a TX>,
25>;
26type DbStorageRoot<'a, TX, A> = StorageRoot<
27    reth_trie_db::DatabaseTrieCursorFactory<&'a TX, A>,
28    reth_trie_db::DatabaseHashedCursorFactory<&'a TX>,
29>;
30type DbStorageProof<'a, TX, A> = StorageProof<
31    'static,
32    reth_trie_db::DatabaseTrieCursorFactory<&'a TX, A>,
33    reth_trie_db::DatabaseHashedCursorFactory<&'a TX>,
34>;
35type DbProof<'a, TX, A> = Proof<
36    reth_trie_db::DatabaseTrieCursorFactory<&'a TX, A>,
37    reth_trie_db::DatabaseHashedCursorFactory<&'a TX>,
38>;
39/// State provider over latest state that takes tx reference.
40///
41/// Wraps a [`DBProvider`] to get access to database.
42#[derive(Debug)]
43pub struct LatestStateProviderRef<'b, Provider>(&'b Provider);
44
45impl<'b, Provider: DBProvider> LatestStateProviderRef<'b, Provider> {
46    /// Create new state provider
47    pub const fn new(provider: &'b Provider) -> Self {
48        Self(provider)
49    }
50
51    fn tx(&self) -> &Provider::Tx {
52        self.0.tx_ref()
53    }
54
55    fn hashed_storage_lookup(
56        &self,
57        hashed_address: B256,
58        hashed_slot: StorageKey,
59    ) -> ProviderResult<Option<StorageValue>> {
60        let mut cursor = self.tx().cursor_dup_read::<tables::HashedStorages>()?;
61        Ok(cursor
62            .seek_by_key_subkey(hashed_address, hashed_slot)?
63            .filter(|e| e.key == hashed_slot)
64            .map(|e| e.value))
65    }
66}
67
68impl<Provider: DBProvider + StorageSettingsCache> AccountReader
69    for LatestStateProviderRef<'_, Provider>
70{
71    /// Get basic account information.
72    fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>> {
73        if self.0.cached_storage_settings().use_hashed_state() {
74            let hashed_address = alloy_primitives::keccak256(address);
75            self.tx()
76                .get_by_encoded_key::<tables::HashedAccounts>(&hashed_address)
77                .map_err(Into::into)
78        } else {
79            self.tx().get_by_encoded_key::<tables::PlainAccountState>(address).map_err(Into::into)
80        }
81    }
82}
83
84impl<Provider: BlockHashReader> BlockHashReader for LatestStateProviderRef<'_, Provider> {
85    /// Get block hash by number.
86    fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>> {
87        self.0.block_hash(number)
88    }
89
90    fn canonical_hashes_range(
91        &self,
92        start: BlockNumber,
93        end: BlockNumber,
94    ) -> ProviderResult<Vec<B256>> {
95        self.0.canonical_hashes_range(start, end)
96    }
97}
98
99impl<Provider: DBProvider + StorageSettingsCache> StateRootProvider
100    for LatestStateProviderRef<'_, Provider>
101{
102    fn state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> {
103        reth_trie_db::with_adapter!(self.0, |A| {
104            let sorted = hashed_state.into_sorted();
105            Ok(<DbStateRoot<'_, _, A> as DatabaseStateRoot<_>>::overlay_root(self.tx(), &sorted)?)
106        })
107    }
108
109    fn state_root_from_nodes(&self, input: TrieInput) -> ProviderResult<B256> {
110        reth_trie_db::with_adapter!(self.0, |A| {
111            Ok(<DbStateRoot<'_, _, A> as DatabaseStateRoot<_>>::overlay_root_from_nodes(
112                self.tx(),
113                TrieInputSorted::from_unsorted(input),
114            )?)
115        })
116    }
117
118    fn state_root_with_updates(
119        &self,
120        hashed_state: HashedPostState,
121    ) -> ProviderResult<(B256, TrieUpdates)> {
122        reth_trie_db::with_adapter!(self.0, |A| {
123            let sorted = hashed_state.into_sorted();
124            Ok(<DbStateRoot<'_, _, A> as DatabaseStateRoot<_>>::overlay_root_with_updates(
125                self.tx(),
126                &sorted,
127            )?)
128        })
129    }
130
131    fn state_root_from_nodes_with_updates(
132        &self,
133        input: TrieInput,
134    ) -> ProviderResult<(B256, TrieUpdates)> {
135        reth_trie_db::with_adapter!(self.0, |A| {
136            Ok(
137                <DbStateRoot<'_, _, A> as DatabaseStateRoot<_>>::overlay_root_from_nodes_with_updates(
138                    self.tx(),
139                    TrieInputSorted::from_unsorted(input),
140                )?,
141            )
142        })
143    }
144}
145
146impl<Provider: DBProvider + StorageSettingsCache> StorageRootProvider
147    for LatestStateProviderRef<'_, Provider>
148{
149    fn storage_root(
150        &self,
151        address: Address,
152        hashed_storage: HashedStorage,
153    ) -> ProviderResult<B256> {
154        reth_trie_db::with_adapter!(self.0, |A| {
155            <DbStorageRoot<'_, _, A>>::overlay_root(self.tx(), address, hashed_storage)
156                .map_err(|err| ProviderError::Database(err.into()))
157        })
158    }
159
160    fn storage_proof(
161        &self,
162        address: Address,
163        slot: B256,
164        hashed_storage: HashedStorage,
165    ) -> ProviderResult<reth_trie::StorageProof> {
166        reth_trie_db::with_adapter!(self.0, |A| {
167            <DbStorageProof<'_, _, A>>::overlay_storage_proof(
168                self.tx(),
169                address,
170                slot,
171                hashed_storage,
172            )
173            .map_err(ProviderError::from)
174        })
175    }
176
177    fn storage_multiproof(
178        &self,
179        address: Address,
180        slots: &[B256],
181        hashed_storage: HashedStorage,
182    ) -> ProviderResult<StorageMultiProof> {
183        reth_trie_db::with_adapter!(self.0, |A| {
184            <DbStorageProof<'_, _, A>>::overlay_storage_multiproof(
185                self.tx(),
186                address,
187                slots,
188                hashed_storage,
189            )
190            .map_err(ProviderError::from)
191        })
192    }
193}
194
195impl<Provider: DBProvider + StorageSettingsCache> StateProofProvider
196    for LatestStateProviderRef<'_, Provider>
197{
198    fn proof(
199        &self,
200        input: TrieInput,
201        address: Address,
202        slots: &[B256],
203    ) -> ProviderResult<AccountProof> {
204        reth_trie_db::with_adapter!(self.0, |A| {
205            let proof = <DbProof<'_, _, A> as DatabaseProof>::from_tx(self.tx());
206            proof.overlay_account_proof(input, address, slots).map_err(ProviderError::from)
207        })
208    }
209
210    fn multiproof(
211        &self,
212        input: TrieInput,
213        targets: MultiProofTargets,
214    ) -> ProviderResult<MultiProof> {
215        reth_trie_db::with_adapter!(self.0, |A| {
216            let proof = <DbProof<'_, _, A> as DatabaseProof>::from_tx(self.tx());
217            proof.overlay_multiproof(input, targets).map_err(ProviderError::from)
218        })
219    }
220
221    fn witness(&self, input: TrieInput, target: HashedPostState) -> ProviderResult<Vec<Bytes>> {
222        reth_trie_db::with_adapter!(self.0, |A| {
223            let nodes_sorted = input.nodes.into_sorted();
224            let state_sorted = input.state.into_sorted();
225            Ok(TrieWitness::new(
226                MaskedTrieCursorFactory::new(
227                    InMemoryTrieCursorFactory::new(
228                        reth_trie_db::DatabaseTrieCursorFactory::<_, A>::new(self.tx()),
229                        &nodes_sorted,
230                    ),
231                    input.prefix_sets.freeze(),
232                ),
233                HashedPostStateCursorFactory::new(
234                    reth_trie_db::DatabaseHashedCursorFactory::new(self.tx()),
235                    &state_sorted,
236                ),
237            )
238            .always_include_root_node()
239            .compute(target)?
240            .into_values()
241            .collect())
242        })
243    }
244}
245
246impl<Provider: DBProvider> HashedPostStateProvider for LatestStateProviderRef<'_, Provider> {
247    fn hashed_post_state(&self, bundle_state: &revm_database::BundleState) -> HashedPostState {
248        HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state())
249    }
250}
251
252impl<Provider: DBProvider + BlockHashReader + StorageSettingsCache> StateProvider
253    for LatestStateProviderRef<'_, Provider>
254{
255    /// Get storage by plain (unhashed) storage key slot.
256    fn storage(
257        &self,
258        account: Address,
259        storage_key: StorageKey,
260    ) -> ProviderResult<Option<StorageValue>> {
261        if self.0.cached_storage_settings().use_hashed_state() {
262            self.hashed_storage_lookup(
263                alloy_primitives::keccak256(account),
264                alloy_primitives::keccak256(storage_key),
265            )
266        } else {
267            let mut cursor = self.tx().cursor_dup_read::<tables::PlainStorageState>()?;
268            if let Some(entry) = cursor.seek_by_key_subkey(account, storage_key)? &&
269                entry.key == storage_key
270            {
271                return Ok(Some(entry.value));
272            }
273            Ok(None)
274        }
275    }
276}
277
278impl<Provider: DBProvider + BlockHashReader> BytecodeReader
279    for LatestStateProviderRef<'_, Provider>
280{
281    /// Get account code by its hash
282    fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult<Option<Bytecode>> {
283        self.tx().get_by_encoded_key::<tables::Bytecodes>(code_hash).map_err(Into::into)
284    }
285}
286
287/// State provider for the latest state.
288#[derive(Debug)]
289pub struct LatestStateProvider<Provider>(Provider);
290
291impl<Provider: DBProvider> LatestStateProvider<Provider> {
292    /// Create new state provider
293    pub const fn new(db: Provider) -> Self {
294        Self(db)
295    }
296
297    /// Returns a new provider that takes the `TX` as reference
298    #[inline(always)]
299    const fn as_ref(&self) -> LatestStateProviderRef<'_, Provider> {
300        LatestStateProviderRef::new(&self.0)
301    }
302}
303
304// Delegates all provider impls to [LatestStateProviderRef]
305reth_storage_api::macros::delegate_provider_impls!(LatestStateProvider<Provider> where [Provider: DBProvider + BlockHashReader + StorageSettingsCache]);
306
307#[cfg(test)]
308mod tests {
309    use super::*;
310    use crate::test_utils::create_test_provider_factory;
311    use alloy_primitives::{address, b256, keccak256, U256};
312    use reth_db_api::{
313        models::StorageSettings,
314        tables,
315        transaction::{DbTx, DbTxMut},
316    };
317    use reth_primitives_traits::StorageEntry;
318    use reth_storage_api::StorageSettingsCache;
319
320    const fn assert_state_provider<T: StateProvider>() {}
321    #[expect(dead_code)]
322    const fn assert_latest_state_provider<
323        T: DBProvider + BlockHashReader + StorageSettingsCache,
324    >() {
325        assert_state_provider::<LatestStateProvider<T>>();
326    }
327
328    #[test]
329    fn test_latest_storage_hashed_state() {
330        let factory = create_test_provider_factory();
331        factory.set_storage_settings_cache(StorageSettings::v2());
332
333        let address = address!("0x0000000000000000000000000000000000000001");
334        let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
335
336        let hashed_address = keccak256(address);
337        let hashed_slot = keccak256(slot);
338
339        let tx = factory.provider_rw().unwrap().into_tx();
340        tx.put::<tables::HashedStorages>(
341            hashed_address,
342            StorageEntry { key: hashed_slot, value: U256::from(42) },
343        )
344        .unwrap();
345        tx.commit().unwrap();
346
347        let db = factory.provider().unwrap();
348        let provider_ref = LatestStateProviderRef::new(&db);
349
350        assert_eq!(provider_ref.storage(address, slot).unwrap(), Some(U256::from(42)));
351
352        let other_address = address!("0x0000000000000000000000000000000000000099");
353        let other_slot =
354            b256!("0x0000000000000000000000000000000000000000000000000000000000000099");
355        assert_eq!(provider_ref.storage(other_address, other_slot).unwrap(), None);
356
357        let tx = factory.provider_rw().unwrap().into_tx();
358        let plain_address = address!("0x0000000000000000000000000000000000000002");
359        let plain_slot =
360            b256!("0x0000000000000000000000000000000000000000000000000000000000000002");
361        tx.put::<tables::PlainStorageState>(
362            plain_address,
363            StorageEntry { key: plain_slot, value: U256::from(99) },
364        )
365        .unwrap();
366        tx.commit().unwrap();
367
368        let db = factory.provider().unwrap();
369        let provider_ref = LatestStateProviderRef::new(&db);
370        assert_eq!(provider_ref.storage(plain_address, plain_slot).unwrap(), None);
371    }
372
373    #[test]
374    fn test_latest_storage_hashed_state_returns_none_for_missing() {
375        let factory = create_test_provider_factory();
376        factory.set_storage_settings_cache(StorageSettings::v2());
377
378        let address = address!("0x0000000000000000000000000000000000000001");
379        let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
380
381        let db = factory.provider().unwrap();
382        let provider_ref = LatestStateProviderRef::new(&db);
383        assert_eq!(provider_ref.storage(address, slot).unwrap(), None);
384    }
385
386    #[test]
387    fn test_latest_storage_legacy() {
388        let factory = create_test_provider_factory();
389        assert!(!factory.provider().unwrap().cached_storage_settings().use_hashed_state());
390
391        let address = address!("0x0000000000000000000000000000000000000001");
392        let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000005");
393
394        let tx = factory.provider_rw().unwrap().into_tx();
395        tx.put::<tables::PlainStorageState>(
396            address,
397            StorageEntry { key: slot, value: U256::from(42) },
398        )
399        .unwrap();
400        tx.commit().unwrap();
401
402        let db = factory.provider().unwrap();
403        let provider_ref = LatestStateProviderRef::new(&db);
404
405        assert_eq!(provider_ref.storage(address, slot).unwrap(), Some(U256::from(42)));
406
407        let other_slot =
408            b256!("0x0000000000000000000000000000000000000000000000000000000000000099");
409        assert_eq!(provider_ref.storage(address, other_slot).unwrap(), None);
410    }
411
412    #[test]
413    fn test_latest_storage_legacy_does_not_read_hashed() {
414        let factory = create_test_provider_factory();
415        assert!(!factory.provider().unwrap().cached_storage_settings().use_hashed_state());
416
417        let address = address!("0x0000000000000000000000000000000000000001");
418        let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000005");
419        let hashed_address = keccak256(address);
420        let hashed_slot = keccak256(slot);
421
422        let tx = factory.provider_rw().unwrap().into_tx();
423        tx.put::<tables::HashedStorages>(
424            hashed_address,
425            StorageEntry { key: hashed_slot, value: U256::from(42) },
426        )
427        .unwrap();
428        tx.commit().unwrap();
429
430        let db = factory.provider().unwrap();
431        let provider_ref = LatestStateProviderRef::new(&db);
432        assert_eq!(provider_ref.storage(address, slot).unwrap(), None);
433    }
434}