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    proof_v2::MultiProofCalculator,
15    trie_cursor::InMemoryTrieCursorFactory,
16    updates::TrieUpdates,
17    witness::TrieWitness,
18    AccountProof, DecodedMultiProofV2, ExecutionWitnessMode, HashedPostState, HashedStorage,
19    KeccakKeyHasher, MultiProof, MultiProofTargets, MultiProofTargetsV2, StateRoot,
20    StorageMultiProof, StorageRoot, TrieInput, TrieInputSorted,
21};
22use reth_trie_db::{DatabaseProof, DatabaseStateRoot, DatabaseStorageProof, DatabaseStorageRoot};
23
24type DbStateRoot<'a, TX, A> = StateRoot<
25    reth_trie_db::DatabaseTrieCursorFactory<&'a TX, A>,
26    reth_trie_db::DatabaseHashedCursorFactory<&'a TX>,
27>;
28type DbStorageRoot<'a, TX, A> = StorageRoot<
29    reth_trie_db::DatabaseTrieCursorFactory<&'a TX, A>,
30    reth_trie_db::DatabaseHashedCursorFactory<&'a TX>,
31>;
32type DbStorageProof<'a, TX, A> = StorageProof<
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        targets: MultiProofTargetsV2,
234        _requires_masks: bool,
235    ) -> ProviderResult<DecodedMultiProofV2> {
236        // The latest database trie is consistent with its hashed state, so masks are always exact
237        // and `requires_masks` needs no special handling.
238        reth_trie_db::with_adapter!(self.0, |A| {
239            MultiProofCalculator::new(
240                reth_trie_db::DatabaseTrieCursorFactory::<_, A>::new(self.tx()),
241                reth_trie_db::DatabaseHashedCursorFactory::new(self.tx()),
242                Default::default(),
243                Default::default(),
244            )
245            .multiproof(targets)
246            .map_err(ProviderError::from)
247        })
248    }
249
250    fn witness(
251        &self,
252        input: TrieInput,
253        target: HashedPostState,
254        mode: ExecutionWitnessMode,
255    ) -> ProviderResult<Vec<Bytes>> {
256        reth_trie_db::with_adapter!(self.0, |A| {
257            let nodes_sorted = input.nodes.into_sorted();
258            let state_sorted = input.state.into_sorted();
259            let witness = TrieWitness::new(
260                InMemoryTrieCursorFactory::new(
261                    reth_trie_db::DatabaseTrieCursorFactory::<_, A>::new(self.tx()),
262                    &nodes_sorted,
263                ),
264                HashedPostStateCursorFactory::new(
265                    reth_trie_db::DatabaseHashedCursorFactory::new(self.tx()),
266                    &state_sorted,
267                ),
268            )
269            .with_prefix_sets_mut(input.prefix_sets)
270            .with_execution_witness_mode(mode);
271            let witness =
272                if mode.is_canonical() { witness } else { witness.always_include_root_node() };
273            let mut values: Vec<_> = witness.compute(target)?.into_values().collect();
274            if mode.is_canonical() {
275                values.sort_unstable();
276            }
277            Ok(values)
278        })
279    }
280}
281
282impl<Provider: DBProvider> HashedPostStateProvider for LatestStateProviderRef<'_, Provider> {
283    fn hashed_post_state(
284        &self,
285        bundle_state: &revm::database::BundleState,
286    ) -> ProviderResult<HashedPostState> {
287        let mut hashed_state =
288            HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state());
289        zero_destroyed_account_storage(
290            &reth_trie_db::DatabaseHashedCursorFactory::new(self.tx()),
291            bundle_state.state(),
292            &mut hashed_state,
293        )?;
294        Ok(hashed_state)
295    }
296}
297
298impl<Provider: DBProvider + BlockHashReader + StorageSettingsCache> StateProvider
299    for LatestStateProviderRef<'_, Provider>
300{
301    /// Get storage by plain (unhashed) storage key slot.
302    fn storage(
303        &self,
304        account: Address,
305        storage_key: StorageKey,
306    ) -> ProviderResult<Option<StorageValue>> {
307        if self.0.cached_storage_settings().use_hashed_state() {
308            self.hashed_storage_lookup(
309                alloy_primitives::keccak256(account),
310                alloy_primitives::keccak256(storage_key),
311            )
312        } else {
313            let mut cursor = self.tx().cursor_dup_read::<tables::PlainStorageState>()?;
314            if let Some(entry) = cursor.seek_by_key_subkey(account, storage_key)? &&
315                entry.key == storage_key
316            {
317                return Ok(Some(entry.value));
318            }
319            Ok(None)
320        }
321    }
322}
323
324impl<Provider: DBProvider + BlockHashReader> BytecodeReader
325    for LatestStateProviderRef<'_, Provider>
326{
327    /// Get account code by its hash
328    fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult<Option<Bytecode>> {
329        self.tx()
330            .prefetch(true)
331            .get_by_encoded_key::<tables::Bytecodes>(code_hash)
332            .map_err(Into::into)
333    }
334}
335
336/// State provider for the latest state.
337#[derive(Debug)]
338pub struct LatestStateProvider<Provider>(Provider);
339
340impl<Provider: DBProvider> LatestStateProvider<Provider> {
341    /// Create new state provider
342    pub const fn new(db: Provider) -> Self {
343        Self(db)
344    }
345
346    /// Returns a new provider that takes the `TX` as reference
347    #[inline(always)]
348    const fn as_ref(&self) -> LatestStateProviderRef<'_, Provider> {
349        LatestStateProviderRef::new(&self.0)
350    }
351}
352
353// Delegates all provider impls to [LatestStateProviderRef]
354reth_storage_api::macros::delegate_provider_impls!(LatestStateProvider<Provider> where [Provider: DBProvider + BlockHashReader + StorageSettingsCache]);
355
356#[cfg(test)]
357mod tests {
358
359    use super::*;
360    use crate::test_utils::create_test_provider_factory;
361    use alloy_primitives::{keccak256, U256};
362    use reth_db_api::{
363        models::StorageSettings,
364        tables,
365        transaction::{DbTx, DbTxMut},
366    };
367    use reth_primitives_traits::StorageEntry;
368    use reth_storage_api::StorageSettingsCache;
369
370    const fn assert_state_provider<T: StateProvider>() {}
371    #[expect(dead_code)]
372    const fn assert_latest_state_provider<
373        T: DBProvider + BlockHashReader + StorageSettingsCache,
374    >() {
375        assert_state_provider::<LatestStateProvider<T>>();
376    }
377
378    #[test]
379    fn test_latest_storage_hashed_state() {
380        let factory = create_test_provider_factory();
381        factory.set_storage_settings_cache(StorageSettings::v2());
382
383        let address = Address::with_last_byte(1);
384        let slot = B256::with_last_byte(1);
385
386        let hashed_address = keccak256(address);
387        let hashed_slot = keccak256(slot);
388
389        let tx = factory.provider_rw().unwrap().into_tx();
390        tx.put::<tables::HashedStorages>(
391            hashed_address,
392            StorageEntry { key: hashed_slot, value: U256::from(42) },
393        )
394        .unwrap();
395        tx.commit().unwrap();
396
397        let db = factory.provider().unwrap();
398        let provider_ref = LatestStateProviderRef::new(&db);
399
400        assert_eq!(provider_ref.storage(address, slot).unwrap(), Some(U256::from(42)));
401
402        let other_address = Address::with_last_byte(0x99);
403        let other_slot = B256::with_last_byte(0x99);
404        assert_eq!(provider_ref.storage(other_address, other_slot).unwrap(), None);
405
406        let tx = factory.provider_rw().unwrap().into_tx();
407        let plain_address = Address::with_last_byte(2);
408        let plain_slot = B256::with_last_byte(2);
409        tx.put::<tables::PlainStorageState>(
410            plain_address,
411            StorageEntry { key: plain_slot, value: U256::from(99) },
412        )
413        .unwrap();
414        tx.commit().unwrap();
415
416        let db = factory.provider().unwrap();
417        let provider_ref = LatestStateProviderRef::new(&db);
418        assert_eq!(provider_ref.storage(plain_address, plain_slot).unwrap(), None);
419    }
420
421    #[test]
422    fn test_latest_storage_hashed_state_returns_none_for_missing() {
423        let factory = create_test_provider_factory();
424        factory.set_storage_settings_cache(StorageSettings::v2());
425
426        let address = Address::with_last_byte(1);
427        let slot = B256::with_last_byte(1);
428
429        let db = factory.provider().unwrap();
430        let provider_ref = LatestStateProviderRef::new(&db);
431        assert_eq!(provider_ref.storage(address, slot).unwrap(), None);
432    }
433
434    #[test]
435    fn test_latest_storage_legacy() {
436        let factory = create_test_provider_factory();
437        assert!(!factory.provider().unwrap().cached_storage_settings().use_hashed_state());
438
439        let address = Address::with_last_byte(1);
440        let slot = B256::with_last_byte(5);
441
442        let tx = factory.provider_rw().unwrap().into_tx();
443        tx.put::<tables::PlainStorageState>(
444            address,
445            StorageEntry { key: slot, value: U256::from(42) },
446        )
447        .unwrap();
448        tx.commit().unwrap();
449
450        let db = factory.provider().unwrap();
451        let provider_ref = LatestStateProviderRef::new(&db);
452
453        assert_eq!(provider_ref.storage(address, slot).unwrap(), Some(U256::from(42)));
454
455        let other_slot = B256::with_last_byte(0x99);
456        assert_eq!(provider_ref.storage(address, other_slot).unwrap(), None);
457    }
458
459    #[test]
460    fn test_latest_storage_legacy_does_not_read_hashed() {
461        let factory = create_test_provider_factory();
462        assert!(!factory.provider().unwrap().cached_storage_settings().use_hashed_state());
463
464        let address = Address::with_last_byte(1);
465        let slot = B256::with_last_byte(5);
466        let hashed_address = keccak256(address);
467        let hashed_slot = keccak256(slot);
468
469        let tx = factory.provider_rw().unwrap().into_tx();
470        tx.put::<tables::HashedStorages>(
471            hashed_address,
472            StorageEntry { key: hashed_slot, value: U256::from(42) },
473        )
474        .unwrap();
475        tx.commit().unwrap();
476
477        let db = factory.provider().unwrap();
478        let provider_ref = LatestStateProviderRef::new(&db);
479        assert_eq!(provider_ref.storage(address, slot).unwrap(), None);
480    }
481}