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#[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 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 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 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 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#[derive(Debug)]
338pub struct LatestStateProvider<Provider>(Provider);
339
340impl<Provider: DBProvider> LatestStateProvider<Provider> {
341 pub const fn new(db: Provider) -> Self {
343 Self(db)
344 }
345
346 #[inline(always)]
348 const fn as_ref(&self) -> LatestStateProviderRef<'_, Provider> {
349 LatestStateProviderRef::new(&self.0)
350 }
351}
352
353reth_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}