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#[derive(Debug)]
44pub struct LatestStateProviderRef<'b, Provider>(&'b Provider);
45
46impl<'b, Provider: DBProvider> LatestStateProviderRef<'b, Provider> {
47 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 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 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 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 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#[derive(Debug)]
318pub struct LatestStateProvider<Provider>(Provider);
319
320impl<Provider: DBProvider> LatestStateProvider<Provider> {
321 pub const fn new(db: Provider) -> Self {
323 Self(db)
324 }
325
326 #[inline(always)]
328 const fn as_ref(&self) -> LatestStateProviderRef<'_, Provider> {
329 LatestStateProviderRef::new(&self.0)
330 }
331}
332
333reth_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}