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