reth_provider/providers/state/
latest.rs1use 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::HashedPostStateCursorFactory,
13 proof::{Proof, StorageProof},
14 trie_cursor::{masked::MaskedTrieCursorFactory, InMemoryTrieCursorFactory},
15 updates::TrieUpdates,
16 witness::TrieWitness,
17 AccountProof, HashedPostState, HashedStorage, KeccakKeyHasher, MultiProof, MultiProofTargets,
18 StateRoot, StorageMultiProof, StorageRoot, TrieInput, TrieInputSorted,
19};
20use reth_trie_db::{DatabaseProof, DatabaseStateRoot, DatabaseStorageProof, DatabaseStorageRoot};
21
22type DbStateRoot<'a, TX, A> = StateRoot<
23 reth_trie_db::DatabaseTrieCursorFactory<&'a TX, A>,
24 reth_trie_db::DatabaseHashedCursorFactory<&'a TX>,
25>;
26type DbStorageRoot<'a, TX, A> = StorageRoot<
27 reth_trie_db::DatabaseTrieCursorFactory<&'a TX, A>,
28 reth_trie_db::DatabaseHashedCursorFactory<&'a TX>,
29>;
30type DbStorageProof<'a, TX, A> = StorageProof<
31 'static,
32 reth_trie_db::DatabaseTrieCursorFactory<&'a TX, A>,
33 reth_trie_db::DatabaseHashedCursorFactory<&'a TX>,
34>;
35type DbProof<'a, TX, A> = Proof<
36 reth_trie_db::DatabaseTrieCursorFactory<&'a TX, A>,
37 reth_trie_db::DatabaseHashedCursorFactory<&'a TX>,
38>;
39#[derive(Debug)]
43pub struct LatestStateProviderRef<'b, Provider>(&'b Provider);
44
45impl<'b, Provider: DBProvider> LatestStateProviderRef<'b, Provider> {
46 pub const fn new(provider: &'b Provider) -> Self {
48 Self(provider)
49 }
50
51 fn tx(&self) -> &Provider::Tx {
52 self.0.tx_ref()
53 }
54
55 fn hashed_storage_lookup(
56 &self,
57 hashed_address: B256,
58 hashed_slot: StorageKey,
59 ) -> ProviderResult<Option<StorageValue>> {
60 let mut cursor = self.tx().cursor_dup_read::<tables::HashedStorages>()?;
61 Ok(cursor
62 .seek_by_key_subkey(hashed_address, hashed_slot)?
63 .filter(|e| e.key == hashed_slot)
64 .map(|e| e.value))
65 }
66}
67
68impl<Provider: DBProvider + StorageSettingsCache> AccountReader
69 for LatestStateProviderRef<'_, Provider>
70{
71 fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>> {
73 if self.0.cached_storage_settings().use_hashed_state() {
74 let hashed_address = alloy_primitives::keccak256(address);
75 self.tx()
76 .get_by_encoded_key::<tables::HashedAccounts>(&hashed_address)
77 .map_err(Into::into)
78 } else {
79 self.tx().get_by_encoded_key::<tables::PlainAccountState>(address).map_err(Into::into)
80 }
81 }
82}
83
84impl<Provider: BlockHashReader> BlockHashReader for LatestStateProviderRef<'_, Provider> {
85 fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>> {
87 self.0.block_hash(number)
88 }
89
90 fn canonical_hashes_range(
91 &self,
92 start: BlockNumber,
93 end: BlockNumber,
94 ) -> ProviderResult<Vec<B256>> {
95 self.0.canonical_hashes_range(start, end)
96 }
97}
98
99impl<Provider: DBProvider + StorageSettingsCache> StateRootProvider
100 for LatestStateProviderRef<'_, Provider>
101{
102 fn state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> {
103 reth_trie_db::with_adapter!(self.0, |A| {
104 let sorted = hashed_state.into_sorted();
105 Ok(<DbStateRoot<'_, _, A> as DatabaseStateRoot<_>>::overlay_root(self.tx(), &sorted)?)
106 })
107 }
108
109 fn state_root_from_nodes(&self, input: TrieInput) -> ProviderResult<B256> {
110 reth_trie_db::with_adapter!(self.0, |A| {
111 Ok(<DbStateRoot<'_, _, A> as DatabaseStateRoot<_>>::overlay_root_from_nodes(
112 self.tx(),
113 TrieInputSorted::from_unsorted(input),
114 )?)
115 })
116 }
117
118 fn state_root_with_updates(
119 &self,
120 hashed_state: HashedPostState,
121 ) -> ProviderResult<(B256, TrieUpdates)> {
122 reth_trie_db::with_adapter!(self.0, |A| {
123 let sorted = hashed_state.into_sorted();
124 Ok(<DbStateRoot<'_, _, A> as DatabaseStateRoot<_>>::overlay_root_with_updates(
125 self.tx(),
126 &sorted,
127 )?)
128 })
129 }
130
131 fn state_root_from_nodes_with_updates(
132 &self,
133 input: TrieInput,
134 ) -> ProviderResult<(B256, TrieUpdates)> {
135 reth_trie_db::with_adapter!(self.0, |A| {
136 Ok(
137 <DbStateRoot<'_, _, A> as DatabaseStateRoot<_>>::overlay_root_from_nodes_with_updates(
138 self.tx(),
139 TrieInputSorted::from_unsorted(input),
140 )?,
141 )
142 })
143 }
144}
145
146impl<Provider: DBProvider + StorageSettingsCache> StorageRootProvider
147 for LatestStateProviderRef<'_, Provider>
148{
149 fn storage_root(
150 &self,
151 address: Address,
152 hashed_storage: HashedStorage,
153 ) -> ProviderResult<B256> {
154 reth_trie_db::with_adapter!(self.0, |A| {
155 <DbStorageRoot<'_, _, A>>::overlay_root(self.tx(), address, hashed_storage)
156 .map_err(|err| ProviderError::Database(err.into()))
157 })
158 }
159
160 fn storage_proof(
161 &self,
162 address: Address,
163 slot: B256,
164 hashed_storage: HashedStorage,
165 ) -> ProviderResult<reth_trie::StorageProof> {
166 reth_trie_db::with_adapter!(self.0, |A| {
167 <DbStorageProof<'_, _, A>>::overlay_storage_proof(
168 self.tx(),
169 address,
170 slot,
171 hashed_storage,
172 )
173 .map_err(ProviderError::from)
174 })
175 }
176
177 fn storage_multiproof(
178 &self,
179 address: Address,
180 slots: &[B256],
181 hashed_storage: HashedStorage,
182 ) -> ProviderResult<StorageMultiProof> {
183 reth_trie_db::with_adapter!(self.0, |A| {
184 <DbStorageProof<'_, _, A>>::overlay_storage_multiproof(
185 self.tx(),
186 address,
187 slots,
188 hashed_storage,
189 )
190 .map_err(ProviderError::from)
191 })
192 }
193}
194
195impl<Provider: DBProvider + StorageSettingsCache> StateProofProvider
196 for LatestStateProviderRef<'_, Provider>
197{
198 fn proof(
199 &self,
200 input: TrieInput,
201 address: Address,
202 slots: &[B256],
203 ) -> ProviderResult<AccountProof> {
204 reth_trie_db::with_adapter!(self.0, |A| {
205 let proof = <DbProof<'_, _, A> as DatabaseProof>::from_tx(self.tx());
206 proof.overlay_account_proof(input, address, slots).map_err(ProviderError::from)
207 })
208 }
209
210 fn multiproof(
211 &self,
212 input: TrieInput,
213 targets: MultiProofTargets,
214 ) -> ProviderResult<MultiProof> {
215 reth_trie_db::with_adapter!(self.0, |A| {
216 let proof = <DbProof<'_, _, A> as DatabaseProof>::from_tx(self.tx());
217 proof.overlay_multiproof(input, targets).map_err(ProviderError::from)
218 })
219 }
220
221 fn witness(&self, input: TrieInput, target: HashedPostState) -> ProviderResult<Vec<Bytes>> {
222 reth_trie_db::with_adapter!(self.0, |A| {
223 let nodes_sorted = input.nodes.into_sorted();
224 let state_sorted = input.state.into_sorted();
225 Ok(TrieWitness::new(
226 MaskedTrieCursorFactory::new(
227 InMemoryTrieCursorFactory::new(
228 reth_trie_db::DatabaseTrieCursorFactory::<_, A>::new(self.tx()),
229 &nodes_sorted,
230 ),
231 input.prefix_sets.freeze(),
232 ),
233 HashedPostStateCursorFactory::new(
234 reth_trie_db::DatabaseHashedCursorFactory::new(self.tx()),
235 &state_sorted,
236 ),
237 )
238 .always_include_root_node()
239 .compute(target)?
240 .into_values()
241 .collect())
242 })
243 }
244}
245
246impl<Provider: DBProvider> HashedPostStateProvider for LatestStateProviderRef<'_, Provider> {
247 fn hashed_post_state(&self, bundle_state: &revm_database::BundleState) -> HashedPostState {
248 HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state())
249 }
250}
251
252impl<Provider: DBProvider + BlockHashReader + StorageSettingsCache> StateProvider
253 for LatestStateProviderRef<'_, Provider>
254{
255 fn storage(
257 &self,
258 account: Address,
259 storage_key: StorageKey,
260 ) -> ProviderResult<Option<StorageValue>> {
261 if self.0.cached_storage_settings().use_hashed_state() {
262 self.hashed_storage_lookup(
263 alloy_primitives::keccak256(account),
264 alloy_primitives::keccak256(storage_key),
265 )
266 } else {
267 let mut cursor = self.tx().cursor_dup_read::<tables::PlainStorageState>()?;
268 if let Some(entry) = cursor.seek_by_key_subkey(account, storage_key)? &&
269 entry.key == storage_key
270 {
271 return Ok(Some(entry.value));
272 }
273 Ok(None)
274 }
275 }
276}
277
278impl<Provider: DBProvider + BlockHashReader> BytecodeReader
279 for LatestStateProviderRef<'_, Provider>
280{
281 fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult<Option<Bytecode>> {
283 self.tx().get_by_encoded_key::<tables::Bytecodes>(code_hash).map_err(Into::into)
284 }
285}
286
287#[derive(Debug)]
289pub struct LatestStateProvider<Provider>(Provider);
290
291impl<Provider: DBProvider> LatestStateProvider<Provider> {
292 pub const fn new(db: Provider) -> Self {
294 Self(db)
295 }
296
297 #[inline(always)]
299 const fn as_ref(&self) -> LatestStateProviderRef<'_, Provider> {
300 LatestStateProviderRef::new(&self.0)
301 }
302}
303
304reth_storage_api::macros::delegate_provider_impls!(LatestStateProvider<Provider> where [Provider: DBProvider + BlockHashReader + StorageSettingsCache]);
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310 use crate::test_utils::create_test_provider_factory;
311 use alloy_primitives::{address, b256, keccak256, U256};
312 use reth_db_api::{
313 models::StorageSettings,
314 tables,
315 transaction::{DbTx, DbTxMut},
316 };
317 use reth_primitives_traits::StorageEntry;
318 use reth_storage_api::StorageSettingsCache;
319
320 const fn assert_state_provider<T: StateProvider>() {}
321 #[expect(dead_code)]
322 const fn assert_latest_state_provider<
323 T: DBProvider + BlockHashReader + StorageSettingsCache,
324 >() {
325 assert_state_provider::<LatestStateProvider<T>>();
326 }
327
328 #[test]
329 fn test_latest_storage_hashed_state() {
330 let factory = create_test_provider_factory();
331 factory.set_storage_settings_cache(StorageSettings::v2());
332
333 let address = address!("0x0000000000000000000000000000000000000001");
334 let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
335
336 let hashed_address = keccak256(address);
337 let hashed_slot = keccak256(slot);
338
339 let tx = factory.provider_rw().unwrap().into_tx();
340 tx.put::<tables::HashedStorages>(
341 hashed_address,
342 StorageEntry { key: hashed_slot, value: U256::from(42) },
343 )
344 .unwrap();
345 tx.commit().unwrap();
346
347 let db = factory.provider().unwrap();
348 let provider_ref = LatestStateProviderRef::new(&db);
349
350 assert_eq!(provider_ref.storage(address, slot).unwrap(), Some(U256::from(42)));
351
352 let other_address = address!("0x0000000000000000000000000000000000000099");
353 let other_slot =
354 b256!("0x0000000000000000000000000000000000000000000000000000000000000099");
355 assert_eq!(provider_ref.storage(other_address, other_slot).unwrap(), None);
356
357 let tx = factory.provider_rw().unwrap().into_tx();
358 let plain_address = address!("0x0000000000000000000000000000000000000002");
359 let plain_slot =
360 b256!("0x0000000000000000000000000000000000000000000000000000000000000002");
361 tx.put::<tables::PlainStorageState>(
362 plain_address,
363 StorageEntry { key: plain_slot, value: U256::from(99) },
364 )
365 .unwrap();
366 tx.commit().unwrap();
367
368 let db = factory.provider().unwrap();
369 let provider_ref = LatestStateProviderRef::new(&db);
370 assert_eq!(provider_ref.storage(plain_address, plain_slot).unwrap(), None);
371 }
372
373 #[test]
374 fn test_latest_storage_hashed_state_returns_none_for_missing() {
375 let factory = create_test_provider_factory();
376 factory.set_storage_settings_cache(StorageSettings::v2());
377
378 let address = address!("0x0000000000000000000000000000000000000001");
379 let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
380
381 let db = factory.provider().unwrap();
382 let provider_ref = LatestStateProviderRef::new(&db);
383 assert_eq!(provider_ref.storage(address, slot).unwrap(), None);
384 }
385
386 #[test]
387 fn test_latest_storage_legacy() {
388 let factory = create_test_provider_factory();
389 assert!(!factory.provider().unwrap().cached_storage_settings().use_hashed_state());
390
391 let address = address!("0x0000000000000000000000000000000000000001");
392 let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000005");
393
394 let tx = factory.provider_rw().unwrap().into_tx();
395 tx.put::<tables::PlainStorageState>(
396 address,
397 StorageEntry { key: slot, value: U256::from(42) },
398 )
399 .unwrap();
400 tx.commit().unwrap();
401
402 let db = factory.provider().unwrap();
403 let provider_ref = LatestStateProviderRef::new(&db);
404
405 assert_eq!(provider_ref.storage(address, slot).unwrap(), Some(U256::from(42)));
406
407 let other_slot =
408 b256!("0x0000000000000000000000000000000000000000000000000000000000000099");
409 assert_eq!(provider_ref.storage(address, other_slot).unwrap(), None);
410 }
411
412 #[test]
413 fn test_latest_storage_legacy_does_not_read_hashed() {
414 let factory = create_test_provider_factory();
415 assert!(!factory.provider().unwrap().cached_storage_settings().use_hashed_state());
416
417 let address = address!("0x0000000000000000000000000000000000000001");
418 let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000005");
419 let hashed_address = keccak256(address);
420 let hashed_slot = keccak256(slot);
421
422 let tx = factory.provider_rw().unwrap().into_tx();
423 tx.put::<tables::HashedStorages>(
424 hashed_address,
425 StorageEntry { key: hashed_slot, value: U256::from(42) },
426 )
427 .unwrap();
428 tx.commit().unwrap();
429
430 let db = factory.provider().unwrap();
431 let provider_ref = LatestStateProviderRef::new(&db);
432 assert_eq!(provider_ref.storage(address, slot).unwrap(), None);
433 }
434}