perf(tree): re-use intermediate nodes (#9836)

This commit is contained in:
Roman Krasiuk
2024-08-13 12:57:22 -07:00
committed by GitHub
parent 8a802dab60
commit ac3d62ba02
16 changed files with 329 additions and 53 deletions

View File

@ -288,7 +288,6 @@ impl Command {
let (state_root, trie_updates) = StateRoot::overlay_root_with_updates( let (state_root, trie_updates) = StateRoot::overlay_root_with_updates(
provider_factory.provider()?.tx_ref(), provider_factory.provider()?.tx_ref(),
hashed_post_state.clone(), hashed_post_state.clone(),
Default::default(),
)?; )?;
if state_root != block_with_senders.state_root { if state_root != block_with_senders.state_root {

View File

@ -154,7 +154,6 @@ impl Command {
let (in_memory_state_root, in_memory_updates) = StateRoot::overlay_root_with_updates( let (in_memory_state_root, in_memory_updates) = StateRoot::overlay_root_with_updates(
provider.tx_ref(), provider.tx_ref(),
execution_outcome.hash_state_slow(), execution_outcome.hash_state_slow(),
Default::default(),
)?; )?;
if in_memory_state_root == block.state_root { if in_memory_state_root == block.state_root {

View File

@ -802,7 +802,7 @@ mod tests {
use reth_storage_api::{ use reth_storage_api::{
AccountReader, BlockHashReader, StateProofProvider, StateProvider, StateRootProvider, AccountReader, BlockHashReader, StateProofProvider, StateProvider, StateRootProvider,
}; };
use reth_trie::{AccountProof, HashedStorage}; use reth_trie::{prefix_set::TriePrefixSetsMut, AccountProof, HashedStorage};
fn create_mock_state( fn create_mock_state(
test_block_builder: &mut TestBlockBuilder, test_block_builder: &mut TestBlockBuilder,
@ -876,6 +876,15 @@ mod tests {
Ok(B256::random()) Ok(B256::random())
} }
fn hashed_state_root_from_nodes(
&self,
_nodes: TrieUpdates,
_post_state: HashedPostState,
_prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<B256> {
Ok(B256::random())
}
fn hashed_state_root_with_updates( fn hashed_state_root_with_updates(
&self, &self,
_hashed_state: HashedPostState, _hashed_state: HashedPostState,
@ -883,6 +892,15 @@ mod tests {
Ok((B256::random(), TrieUpdates::default())) Ok((B256::random(), TrieUpdates::default()))
} }
fn hashed_state_root_from_nodes_with_updates(
&self,
_nodes: TrieUpdates,
_post_state: HashedPostState,
_prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<(B256, TrieUpdates)> {
Ok((B256::random(), TrieUpdates::default()))
}
fn hashed_storage_root( fn hashed_storage_root(
&self, &self,
_address: Address, _address: Address,

View File

@ -1,5 +1,3 @@
use std::collections::HashMap;
use super::ExecutedBlock; use super::ExecutedBlock;
use reth_errors::ProviderResult; use reth_errors::ProviderResult;
use reth_primitives::{ use reth_primitives::{
@ -9,7 +7,11 @@ use reth_storage_api::{
AccountReader, BlockHashReader, StateProofProvider, StateProvider, StateProviderBox, AccountReader, BlockHashReader, StateProofProvider, StateProvider, StateProviderBox,
StateRootProvider, StateRootProvider,
}; };
use reth_trie::{updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage}; use reth_trie::{
prefix_set::TriePrefixSetsMut, updates::TrieUpdates, AccountProof, HashedPostState,
HashedStorage,
};
use std::collections::HashMap;
/// A state provider that stores references to in-memory blocks along with their state as well as /// A state provider that stores references to in-memory blocks along with their state as well as
/// the historical state provider for fallback lookups. /// the historical state provider for fallback lookups.
@ -19,6 +21,8 @@ pub struct MemoryOverlayStateProvider {
pub(crate) in_memory: Vec<ExecutedBlock>, pub(crate) in_memory: Vec<ExecutedBlock>,
/// The collection of hashed state from in-memory blocks. /// The collection of hashed state from in-memory blocks.
pub(crate) hashed_post_state: HashedPostState, pub(crate) hashed_post_state: HashedPostState,
/// The collection of aggregated in-memory trie updates.
pub(crate) trie_updates: TrieUpdates,
/// Historical state provider for state lookups that are not found in in-memory blocks. /// Historical state provider for state lookups that are not found in in-memory blocks.
pub(crate) historical: Box<dyn StateProvider>, pub(crate) historical: Box<dyn StateProvider>,
} }
@ -33,10 +37,12 @@ impl MemoryOverlayStateProvider {
/// database. /// database.
pub fn new(in_memory: Vec<ExecutedBlock>, historical: Box<dyn StateProvider>) -> Self { pub fn new(in_memory: Vec<ExecutedBlock>, historical: Box<dyn StateProvider>) -> Self {
let mut hashed_post_state = HashedPostState::default(); let mut hashed_post_state = HashedPostState::default();
let mut trie_updates = TrieUpdates::default();
for block in in_memory.iter().rev() { for block in in_memory.iter().rev() {
hashed_post_state.extend(block.hashed_state.as_ref().clone()); hashed_post_state.extend(block.hashed_state.as_ref().clone());
trie_updates.extend(block.trie.as_ref().clone());
} }
Self { in_memory, hashed_post_state, historical } Self { in_memory, hashed_post_state, trie_updates, historical }
} }
/// Turn this state provider into a [`StateProviderBox`] /// Turn this state provider into a [`StateProviderBox`]
@ -91,21 +97,47 @@ impl AccountReader for MemoryOverlayStateProvider {
} }
impl StateRootProvider for MemoryOverlayStateProvider { impl StateRootProvider for MemoryOverlayStateProvider {
// TODO: Currently this does not reuse available in-memory trie nodes.
fn hashed_state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> { fn hashed_state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> {
let mut state = self.hashed_post_state.clone(); let prefix_sets = hashed_state.construct_prefix_sets();
state.extend(hashed_state); self.hashed_state_root_from_nodes(TrieUpdates::default(), hashed_state, prefix_sets)
self.historical.hashed_state_root(state) }
fn hashed_state_root_from_nodes(
&self,
nodes: TrieUpdates,
hashed_state: HashedPostState,
prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<B256> {
let mut trie_nodes = self.trie_updates.clone();
trie_nodes.extend(nodes);
let mut state = self.hashed_post_state.clone();
state.extend(hashed_state);
self.historical.hashed_state_root_from_nodes(trie_nodes, state, prefix_sets)
} }
// TODO: Currently this does not reuse available in-memory trie nodes.
fn hashed_state_root_with_updates( fn hashed_state_root_with_updates(
&self, &self,
hashed_state: HashedPostState, hashed_state: HashedPostState,
) -> ProviderResult<(B256, TrieUpdates)> { ) -> ProviderResult<(B256, TrieUpdates)> {
let prefix_sets = hashed_state.construct_prefix_sets();
self.hashed_state_root_from_nodes_with_updates(
TrieUpdates::default(),
hashed_state,
prefix_sets,
)
}
fn hashed_state_root_from_nodes_with_updates(
&self,
nodes: TrieUpdates,
hashed_state: HashedPostState,
prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<(B256, TrieUpdates)> {
let mut trie_nodes = self.trie_updates.clone();
trie_nodes.extend(nodes);
let mut state = self.hashed_post_state.clone(); let mut state = self.hashed_post_state.clone();
state.extend(hashed_state); state.extend(hashed_state);
self.historical.hashed_state_root_with_updates(state) self.historical.hashed_state_root_from_nodes_with_updates(trie_nodes, state, prefix_sets)
} }
// TODO: Currently this does not reuse available in-memory trie nodes. // TODO: Currently this does not reuse available in-memory trie nodes.
@ -119,7 +151,6 @@ impl StateRootProvider for MemoryOverlayStateProvider {
} }
impl StateProofProvider for MemoryOverlayStateProvider { impl StateProofProvider for MemoryOverlayStateProvider {
// TODO: Currently this does not reuse available in-memory trie nodes.
fn hashed_proof( fn hashed_proof(
&self, &self,
hashed_state: HashedPostState, hashed_state: HashedPostState,

View File

@ -6,7 +6,10 @@ use reth_storage_api::{
AccountReader, BlockHashReader, StateProofProvider, StateProvider, StateRootProvider, AccountReader, BlockHashReader, StateProofProvider, StateProvider, StateRootProvider,
}; };
use reth_storage_errors::provider::ProviderResult; use reth_storage_errors::provider::ProviderResult;
use reth_trie::{updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage}; use reth_trie::{
prefix_set::TriePrefixSetsMut, updates::TrieUpdates, AccountProof, HashedPostState,
HashedStorage,
};
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use alloc::vec::Vec; use alloc::vec::Vec;
@ -72,6 +75,15 @@ impl StateRootProvider for StateProviderTest {
unimplemented!("state root computation is not supported") unimplemented!("state root computation is not supported")
} }
fn hashed_state_root_from_nodes(
&self,
_nodes: TrieUpdates,
_hashed_state: HashedPostState,
_prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<B256> {
unimplemented!("state root computation is not supported")
}
fn hashed_state_root_with_updates( fn hashed_state_root_with_updates(
&self, &self,
_hashed_state: HashedPostState, _hashed_state: HashedPostState,
@ -79,6 +91,15 @@ impl StateRootProvider for StateProviderTest {
unimplemented!("state root computation is not supported") unimplemented!("state root computation is not supported")
} }
fn hashed_state_root_from_nodes_with_updates(
&self,
_nodes: TrieUpdates,
_hashed_state: HashedPostState,
_prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<(B256, TrieUpdates)> {
unimplemented!("state root computation is not supported")
}
fn hashed_storage_root( fn hashed_storage_root(
&self, &self,
_address: Address, _address: Address,

View File

@ -25,6 +25,15 @@ impl<'a> reth_storage_api::StateRootProvider for StateProviderTraitObjWrapper<'a
self.0.hashed_state_root(hashed_state) self.0.hashed_state_root(hashed_state)
} }
fn hashed_state_root_from_nodes(
&self,
nodes: reth_trie::updates::TrieUpdates,
hashed_state: reth_trie::HashedPostState,
prefix_sets: reth_trie::prefix_set::TriePrefixSetsMut,
) -> reth_errors::ProviderResult<B256> {
self.0.hashed_state_root_from_nodes(nodes, hashed_state, prefix_sets)
}
fn hashed_state_root_with_updates( fn hashed_state_root_with_updates(
&self, &self,
hashed_state: reth_trie::HashedPostState, hashed_state: reth_trie::HashedPostState,
@ -32,6 +41,15 @@ impl<'a> reth_storage_api::StateRootProvider for StateProviderTraitObjWrapper<'a
self.0.hashed_state_root_with_updates(hashed_state) self.0.hashed_state_root_with_updates(hashed_state)
} }
fn hashed_state_root_from_nodes_with_updates(
&self,
nodes: reth_trie::updates::TrieUpdates,
hashed_state: reth_trie::HashedPostState,
prefix_sets: reth_trie::prefix_set::TriePrefixSetsMut,
) -> reth_errors::ProviderResult<(B256, reth_trie::updates::TrieUpdates)> {
self.0.hashed_state_root_from_nodes_with_updates(nodes, hashed_state, prefix_sets)
}
fn hashed_storage_root( fn hashed_storage_root(
&self, &self,
address: Address, address: Address,

View File

@ -1,13 +1,15 @@
use std::collections::HashMap;
use crate::{ use crate::{
AccountReader, BlockHashReader, ExecutionDataProvider, StateProvider, StateRootProvider, AccountReader, BlockHashReader, ExecutionDataProvider, StateProvider, StateRootProvider,
}; };
use reth_primitives::{Account, Address, BlockNumber, Bytecode, Bytes, B256}; use reth_primitives::{Account, Address, BlockNumber, Bytecode, Bytes, B256};
use reth_storage_api::StateProofProvider; use reth_storage_api::StateProofProvider;
use reth_storage_errors::provider::ProviderResult; use reth_storage_errors::provider::ProviderResult;
use reth_trie::{updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage}; use reth_trie::{
prefix_set::TriePrefixSetsMut, updates::TrieUpdates, AccountProof, HashedPostState,
HashedStorage,
};
use revm::db::BundleState; use revm::db::BundleState;
use std::collections::HashMap;
/// A state provider that resolves to data from either a wrapped [`crate::ExecutionOutcome`] /// A state provider that resolves to data from either a wrapped [`crate::ExecutionOutcome`]
/// or an underlying state provider. /// or an underlying state provider.
@ -80,6 +82,15 @@ impl<SP: StateProvider, EDP: ExecutionDataProvider> StateRootProvider
self.state_provider.hashed_state_root(state) self.state_provider.hashed_state_root(state)
} }
fn hashed_state_root_from_nodes(
&self,
_nodes: TrieUpdates,
_hashed_state: HashedPostState,
_prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<B256> {
unimplemented!()
}
fn state_root_with_updates( fn state_root_with_updates(
&self, &self,
bundle_state: &BundleState, bundle_state: &BundleState,
@ -99,6 +110,24 @@ impl<SP: StateProvider, EDP: ExecutionDataProvider> StateRootProvider
self.state_provider.hashed_state_root_with_updates(state) self.state_provider.hashed_state_root_with_updates(state)
} }
fn hashed_state_root_from_nodes_with_updates(
&self,
nodes: TrieUpdates,
hashed_state: HashedPostState,
prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<(B256, TrieUpdates)> {
let bundle_state = self.block_execution_data_provider.execution_outcome().state();
let mut state = HashedPostState::from_bundle_state(&bundle_state.state);
let mut state_prefix_sets = state.construct_prefix_sets();
state.extend(hashed_state);
state_prefix_sets.extend(prefix_sets);
self.state_provider.hashed_state_root_from_nodes_with_updates(
nodes,
state,
state_prefix_sets,
)
}
fn hashed_storage_root( fn hashed_storage_root(
&self, &self,
address: Address, address: Address,

View File

@ -16,8 +16,8 @@ use reth_primitives::{
use reth_storage_api::StateProofProvider; use reth_storage_api::StateProofProvider;
use reth_storage_errors::provider::ProviderResult; use reth_storage_errors::provider::ProviderResult;
use reth_trie::{ use reth_trie::{
proof::Proof, updates::TrieUpdates, witness::TrieWitness, AccountProof, HashedPostState, prefix_set::TriePrefixSetsMut, proof::Proof, updates::TrieUpdates, witness::TrieWitness,
HashedStorage, StateRoot, StorageRoot, AccountProof, HashedPostState, HashedStorage, StateRoot, StorageRoot,
}; };
use reth_trie_db::{ use reth_trie_db::{
DatabaseHashedPostState, DatabaseProof, DatabaseStateRoot, DatabaseStorageRoot, DatabaseHashedPostState, DatabaseProof, DatabaseStateRoot, DatabaseStorageRoot,
@ -266,7 +266,21 @@ impl<'b, TX: DbTx> StateRootProvider for HistoricalStateProviderRef<'b, TX> {
fn hashed_state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> { fn hashed_state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> {
let mut revert_state = self.revert_state()?; let mut revert_state = self.revert_state()?;
revert_state.extend(hashed_state); revert_state.extend(hashed_state);
StateRoot::overlay_root(self.tx, revert_state, Default::default()) StateRoot::overlay_root(self.tx, revert_state)
.map_err(|err| ProviderError::Database(err.into()))
}
fn hashed_state_root_from_nodes(
&self,
nodes: TrieUpdates,
hashed_state: HashedPostState,
prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<B256> {
let mut revert_state = self.revert_state()?;
let mut revert_prefix_sets = revert_state.construct_prefix_sets();
revert_state.extend(hashed_state);
revert_prefix_sets.extend(prefix_sets);
StateRoot::overlay_root_from_nodes(self.tx, nodes, revert_state, revert_prefix_sets)
.map_err(|err| ProviderError::Database(err.into())) .map_err(|err| ProviderError::Database(err.into()))
} }
@ -276,10 +290,29 @@ impl<'b, TX: DbTx> StateRootProvider for HistoricalStateProviderRef<'b, TX> {
) -> ProviderResult<(B256, TrieUpdates)> { ) -> ProviderResult<(B256, TrieUpdates)> {
let mut revert_state = self.revert_state()?; let mut revert_state = self.revert_state()?;
revert_state.extend(hashed_state); revert_state.extend(hashed_state);
StateRoot::overlay_root_with_updates(self.tx, revert_state, Default::default()) StateRoot::overlay_root_with_updates(self.tx, revert_state)
.map_err(|err| ProviderError::Database(err.into())) .map_err(|err| ProviderError::Database(err.into()))
} }
fn hashed_state_root_from_nodes_with_updates(
&self,
nodes: TrieUpdates,
hashed_state: HashedPostState,
prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<(B256, TrieUpdates)> {
let mut revert_state = self.revert_state()?;
let mut revert_prefix_sets = revert_state.construct_prefix_sets();
revert_state.extend(hashed_state);
revert_prefix_sets.extend(prefix_sets);
StateRoot::overlay_root_from_nodes_with_updates(
self.tx,
nodes,
revert_state,
revert_prefix_sets,
)
.map_err(|err| ProviderError::Database(err.into()))
}
fn hashed_storage_root( fn hashed_storage_root(
&self, &self,
address: Address, address: Address,

View File

@ -16,8 +16,8 @@ use reth_primitives::{
use reth_storage_api::StateProofProvider; use reth_storage_api::StateProofProvider;
use reth_storage_errors::provider::{ProviderError, ProviderResult}; use reth_storage_errors::provider::{ProviderError, ProviderResult};
use reth_trie::{ use reth_trie::{
proof::Proof, updates::TrieUpdates, witness::TrieWitness, AccountProof, HashedPostState, prefix_set::TriePrefixSetsMut, proof::Proof, updates::TrieUpdates, witness::TrieWitness,
HashedStorage, StateRoot, StorageRoot, AccountProof, HashedPostState, HashedStorage, StateRoot, StorageRoot,
}; };
use reth_trie_db::{DatabaseProof, DatabaseStateRoot, DatabaseStorageRoot, DatabaseTrieWitness}; use reth_trie_db::{DatabaseProof, DatabaseStateRoot, DatabaseStorageRoot, DatabaseTrieWitness};
@ -82,7 +82,17 @@ impl<'b, TX: DbTx> BlockHashReader for LatestStateProviderRef<'b, TX> {
impl<'b, TX: DbTx> StateRootProvider for LatestStateProviderRef<'b, TX> { impl<'b, TX: DbTx> StateRootProvider for LatestStateProviderRef<'b, TX> {
fn hashed_state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> { fn hashed_state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> {
StateRoot::overlay_root(self.tx, hashed_state, Default::default()) StateRoot::overlay_root(self.tx, hashed_state)
.map_err(|err| ProviderError::Database(err.into()))
}
fn hashed_state_root_from_nodes(
&self,
nodes: TrieUpdates,
hashed_state: HashedPostState,
prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<B256> {
StateRoot::overlay_root_from_nodes(self.tx, nodes, hashed_state, prefix_sets)
.map_err(|err| ProviderError::Database(err.into())) .map_err(|err| ProviderError::Database(err.into()))
} }
@ -90,7 +100,17 @@ impl<'b, TX: DbTx> StateRootProvider for LatestStateProviderRef<'b, TX> {
&self, &self,
hashed_state: HashedPostState, hashed_state: HashedPostState,
) -> ProviderResult<(B256, TrieUpdates)> { ) -> ProviderResult<(B256, TrieUpdates)> {
StateRoot::overlay_root_with_updates(self.tx, hashed_state, Default::default()) StateRoot::overlay_root_with_updates(self.tx, hashed_state)
.map_err(|err| ProviderError::Database(err.into()))
}
fn hashed_state_root_from_nodes_with_updates(
&self,
nodes: TrieUpdates,
hashed_state: HashedPostState,
prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<(B256, TrieUpdates)> {
StateRoot::overlay_root_from_nodes_with_updates(self.tx, nodes, hashed_state, prefix_sets)
.map_err(|err| ProviderError::Database(err.into())) .map_err(|err| ProviderError::Database(err.into()))
} }

View File

@ -44,8 +44,10 @@ macro_rules! delegate_provider_impls {
StateRootProvider $(where [$($generics)*])? { StateRootProvider $(where [$($generics)*])? {
fn state_root(&self, state: &revm::db::BundleState) -> reth_storage_errors::provider::ProviderResult<reth_primitives::B256>; fn state_root(&self, state: &revm::db::BundleState) -> reth_storage_errors::provider::ProviderResult<reth_primitives::B256>;
fn hashed_state_root(&self, state: reth_trie::HashedPostState) -> reth_storage_errors::provider::ProviderResult<reth_primitives::B256>; fn hashed_state_root(&self, state: reth_trie::HashedPostState) -> reth_storage_errors::provider::ProviderResult<reth_primitives::B256>;
fn hashed_state_root_from_nodes(&self, nodes: reth_trie::updates::TrieUpdates, state: reth_trie::HashedPostState, prefix_sets: reth_trie::prefix_set::TriePrefixSetsMut) -> reth_storage_errors::provider::ProviderResult<reth_primitives::B256>;
fn state_root_with_updates(&self, state: &revm::db::BundleState) -> reth_storage_errors::provider::ProviderResult<(reth_primitives::B256, reth_trie::updates::TrieUpdates)>; fn state_root_with_updates(&self, state: &revm::db::BundleState) -> reth_storage_errors::provider::ProviderResult<(reth_primitives::B256, reth_trie::updates::TrieUpdates)>;
fn hashed_state_root_with_updates(&self, state: reth_trie::HashedPostState) -> reth_storage_errors::provider::ProviderResult<(reth_primitives::B256, reth_trie::updates::TrieUpdates)>; fn hashed_state_root_with_updates(&self, state: reth_trie::HashedPostState) -> reth_storage_errors::provider::ProviderResult<(reth_primitives::B256, reth_trie::updates::TrieUpdates)>;
fn hashed_state_root_from_nodes_with_updates(&self, nodes: reth_trie::updates::TrieUpdates, state: reth_trie::HashedPostState, prefix_sets: reth_trie::prefix_set::TriePrefixSetsMut) -> reth_storage_errors::provider::ProviderResult<(reth_primitives::B256, reth_trie::updates::TrieUpdates)>;
fn hashed_storage_root(&self, address: reth_primitives::Address, storage: reth_trie::HashedStorage) -> reth_storage_errors::provider::ProviderResult<reth_primitives::B256>; fn hashed_storage_root(&self, address: reth_primitives::Address, storage: reth_trie::HashedStorage) -> reth_storage_errors::provider::ProviderResult<reth_primitives::B256>;
} }
StateProofProvider $(where [$($generics)*])? { StateProofProvider $(where [$($generics)*])? {

View File

@ -19,7 +19,10 @@ use reth_primitives::{
use reth_stages_types::{StageCheckpoint, StageId}; use reth_stages_types::{StageCheckpoint, StageId};
use reth_storage_api::{StageCheckpointReader, StateProofProvider}; use reth_storage_api::{StageCheckpointReader, StateProofProvider};
use reth_storage_errors::provider::{ProviderError, ProviderResult}; use reth_storage_errors::provider::{ProviderError, ProviderResult};
use reth_trie::{updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage}; use reth_trie::{
prefix_set::TriePrefixSetsMut, updates::TrieUpdates, AccountProof, HashedPostState,
HashedStorage,
};
use revm::primitives::{BlockEnv, CfgEnvWithHandlerCfg}; use revm::primitives::{BlockEnv, CfgEnvWithHandlerCfg};
use std::{ use std::{
collections::{BTreeMap, HashMap}, collections::{BTreeMap, HashMap},
@ -564,8 +567,16 @@ impl StageCheckpointReader for MockEthProvider {
impl StateRootProvider for MockEthProvider { impl StateRootProvider for MockEthProvider {
fn hashed_state_root(&self, _state: HashedPostState) -> ProviderResult<B256> { fn hashed_state_root(&self, _state: HashedPostState) -> ProviderResult<B256> {
let state_root = self.state_roots.lock().pop().unwrap_or_default(); Ok(self.state_roots.lock().pop().unwrap_or_default())
Ok(state_root) }
fn hashed_state_root_from_nodes(
&self,
_nodes: TrieUpdates,
_hashed_state: HashedPostState,
_prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<B256> {
Ok(self.state_roots.lock().pop().unwrap_or_default())
} }
fn hashed_state_root_with_updates( fn hashed_state_root_with_updates(
@ -583,6 +594,16 @@ impl StateRootProvider for MockEthProvider {
) -> ProviderResult<B256> { ) -> ProviderResult<B256> {
Ok(B256::default()) Ok(B256::default())
} }
fn hashed_state_root_from_nodes_with_updates(
&self,
_nodes: TrieUpdates,
_hashed_state: HashedPostState,
_prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<(B256, TrieUpdates)> {
let state_root = self.state_roots.lock().pop().unwrap_or_default();
Ok((state_root, Default::default()))
}
} }
impl StateProofProvider for MockEthProvider { impl StateProofProvider for MockEthProvider {

View File

@ -22,7 +22,9 @@ use reth_prune_types::{PruneCheckpoint, PruneSegment};
use reth_stages_types::{StageCheckpoint, StageId}; use reth_stages_types::{StageCheckpoint, StageId};
use reth_storage_api::StateProofProvider; use reth_storage_api::StateProofProvider;
use reth_storage_errors::provider::ProviderResult; use reth_storage_errors::provider::ProviderResult;
use reth_trie::{updates::TrieUpdates, AccountProof, HashedPostState}; use reth_trie::{
prefix_set::TriePrefixSetsMut, updates::TrieUpdates, AccountProof, HashedPostState,
};
use revm::primitives::{BlockEnv, CfgEnvWithHandlerCfg}; use revm::primitives::{BlockEnv, CfgEnvWithHandlerCfg};
use tokio::sync::{broadcast, watch}; use tokio::sync::{broadcast, watch};
@ -321,6 +323,15 @@ impl StateRootProvider for NoopProvider {
Ok(B256::default()) Ok(B256::default())
} }
fn hashed_state_root_from_nodes(
&self,
_nodes: TrieUpdates,
_hashed_state: HashedPostState,
_prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<B256> {
Ok(B256::default())
}
fn hashed_state_root_with_updates( fn hashed_state_root_with_updates(
&self, &self,
_state: HashedPostState, _state: HashedPostState,
@ -328,6 +339,15 @@ impl StateRootProvider for NoopProvider {
Ok((B256::default(), TrieUpdates::default())) Ok((B256::default(), TrieUpdates::default()))
} }
fn hashed_state_root_from_nodes_with_updates(
&self,
_nodes: TrieUpdates,
_hashed_state: HashedPostState,
_prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<(B256, TrieUpdates)> {
Ok((B256::default(), TrieUpdates::default()))
}
fn hashed_storage_root( fn hashed_storage_root(
&self, &self,
_address: Address, _address: Address,

View File

@ -1413,7 +1413,6 @@ mod tests {
Vec::new() Vec::new()
) )
.hash_state_slow(), .hash_state_slow(),
Default::default()
) )
.unwrap(), .unwrap(),
state_root(expected.clone().into_iter().map(|(address, (account, storage))| ( state_root(expected.clone().into_iter().map(|(address, (account, storage))| (

View File

@ -1,6 +1,9 @@
use reth_primitives::{Address, Bytes, B256}; use reth_primitives::{Address, Bytes, B256};
use reth_storage_errors::provider::ProviderResult; use reth_storage_errors::provider::ProviderResult;
use reth_trie::{updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage}; use reth_trie::{
prefix_set::TriePrefixSetsMut, updates::TrieUpdates, AccountProof, HashedPostState,
HashedStorage,
};
use revm::db::BundleState; use revm::db::BundleState;
use std::collections::HashMap; use std::collections::HashMap;
@ -21,6 +24,16 @@ pub trait StateRootProvider: Send + Sync {
/// Returns the state root of the `HashedPostState` on top of the current state. /// Returns the state root of the `HashedPostState` on top of the current state.
fn hashed_state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256>; fn hashed_state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256>;
/// Returns the state root of the `HashedPostState` on top of the current state but re-uses the
/// intermediate nodes to speed up the computation. It's up to the caller to construct the
/// prefix sets and inform the provider of the trie paths that have changes.
fn hashed_state_root_from_nodes(
&self,
nodes: TrieUpdates,
hashed_state: HashedPostState,
prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<B256>;
/// Returns the state root of the BundleState on top of the current state with trie /// Returns the state root of the BundleState on top of the current state with trie
/// updates to be committed to the database. /// updates to be committed to the database.
fn state_root_with_updates( fn state_root_with_updates(
@ -37,6 +50,15 @@ pub trait StateRootProvider: Send + Sync {
hashed_state: HashedPostState, hashed_state: HashedPostState,
) -> ProviderResult<(B256, TrieUpdates)>; ) -> ProviderResult<(B256, TrieUpdates)>;
/// Returns state root and trie updates.
/// See [`StateRootProvider::hashed_state_root_from_nodes`] for more info.
fn hashed_state_root_from_nodes_with_updates(
&self,
nodes: TrieUpdates,
hashed_state: HashedPostState,
prefix_sets: TriePrefixSetsMut,
) -> ProviderResult<(B256, TrieUpdates)>;
/// Returns the storage root of the `HashedStorage` for target address on top of the current /// Returns the storage root of the `HashedStorage` for target address on top of the current
/// state. /// state.
fn hashed_storage_root( fn hashed_storage_root(

View File

@ -9,8 +9,9 @@ use reth_execution_errors::StateRootError;
use reth_primitives::{keccak256, Account, Address, BlockNumber, B256, U256}; use reth_primitives::{keccak256, Account, Address, BlockNumber, B256, U256};
use reth_storage_errors::db::DatabaseError; use reth_storage_errors::db::DatabaseError;
use reth_trie::{ use reth_trie::{
hashed_cursor::HashedPostStateCursorFactory, trie_cursor::InMemoryTrieCursorFactory, hashed_cursor::HashedPostStateCursorFactory, prefix_set::TriePrefixSetsMut,
updates::TrieUpdates, HashedPostState, HashedStorage, StateRoot, StateRootProgress, trie_cursor::InMemoryTrieCursorFactory, updates::TrieUpdates, HashedPostState, HashedStorage,
StateRoot, StateRootProgress,
}; };
use std::{ use std::{
collections::{hash_map, HashMap}, collections::{hash_map, HashMap},
@ -92,29 +93,38 @@ pub trait DatabaseStateRoot<'a, TX>: Sized {
/// Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }), /// Some(Account { nonce: 1, balance: U256::from(10), bytecode_hash: None }),
/// ); /// );
/// ///
/// // Initialize intermediate nodes if any.
/// let intermediate_nodes = TrieUpdates::default();
///
/// // Calculate the state root /// // Calculate the state root
/// let tx = db.tx().expect("failed to create transaction"); /// let tx = db.tx().expect("failed to create transaction");
/// let state_root = StateRoot::overlay_root(&tx, hashed_state, intermediate_nodes); /// let state_root = StateRoot::overlay_root(&tx, hashed_state);
/// ``` /// ```
/// ///
/// # Returns /// # Returns
/// ///
/// The state root for this [`HashedPostState`]. /// The state root for this [`HashedPostState`].
fn overlay_root( fn overlay_root(tx: &'a TX, post_state: HashedPostState) -> Result<B256, StateRootError>;
tx: &'a TX,
post_state: HashedPostState,
intermediate_nodes: TrieUpdates,
) -> Result<B256, StateRootError>;
/// Calculates the state root for this [`HashedPostState`] and returns it alongside trie /// Calculates the state root for this [`HashedPostState`] and returns it alongside trie
/// updates. See [`Self::overlay_root`] for more info. /// updates. See [`Self::overlay_root`] for more info.
fn overlay_root_with_updates( fn overlay_root_with_updates(
tx: &'a TX, tx: &'a TX,
post_state: HashedPostState, post_state: HashedPostState,
) -> Result<(B256, TrieUpdates), StateRootError>;
/// Calculates the state root for provided [`HashedPostState`] using cached intermediate nodes.
fn overlay_root_from_nodes(
tx: &'a TX,
intermediate_nodes: TrieUpdates, intermediate_nodes: TrieUpdates,
post_state: HashedPostState,
prefix_sets: TriePrefixSetsMut,
) -> Result<B256, StateRootError>;
/// Calculates the state root and trie updates for provided [`HashedPostState`] using
/// cached intermediate nodes.
fn overlay_root_from_nodes_with_updates(
tx: &'a TX,
intermediate_nodes: TrieUpdates,
post_state: HashedPostState,
prefix_sets: TriePrefixSetsMut,
) -> Result<(B256, TrieUpdates), StateRootError>; ) -> Result<(B256, TrieUpdates), StateRootError>;
} }
@ -164,16 +174,11 @@ impl<'a, TX: DbTx> DatabaseStateRoot<'a, TX>
Self::incremental_root_calculator(tx, range)?.root_with_progress() Self::incremental_root_calculator(tx, range)?.root_with_progress()
} }
fn overlay_root( fn overlay_root(tx: &'a TX, post_state: HashedPostState) -> Result<B256, StateRootError> {
tx: &'a TX,
post_state: HashedPostState,
intermediate_nodes: TrieUpdates,
) -> Result<B256, StateRootError> {
let prefix_sets = post_state.construct_prefix_sets().freeze(); let prefix_sets = post_state.construct_prefix_sets().freeze();
let state_sorted = post_state.into_sorted(); let state_sorted = post_state.into_sorted();
let nodes_sorted = intermediate_nodes.into_sorted();
StateRoot::new( StateRoot::new(
InMemoryTrieCursorFactory::new(DatabaseTrieCursorFactory::new(tx), &nodes_sorted), DatabaseTrieCursorFactory::new(tx),
HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted), HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted),
) )
.with_prefix_sets(prefix_sets) .with_prefix_sets(prefix_sets)
@ -183,16 +188,46 @@ impl<'a, TX: DbTx> DatabaseStateRoot<'a, TX>
fn overlay_root_with_updates( fn overlay_root_with_updates(
tx: &'a TX, tx: &'a TX,
post_state: HashedPostState, post_state: HashedPostState,
intermediate_nodes: TrieUpdates,
) -> Result<(B256, TrieUpdates), StateRootError> { ) -> Result<(B256, TrieUpdates), StateRootError> {
let prefix_sets = post_state.construct_prefix_sets().freeze(); let prefix_sets = post_state.construct_prefix_sets().freeze();
let state_sorted = post_state.into_sorted();
StateRoot::new(
DatabaseTrieCursorFactory::new(tx),
HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted),
)
.with_prefix_sets(prefix_sets)
.root_with_updates()
}
fn overlay_root_from_nodes(
tx: &'a TX,
intermediate_nodes: TrieUpdates,
post_state: HashedPostState,
prefix_sets: TriePrefixSetsMut,
) -> Result<B256, StateRootError> {
let state_sorted = post_state.into_sorted(); let state_sorted = post_state.into_sorted();
let nodes_sorted = intermediate_nodes.into_sorted(); let nodes_sorted = intermediate_nodes.into_sorted();
StateRoot::new( StateRoot::new(
InMemoryTrieCursorFactory::new(DatabaseTrieCursorFactory::new(tx), &nodes_sorted), InMemoryTrieCursorFactory::new(DatabaseTrieCursorFactory::new(tx), &nodes_sorted),
HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted), HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted),
) )
.with_prefix_sets(prefix_sets) .with_prefix_sets(prefix_sets.freeze())
.root()
}
fn overlay_root_from_nodes_with_updates(
tx: &'a TX,
intermediate_nodes: TrieUpdates,
post_state: HashedPostState,
prefix_sets: TriePrefixSetsMut,
) -> Result<(B256, TrieUpdates), StateRootError> {
let state_sorted = post_state.into_sorted();
let nodes_sorted = intermediate_nodes.into_sorted();
StateRoot::new(
InMemoryTrieCursorFactory::new(DatabaseTrieCursorFactory::new(tx), &nodes_sorted),
HashedPostStateCursorFactory::new(DatabaseHashedCursorFactory::new(tx), &state_sorted),
)
.with_prefix_sets(prefix_sets.freeze())
.root_with_updates() .root_with_updates()
} }
} }
@ -277,7 +312,7 @@ mod tests {
let db = create_test_rw_db(); let db = create_test_rw_db();
let tx = db.tx().expect("failed to create transaction"); let tx = db.tx().expect("failed to create transaction");
assert_eq!( assert_eq!(
StateRoot::overlay_root(&tx, post_state, Default::default()).unwrap(), StateRoot::overlay_root(&tx, post_state).unwrap(),
hex!("b464525710cafcf5d4044ac85b72c08b1e76231b8d91f288fe438cc41d8eaafd") hex!("b464525710cafcf5d4044ac85b72c08b1e76231b8d91f288fe438cc41d8eaafd")
); );
} }

View File

@ -18,6 +18,15 @@ pub struct TriePrefixSetsMut {
} }
impl TriePrefixSetsMut { impl TriePrefixSetsMut {
/// Extends prefix sets with contents of another prefix set.
pub fn extend(&mut self, other: Self) {
self.account_prefix_set.extend(other.account_prefix_set.keys);
for (hashed_address, prefix_set) in other.storage_prefix_sets {
self.storage_prefix_sets.entry(hashed_address).or_default().extend(prefix_set.keys);
}
self.destroyed_accounts.extend(other.destroyed_accounts);
}
/// Returns a `TriePrefixSets` with the same elements as these sets. /// Returns a `TriePrefixSets` with the same elements as these sets.
/// ///
/// If not yet sorted, the elements will be sorted and deduplicated. /// If not yet sorted, the elements will be sorted and deduplicated.