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::{zero_destroyed_account_storage, HashedPostStateCursorFactory},
13    proof::{Proof, StorageProof},
14    trie_cursor::InMemoryTrieCursorFactory,
15    updates::TrieUpdates,
16    witness::TrieWitness,
17    AccountProof, DecodedMultiProofV2, ExecutionWitnessMode, HashedPostState, HashedStorage,
18    KeccakKeyHasher, MultiProof, MultiProofTargets, MultiProofTargetsV2, StateRoot,
19    StorageMultiProof, StorageRoot, TrieInput, 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            let input = TrieInputSorted::from_state(
157                HashedPostState::from_hashed_storage(
158                    alloy_primitives::keccak256(address),
159                    hashed_storage,
160                )
161                .into_sorted(),
162            );
163            <DbStorageRoot<'_, _, A>>::overlay_root(self.tx(), address, input)
164                .map_err(|err| ProviderError::Database(err.into()))
165        })
166    }
167
168    fn storage_proof(
169        &self,
170        address: Address,
171        slot: B256,
172        hashed_storage: HashedStorage,
173    ) -> ProviderResult<reth_trie::StorageProof> {
174        reth_trie_db::with_adapter!(self.0, |A| {
175            <DbStorageProof<'_, _, A>>::overlay_storage_proof(
176                self.tx(),
177                address,
178                slot,
179                hashed_storage,
180            )
181            .map_err(ProviderError::from)
182        })
183    }
184
185    fn storage_multiproof(
186        &self,
187        address: Address,
188        slots: &[B256],
189        hashed_storage: HashedStorage,
190    ) -> ProviderResult<StorageMultiProof> {
191        reth_trie_db::with_adapter!(self.0, |A| {
192            let input = TrieInputSorted::from_state(
193                HashedPostState::from_hashed_storage(
194                    alloy_primitives::keccak256(address),
195                    hashed_storage,
196                )
197                .into_sorted(),
198            );
199            <DbStorageProof<'_, _, A>>::overlay_storage_multiproof(self.tx(), address, slots, input)
200                .map_err(ProviderError::from)
201        })
202    }
203}
204
205impl<Provider: DBProvider + StorageSettingsCache> StateProofProvider
206    for LatestStateProviderRef<'_, Provider>
207{
208    fn proof(
209        &self,
210        input: TrieInput,
211        address: Address,
212        slots: &[B256],
213    ) -> ProviderResult<AccountProof> {
214        reth_trie_db::with_adapter!(self.0, |A| {
215            let proof = <DbProof<'_, _, A> as DatabaseProof>::from_tx(self.tx());
216            proof.overlay_account_proof(input, address, slots).map_err(ProviderError::from)
217        })
218    }
219
220    fn multiproof(
221        &self,
222        input: TrieInput,
223        targets: MultiProofTargets,
224    ) -> ProviderResult<MultiProof> {
225        reth_trie_db::with_adapter!(self.0, |A| {
226            let proof = <DbProof<'_, _, A> as DatabaseProof>::from_tx(self.tx());
227            proof.overlay_multiproof(input, targets).map_err(ProviderError::from)
228        })
229    }
230
231    fn multiproof_v2(
232        &self,
233        input: TrieInput,
234        targets: MultiProofTargetsV2,
235    ) -> ProviderResult<DecodedMultiProofV2> {
236        reth_trie_db::with_adapter!(self.0, |A| {
237            let proof = <DbProof<'_, _, A> as DatabaseProof>::from_tx(self.tx());
238            proof.overlay_multiproof_v2(input, targets).map_err(ProviderError::from)
239        })
240    }
241
242    fn witness(
243        &self,
244        input: TrieInput,
245        target: HashedPostState,
246        mode: ExecutionWitnessMode,
247    ) -> ProviderResult<Vec<Bytes>> {
248        reth_trie_db::with_adapter!(self.0, |A| {
249            let nodes_sorted = input.nodes.into_sorted();
250            let state_sorted = input.state.into_sorted();
251            let witness = TrieWitness::new(
252                InMemoryTrieCursorFactory::new(
253                    reth_trie_db::DatabaseTrieCursorFactory::<_, A>::new(self.tx()),
254                    &nodes_sorted,
255                ),
256                HashedPostStateCursorFactory::new(
257                    reth_trie_db::DatabaseHashedCursorFactory::new(self.tx()),
258                    &state_sorted,
259                ),
260            )
261            .with_prefix_sets_mut(input.prefix_sets)
262            .with_execution_witness_mode(mode);
263            let witness =
264                if mode.is_canonical() { witness } else { witness.always_include_root_node() };
265            let mut values: Vec<_> = witness.compute(target)?.into_values().collect();
266            if mode.is_canonical() {
267                values.sort_unstable();
268            }
269            Ok(values)
270        })
271    }
272}
273
274impl<Provider: DBProvider> HashedPostStateProvider for LatestStateProviderRef<'_, Provider> {
275    fn hashed_post_state(
276        &self,
277        bundle_state: &revm::database::BundleState,
278    ) -> ProviderResult<HashedPostState> {
279        let mut hashed_state =
280            HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state());
281        zero_destroyed_account_storage(
282            &reth_trie_db::DatabaseHashedCursorFactory::new(self.tx()),
283            bundle_state.state(),
284            &mut hashed_state,
285        )?;
286        Ok(hashed_state)
287    }
288}
289
290impl<Provider: DBProvider + BlockHashReader + StorageSettingsCache> StateProvider
291    for LatestStateProviderRef<'_, Provider>
292{
293    /// Get storage by plain (unhashed) storage key slot.
294    fn storage(
295        &self,
296        account: Address,
297        storage_key: StorageKey,
298    ) -> ProviderResult<Option<StorageValue>> {
299        if self.0.cached_storage_settings().use_hashed_state() {
300            self.hashed_storage_lookup(
301                alloy_primitives::keccak256(account),
302                alloy_primitives::keccak256(storage_key),
303            )
304        } else {
305            let mut cursor = self.tx().cursor_dup_read::<tables::PlainStorageState>()?;
306            if let Some(entry) = cursor.seek_by_key_subkey(account, storage_key)? &&
307                entry.key == storage_key
308            {
309                return Ok(Some(entry.value));
310            }
311            Ok(None)
312        }
313    }
314}
315
316impl<Provider: DBProvider + BlockHashReader> BytecodeReader
317    for LatestStateProviderRef<'_, Provider>
318{
319    /// Get account code by its hash
320    fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult<Option<Bytecode>> {
321        self.tx().get_by_encoded_key::<tables::Bytecodes>(code_hash).map_err(Into::into)
322    }
323}
324
325/// State provider for the latest state.
326#[derive(Debug)]
327pub struct LatestStateProvider<Provider>(Provider);
328
329impl<Provider: DBProvider> LatestStateProvider<Provider> {
330    /// Create new state provider
331    pub const fn new(db: Provider) -> Self {
332        Self(db)
333    }
334
335    /// Returns a new provider that takes the `TX` as reference
336    #[inline(always)]
337    const fn as_ref(&self) -> LatestStateProviderRef<'_, Provider> {
338        LatestStateProviderRef::new(&self.0)
339    }
340}
341
342// Delegates all provider impls to [LatestStateProviderRef]
343reth_storage_api::macros::delegate_provider_impls!(LatestStateProvider<Provider> where [Provider: DBProvider + BlockHashReader + StorageSettingsCache]);
344
345#[cfg(test)]
346mod tests {
347    use super::*;
348    use crate::test_utils::create_test_provider_factory;
349    use alloy_primitives::{address, b256, keccak256, U256};
350    use reth_db_api::{
351        models::StorageSettings,
352        tables,
353        transaction::{DbTx, DbTxMut},
354    };
355    use reth_primitives_traits::StorageEntry;
356    use reth_storage_api::StorageSettingsCache;
357
358    const fn assert_state_provider<T: StateProvider>() {}
359    #[expect(dead_code)]
360    const fn assert_latest_state_provider<
361        T: DBProvider + BlockHashReader + StorageSettingsCache,
362    >() {
363        assert_state_provider::<LatestStateProvider<T>>();
364    }
365
366    #[test]
367    fn test_latest_storage_hashed_state() {
368        let factory = create_test_provider_factory();
369        factory.set_storage_settings_cache(StorageSettings::v2());
370
371        let address = address!("0x0000000000000000000000000000000000000001");
372        let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
373
374        let hashed_address = keccak256(address);
375        let hashed_slot = keccak256(slot);
376
377        let tx = factory.provider_rw().unwrap().into_tx();
378        tx.put::<tables::HashedStorages>(
379            hashed_address,
380            StorageEntry { key: hashed_slot, value: U256::from(42) },
381        )
382        .unwrap();
383        tx.commit().unwrap();
384
385        let db = factory.provider().unwrap();
386        let provider_ref = LatestStateProviderRef::new(&db);
387
388        assert_eq!(provider_ref.storage(address, slot).unwrap(), Some(U256::from(42)));
389
390        let other_address = address!("0x0000000000000000000000000000000000000099");
391        let other_slot =
392            b256!("0x0000000000000000000000000000000000000000000000000000000000000099");
393        assert_eq!(provider_ref.storage(other_address, other_slot).unwrap(), None);
394
395        let tx = factory.provider_rw().unwrap().into_tx();
396        let plain_address = address!("0x0000000000000000000000000000000000000002");
397        let plain_slot =
398            b256!("0x0000000000000000000000000000000000000000000000000000000000000002");
399        tx.put::<tables::PlainStorageState>(
400            plain_address,
401            StorageEntry { key: plain_slot, value: U256::from(99) },
402        )
403        .unwrap();
404        tx.commit().unwrap();
405
406        let db = factory.provider().unwrap();
407        let provider_ref = LatestStateProviderRef::new(&db);
408        assert_eq!(provider_ref.storage(plain_address, plain_slot).unwrap(), None);
409    }
410
411    #[test]
412    fn test_latest_storage_hashed_state_returns_none_for_missing() {
413        let factory = create_test_provider_factory();
414        factory.set_storage_settings_cache(StorageSettings::v2());
415
416        let address = address!("0x0000000000000000000000000000000000000001");
417        let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
418
419        let db = factory.provider().unwrap();
420        let provider_ref = LatestStateProviderRef::new(&db);
421        assert_eq!(provider_ref.storage(address, slot).unwrap(), None);
422    }
423
424    #[test]
425    fn test_latest_storage_legacy() {
426        let factory = create_test_provider_factory();
427        assert!(!factory.provider().unwrap().cached_storage_settings().use_hashed_state());
428
429        let address = address!("0x0000000000000000000000000000000000000001");
430        let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000005");
431
432        let tx = factory.provider_rw().unwrap().into_tx();
433        tx.put::<tables::PlainStorageState>(
434            address,
435            StorageEntry { key: slot, value: U256::from(42) },
436        )
437        .unwrap();
438        tx.commit().unwrap();
439
440        let db = factory.provider().unwrap();
441        let provider_ref = LatestStateProviderRef::new(&db);
442
443        assert_eq!(provider_ref.storage(address, slot).unwrap(), Some(U256::from(42)));
444
445        let other_slot =
446            b256!("0x0000000000000000000000000000000000000000000000000000000000000099");
447        assert_eq!(provider_ref.storage(address, other_slot).unwrap(), None);
448    }
449
450    #[test]
451    fn test_latest_storage_legacy_does_not_read_hashed() {
452        let factory = create_test_provider_factory();
453        assert!(!factory.provider().unwrap().cached_storage_settings().use_hashed_state());
454
455        let address = address!("0x0000000000000000000000000000000000000001");
456        let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000005");
457        let hashed_address = keccak256(address);
458        let hashed_slot = keccak256(slot);
459
460        let tx = factory.provider_rw().unwrap().into_tx();
461        tx.put::<tables::HashedStorages>(
462            hashed_address,
463            StorageEntry { key: hashed_slot, value: U256::from(42) },
464        )
465        .unwrap();
466        tx.commit().unwrap();
467
468        let db = factory.provider().unwrap();
469        let provider_ref = LatestStateProviderRef::new(&db);
470        assert_eq!(provider_ref.storage(address, slot).unwrap(), None);
471    }
472}