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