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