diff --git a/crates/chain-state/src/in_memory.rs b/crates/chain-state/src/in_memory.rs index f47417aa3..07120cf8e 100644 --- a/crates/chain-state/src/in_memory.rs +++ b/crates/chain-state/src/in_memory.rs @@ -872,7 +872,7 @@ mod tests { AccountReader, BlockHashReader, StateProofProvider, StateProvider, StateRootProvider, StorageRootProvider, }; - use reth_trie::{AccountProof, HashedStorage, MultiProof, TrieInput}; + use reth_trie::{AccountProof, HashedStorage, MultiProof, StorageProof, TrieInput}; fn create_mock_state( test_block_builder: &mut TestBlockBuilder, @@ -973,6 +973,15 @@ mod tests { ) -> ProviderResult { Ok(B256::random()) } + + fn storage_proof( + &self, + _address: Address, + slot: B256, + _hashed_storage: HashedStorage, + ) -> ProviderResult { + Ok(StorageProof::new(slot)) + } } impl StateProofProvider for MockStateProvider { diff --git a/crates/chain-state/src/memory_overlay.rs b/crates/chain-state/src/memory_overlay.rs index 2712d1259..eb125dad1 100644 --- a/crates/chain-state/src/memory_overlay.rs +++ b/crates/chain-state/src/memory_overlay.rs @@ -133,11 +133,26 @@ impl StateRootProvider for MemoryOverlayStateProvider { impl StorageRootProvider for MemoryOverlayStateProvider { // TODO: Currently this does not reuse available in-memory trie nodes. fn storage_root(&self, address: Address, storage: HashedStorage) -> ProviderResult { + let state = &self.trie_state().state; let mut hashed_storage = - self.trie_state().state.storages.get(&keccak256(address)).cloned().unwrap_or_default(); + state.storages.get(&keccak256(address)).cloned().unwrap_or_default(); hashed_storage.extend(&storage); self.historical.storage_root(address, hashed_storage) } + + // TODO: Currently this does not reuse available in-memory trie nodes. + fn storage_proof( + &self, + address: Address, + slot: B256, + storage: HashedStorage, + ) -> ProviderResult { + let state = &self.trie_state().state; + let mut hashed_storage = + state.storages.get(&keccak256(address)).cloned().unwrap_or_default(); + hashed_storage.extend(&storage); + self.historical.storage_proof(address, slot, hashed_storage) + } } impl StateProofProvider for MemoryOverlayStateProvider { diff --git a/crates/revm/src/test_utils.rs b/crates/revm/src/test_utils.rs index d42ec4959..813997c72 100644 --- a/crates/revm/src/test_utils.rs +++ b/crates/revm/src/test_utils.rs @@ -11,7 +11,8 @@ use reth_storage_api::{ }; use reth_storage_errors::provider::ProviderResult; use reth_trie::{ - updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof, TrieInput, + updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof, StorageProof, + TrieInput, }; /// Mock state for testing @@ -102,6 +103,15 @@ impl StorageRootProvider for StateProviderTest { ) -> ProviderResult { unimplemented!("storage root is not supported") } + + fn storage_proof( + &self, + _address: Address, + _slot: B256, + _hashed_storage: HashedStorage, + ) -> ProviderResult { + unimplemented!("proof generation is not supported") + } } impl StateProofProvider for StateProviderTest { diff --git a/crates/rpc/rpc-eth-types/src/cache/db.rs b/crates/rpc/rpc-eth-types/src/cache/db.rs index 9731e3845..7422dcfb8 100644 --- a/crates/rpc/rpc-eth-types/src/cache/db.rs +++ b/crates/rpc/rpc-eth-types/src/cache/db.rs @@ -58,6 +58,15 @@ impl reth_storage_api::StorageRootProvider for StateProviderTraitObjWrapper<'_> ) -> ProviderResult { self.0.storage_root(address, hashed_storage) } + + fn storage_proof( + &self, + address: Address, + slot: B256, + hashed_storage: HashedStorage, + ) -> ProviderResult { + self.0.storage_proof(address, slot, hashed_storage) + } } impl reth_storage_api::StateProofProvider for StateProviderTraitObjWrapper<'_> { diff --git a/crates/storage/provider/src/providers/bundle_state_provider.rs b/crates/storage/provider/src/providers/bundle_state_provider.rs index 6fe3fa85c..296dae8c6 100644 --- a/crates/storage/provider/src/providers/bundle_state_provider.rs +++ b/crates/storage/provider/src/providers/bundle_state_provider.rs @@ -31,6 +31,20 @@ impl BundleStateProvider pub const fn new(state_provider: SP, block_execution_data_provider: EDP) -> Self { Self { state_provider, block_execution_data_provider } } + + /// Retrieve hashed storage for target address. + fn get_hashed_storage(&self, address: Address) -> HashedStorage { + let bundle_state = self.block_execution_data_provider.execution_outcome().state(); + bundle_state + .account(&address) + .map(|account| { + HashedStorage::from_plain_storage( + account.status, + account.storage.iter().map(|(slot, value)| (slot, &value.present_value)), + ) + }) + .unwrap_or_else(|| HashedStorage::new(false)) + } } /* Implement StateProvider traits */ @@ -109,19 +123,21 @@ impl StorageRootProvider address: Address, hashed_storage: HashedStorage, ) -> ProviderResult { - let bundle_state = self.block_execution_data_provider.execution_outcome().state(); - let mut storage = bundle_state - .account(&address) - .map(|account| { - HashedStorage::from_plain_storage( - account.status, - account.storage.iter().map(|(slot, value)| (slot, &value.present_value)), - ) - }) - .unwrap_or_else(|| HashedStorage::new(false)); + let mut storage = self.get_hashed_storage(address); storage.extend(&hashed_storage); self.state_provider.storage_root(address, storage) } + + fn storage_proof( + &self, + address: Address, + slot: B256, + hashed_storage: HashedStorage, + ) -> ProviderResult { + let mut storage = self.get_hashed_storage(address); + storage.extend(&hashed_storage); + self.state_provider.storage_proof(address, slot, storage) + } } impl StateProofProvider diff --git a/crates/storage/provider/src/providers/state/historical.rs b/crates/storage/provider/src/providers/state/historical.rs index 22f40f6f9..781a11f6d 100644 --- a/crates/storage/provider/src/providers/state/historical.rs +++ b/crates/storage/provider/src/providers/state/historical.rs @@ -17,12 +17,14 @@ use reth_primitives::{constants::EPOCH_SLOTS, Account, Bytecode, StaticFileSegme use reth_storage_api::{StateProofProvider, StorageRootProvider}; use reth_storage_errors::provider::ProviderResult; use reth_trie::{ - proof::Proof, updates::TrieUpdates, witness::TrieWitness, AccountProof, HashedPostState, - HashedStorage, MultiProof, StateRoot, StorageRoot, TrieInput, + proof::{Proof, StorageProof}, + updates::TrieUpdates, + witness::TrieWitness, + AccountProof, HashedPostState, HashedStorage, MultiProof, StateRoot, StorageRoot, TrieInput, }; use reth_trie_db::{ DatabaseHashedPostState, DatabaseHashedStorage, DatabaseProof, DatabaseStateRoot, - DatabaseStorageRoot, DatabaseTrieWitness, + DatabaseStorageProof, DatabaseStorageRoot, DatabaseTrieWitness, }; use std::fmt::Debug; @@ -330,6 +332,18 @@ impl StorageRootProvider for HistoricalStateProviderRef<'_, TX> { StorageRoot::overlay_root(self.tx, address, revert_storage) .map_err(|err| ProviderError::Database(err.into())) } + + fn storage_proof( + &self, + address: Address, + slot: B256, + hashed_storage: HashedStorage, + ) -> ProviderResult { + let mut revert_storage = self.revert_storage(address)?; + revert_storage.extend(&hashed_storage); + StorageProof::overlay_storage_proof(self.tx, address, slot, revert_storage) + .map_err(Into::::into) + } } impl StateProofProvider for HistoricalStateProviderRef<'_, TX> { diff --git a/crates/storage/provider/src/providers/state/latest.rs b/crates/storage/provider/src/providers/state/latest.rs index 9fbe00cbd..fdcbfc493 100644 --- a/crates/storage/provider/src/providers/state/latest.rs +++ b/crates/storage/provider/src/providers/state/latest.rs @@ -15,10 +15,15 @@ use reth_primitives::{Account, Bytecode, StaticFileSegment}; use reth_storage_api::{StateProofProvider, StorageRootProvider}; use reth_storage_errors::provider::{ProviderError, ProviderResult}; use reth_trie::{ - proof::Proof, updates::TrieUpdates, witness::TrieWitness, AccountProof, HashedPostState, - HashedStorage, MultiProof, StateRoot, StorageRoot, TrieInput, + proof::{Proof, StorageProof}, + updates::TrieUpdates, + witness::TrieWitness, + AccountProof, HashedPostState, HashedStorage, MultiProof, StateRoot, StorageRoot, TrieInput, +}; +use reth_trie_db::{ + DatabaseProof, DatabaseStateRoot, DatabaseStorageProof, DatabaseStorageRoot, + DatabaseTrieWitness, }; -use reth_trie_db::{DatabaseProof, DatabaseStateRoot, DatabaseStorageRoot, DatabaseTrieWitness}; /// State provider over latest state that takes tx reference. #[derive(Debug)] @@ -116,6 +121,16 @@ impl StorageRootProvider for LatestStateProviderRef<'_, TX> { StorageRoot::overlay_root(self.tx, address, hashed_storage) .map_err(|err| ProviderError::Database(err.into())) } + + fn storage_proof( + &self, + address: Address, + slot: B256, + hashed_storage: HashedStorage, + ) -> ProviderResult { + StorageProof::overlay_storage_proof(self.tx, address, slot, hashed_storage) + .map_err(Into::::into) + } } impl StateProofProvider for LatestStateProviderRef<'_, TX> { diff --git a/crates/storage/provider/src/providers/state/macros.rs b/crates/storage/provider/src/providers/state/macros.rs index 388b59ab0..b90924354 100644 --- a/crates/storage/provider/src/providers/state/macros.rs +++ b/crates/storage/provider/src/providers/state/macros.rs @@ -48,7 +48,8 @@ macro_rules! delegate_provider_impls { fn state_root_from_nodes_with_updates(&self, input: reth_trie::TrieInput) -> reth_storage_errors::provider::ProviderResult<(alloy_primitives::B256, reth_trie::updates::TrieUpdates)>; } StorageRootProvider $(where [$($generics)*])? { - fn storage_root(&self, address: alloy_primitives::Address, storage: reth_trie::HashedStorage) -> reth_storage_errors::provider::ProviderResult; + fn storage_root(&self, address: alloy_primitives::Address, storage: reth_trie::HashedStorage) -> reth_storage_errors::provider::ProviderResult; + fn storage_proof(&self, address: alloy_primitives::Address, slot: alloy_primitives::B256, storage: reth_trie::HashedStorage) -> reth_storage_errors::provider::ProviderResult; } StateProofProvider $(where [$($generics)*])? { fn proof(&self, input: reth_trie::TrieInput, address: alloy_primitives::Address, slots: &[alloy_primitives::B256]) -> reth_storage_errors::provider::ProviderResult; diff --git a/crates/storage/provider/src/test_utils/mock.rs b/crates/storage/provider/src/test_utils/mock.rs index 4f2faad8a..3325d3ae9 100644 --- a/crates/storage/provider/src/test_utils/mock.rs +++ b/crates/storage/provider/src/test_utils/mock.rs @@ -31,7 +31,8 @@ use reth_storage_api::{ }; use reth_storage_errors::provider::{ConsistentViewError, ProviderError, ProviderResult}; use reth_trie::{ - updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof, TrieInput, + updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof, StorageProof, + TrieInput, }; use revm::primitives::{BlockEnv, CfgEnvWithHandlerCfg}; use std::{ @@ -639,6 +640,15 @@ impl StorageRootProvider for MockEthProvider { ) -> ProviderResult { Ok(EMPTY_ROOT_HASH) } + + fn storage_proof( + &self, + _address: Address, + slot: B256, + _hashed_storage: HashedStorage, + ) -> ProviderResult { + Ok(StorageProof::new(slot)) + } } impl StateProofProvider for MockEthProvider { diff --git a/crates/storage/provider/src/test_utils/noop.rs b/crates/storage/provider/src/test_utils/noop.rs index e8b7760b8..0a205389c 100644 --- a/crates/storage/provider/src/test_utils/noop.rs +++ b/crates/storage/provider/src/test_utils/noop.rs @@ -356,6 +356,15 @@ impl StorageRootProvider for NoopProvider { ) -> ProviderResult { Ok(B256::default()) } + + fn storage_proof( + &self, + _address: Address, + slot: B256, + _hashed_storage: HashedStorage, + ) -> ProviderResult { + Ok(reth_trie::StorageProof::new(slot)) + } } impl StateProofProvider for NoopProvider { diff --git a/crates/storage/storage-api/src/trie.rs b/crates/storage/storage-api/src/trie.rs index d989def8b..f7d41066d 100644 --- a/crates/storage/storage-api/src/trie.rs +++ b/crates/storage/storage-api/src/trie.rs @@ -4,7 +4,8 @@ use alloy_primitives::{ }; use reth_storage_errors::provider::ProviderResult; use reth_trie::{ - updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof, TrieInput, + updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof, StorageProof, + TrieInput, }; /// A type that can compute the state root of a given post state. @@ -46,6 +47,15 @@ pub trait StorageRootProvider: Send + Sync { /// state. fn storage_root(&self, address: Address, hashed_storage: HashedStorage) -> ProviderResult; + + /// Returns the storage proof of the `HashedStorage` for target slot on top of the current + /// state. + fn storage_proof( + &self, + address: Address, + slot: B256, + hashed_storage: HashedStorage, + ) -> ProviderResult; } /// A type that can generate state proof on top of a given post state. diff --git a/crates/trie/db/src/lib.rs b/crates/trie/db/src/lib.rs index aceea1da8..3a9b1e328 100644 --- a/crates/trie/db/src/lib.rs +++ b/crates/trie/db/src/lib.rs @@ -12,7 +12,7 @@ pub use hashed_cursor::{ DatabaseHashedAccountCursor, DatabaseHashedCursorFactory, DatabaseHashedStorageCursor, }; pub use prefix_set::PrefixSetLoader; -pub use proof::DatabaseProof; +pub use proof::{DatabaseProof, DatabaseStorageProof}; pub use state::{DatabaseHashedPostState, DatabaseStateRoot}; pub use storage::{DatabaseHashedStorage, DatabaseStorageRoot}; pub use trie_cursor::{ diff --git a/crates/trie/db/src/proof.rs b/crates/trie/db/src/proof.rs index 1d5fda84c..9bf08fe13 100644 --- a/crates/trie/db/src/proof.rs +++ b/crates/trie/db/src/proof.rs @@ -1,13 +1,16 @@ use crate::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory}; use alloy_primitives::{ + keccak256, map::{HashMap, HashSet}, Address, B256, }; use reth_db_api::transaction::DbTx; use reth_execution_errors::StateProofError; use reth_trie::{ - hashed_cursor::HashedPostStateCursorFactory, proof::Proof, - trie_cursor::InMemoryTrieCursorFactory, MultiProof, TrieInput, + hashed_cursor::HashedPostStateCursorFactory, + proof::{Proof, StorageProof}, + trie_cursor::InMemoryTrieCursorFactory, + HashedPostStateSorted, HashedStorage, MultiProof, TrieInput, }; use reth_trie_common::AccountProof; @@ -81,3 +84,46 @@ impl<'a, TX: DbTx> DatabaseProof<'a, TX> .multiproof(targets) } } + +/// Extends [`StorageProof`] with operations specific for working with a database transaction. +pub trait DatabaseStorageProof<'a, TX> { + /// Create a new [`StorageProof`] from database transaction and account address. + fn from_tx(tx: &'a TX, address: Address) -> Self; + + /// Generates the storage proof for target slot based on [`TrieInput`]. + fn overlay_storage_proof( + tx: &'a TX, + address: Address, + slot: B256, + storage: HashedStorage, + ) -> Result; +} + +impl<'a, TX: DbTx> DatabaseStorageProof<'a, TX> + for StorageProof, DatabaseHashedCursorFactory<'a, TX>> +{ + fn from_tx(tx: &'a TX, address: Address) -> Self { + Self::new(DatabaseTrieCursorFactory::new(tx), DatabaseHashedCursorFactory::new(tx), address) + } + + fn overlay_storage_proof( + tx: &'a TX, + address: Address, + slot: B256, + storage: HashedStorage, + ) -> Result { + let hashed_address = keccak256(address); + let prefix_set = storage.construct_prefix_set(); + let state_sorted = HashedPostStateSorted::new( + Default::default(), + HashMap::from([(hashed_address, storage.into_sorted())]), + ); + Self::from_tx(tx, address) + .with_hashed_cursor_factory(HashedPostStateCursorFactory::new( + DatabaseHashedCursorFactory::new(tx), + &state_sorted, + )) + .with_prefix_set_mut(prefix_set) + .storage_proof(slot) + } +} diff --git a/crates/trie/trie/src/proof.rs b/crates/trie/trie/src/proof.rs index 95d950521..bff681a34 100644 --- a/crates/trie/trie/src/proof.rs +++ b/crates/trie/trie/src/proof.rs @@ -124,7 +124,7 @@ where hashed_address, ) .with_prefix_set_mut(storage_prefix_set) - .storage_proof(proof_targets)?; + .storage_multiproof(proof_targets)?; // Encode account account_rlp.clear(); @@ -170,6 +170,26 @@ impl StorageProof { } } + /// Set the trie cursor factory. + pub fn with_trie_cursor_factory(self, trie_cursor_factory: TF) -> StorageProof { + StorageProof { + trie_cursor_factory, + hashed_cursor_factory: self.hashed_cursor_factory, + hashed_address: self.hashed_address, + prefix_set: self.prefix_set, + } + } + + /// Set the hashed cursor factory. + pub fn with_hashed_cursor_factory(self, hashed_cursor_factory: HF) -> StorageProof { + StorageProof { + trie_cursor_factory: self.trie_cursor_factory, + hashed_cursor_factory, + hashed_address: self.hashed_address, + prefix_set: self.prefix_set, + } + } + /// Set the changed prefixes. pub fn with_prefix_set_mut(mut self, prefix_set: PrefixSetMut) -> Self { self.prefix_set = prefix_set; @@ -182,8 +202,17 @@ where T: TrieCursorFactory, H: HashedCursorFactory, { - /// Generate storage proof. + /// Generate an account proof from intermediate nodes. pub fn storage_proof( + self, + slot: B256, + ) -> Result { + let targets = HashSet::from_iter([keccak256(slot)]); + Ok(self.storage_multiproof(targets)?.storage_proof(slot)?) + } + + /// Generate storage proof. + pub fn storage_multiproof( mut self, targets: HashSet, ) -> Result { diff --git a/crates/trie/trie/src/witness.rs b/crates/trie/trie/src/witness.rs index b0fcfb021..d668946e6 100644 --- a/crates/trie/trie/src/witness.rs +++ b/crates/trie/trie/src/witness.rs @@ -178,7 +178,7 @@ where hashed_address, ) .with_prefix_set_mut(storage_prefix_set) - .storage_proof(HashSet::from_iter([target_key]))?; + .storage_multiproof(HashSet::from_iter([target_key]))?; // The subtree only contains the proof for a single target. let node =