perf(trie): calculate state root from BundleState (#7186)

This commit is contained in:
Roman Krasiuk
2024-03-19 07:52:19 +01:00
committed by GitHub
parent 6ffd6ef8f3
commit d4e9695556
13 changed files with 64 additions and 65 deletions

View File

@ -200,10 +200,10 @@ impl AppendableChain {
let start = Instant::now();
let (state_root, trie_updates) = if block_attachment.is_canonical() {
provider
.state_root_with_updates(&bundle_state)
.state_root_with_updates(bundle_state.state())
.map(|(root, updates)| (root, Some(updates)))?
} else {
(provider.state_root(&bundle_state)?, None)
(provider.state_root(bundle_state.state())?, None)
};
if block.state_root != state_root {
return Err(ConsensusError::BodyStateRootDiff(

View File

@ -410,7 +410,7 @@ impl StorageInner {
let state_root = client
.latest()
.map_err(|_| BlockExecutionError::ProviderError)?
.state_root(bundle_state)
.state_root(bundle_state.state())
.unwrap();
header.state_root = state_root;
Ok(header)

View File

@ -108,8 +108,7 @@ where
db.merge_transitions(BundleRetention::PlainState);
// calculate the state root
let bundle_state =
BundleStateWithReceipts::new(db.take_bundle(), Receipts::new(), block_number);
let bundle_state = db.take_bundle();
let state_root = state.state_root(&bundle_state).map_err(|err| {
warn!(target: "payload_builder", parent_hash=%parent_block.hash(), %err, "failed to calculate state root for empty payload");
err
@ -349,7 +348,7 @@ where
let logs_bloom = bundle.block_logs_bloom(block_number).expect("Number is in range");
// calculate the state root
let state_root = state_provider.state_root(&bundle)?;
let state_root = state_provider.state_root(bundle.state())?;
// create the block header
let transactions_root = proofs::calculate_transaction_root(&executed_txs);

View File

@ -176,8 +176,7 @@ mod builder {
db.merge_transitions(BundleRetention::PlainState);
// calculate the state root
let bundle_state =
BundleStateWithReceipts::new(db.take_bundle(), Receipts::new(), block_number);
let bundle_state = db.take_bundle();
let state_root = state.state_root(&bundle_state).map_err(|err| {
warn!(target: "payload_builder", parent_hash=%parent_block.hash(), %err, "failed to calculate state root for empty payload");
err
@ -538,7 +537,7 @@ mod builder {
let logs_bloom = bundle.block_logs_bloom(block_number).expect("Number is in range");
// calculate the state root
let state_root = state_provider.state_root(&bundle)?;
let state_root = state_provider.state_root(bundle.state())?;
// create the block header
let transactions_root = proofs::calculate_transaction_root(&executed_txs);

View File

@ -7,6 +7,13 @@ use reth_primitives::{
#[cfg(not(feature = "optimism"))]
use reth_primitives::revm::env::fill_tx_env;
use reth_provider::{AccountReader, BlockHashReader, StateProvider, StateRootProvider};
use reth_trie::updates::TrieUpdates;
use revm::{
db::BundleState,
primitives::{AnalysisKind, CfgEnvWithHandlerCfg, TxEnv},
};
use std::collections::HashMap;
#[cfg(feature = "optimism")]
use {
reth_primitives::revm::env::fill_op_tx_env,
@ -16,13 +23,6 @@ use {
},
};
use reth_provider::{
AccountReader, BlockHashReader, BundleStateWithReceipts, StateProvider, StateRootProvider,
};
use reth_trie::updates::TrieUpdates;
use revm::primitives::{AnalysisKind, CfgEnvWithHandlerCfg, TxEnv};
use std::collections::HashMap;
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct StateProviderTest {
accounts: HashMap<Address, (HashMap<StorageKey, U256>, Account)>,
@ -74,13 +74,13 @@ impl BlockHashReader for StateProviderTest {
}
impl StateRootProvider for StateProviderTest {
fn state_root(&self, _bundle_state: &BundleStateWithReceipts) -> ProviderResult<B256> {
fn state_root(&self, _bundle_state: &BundleState) -> ProviderResult<B256> {
unimplemented!("state root computation is not supported")
}
fn state_root_with_updates(
&self,
_bundle_state: &BundleStateWithReceipts,
_bundle_state: &BundleState,
) -> ProviderResult<(B256, TrieUpdates)> {
unimplemented!("state root computation is not supported")
}

View File

@ -230,7 +230,7 @@ impl PendingBlockEnv {
let logs_bloom = bundle.block_logs_bloom(block_number).expect("Block is present");
// calculate the state root
let state_root = state_provider.state_root(&bundle)?;
let state_root = state_provider.state_root(bundle.state())?;
// create the block header
let transactions_root = proofs::calculate_transaction_root(&executed_txs);

View File

@ -1,10 +1,10 @@
use crate::{
bundle_state::BundleStateWithReceipts, AccountReader, BlockHashReader, BundleStateDataProvider,
StateProvider, StateRootProvider,
AccountReader, BlockHashReader, BundleStateDataProvider, StateProvider, StateRootProvider,
};
use reth_interfaces::provider::{ProviderError, ProviderResult};
use reth_primitives::{trie::AccountProof, Account, Address, BlockNumber, Bytecode, B256};
use reth_trie::updates::TrieUpdates;
use revm::db::BundleState;
/// A state provider that either resolves to data in a wrapped [`crate::BundleStateWithReceipts`],
/// or an underlying state provider.
@ -60,17 +60,17 @@ impl<SP: StateProvider, BSDP: BundleStateDataProvider> AccountReader
impl<SP: StateProvider, BSDP: BundleStateDataProvider> StateRootProvider
for BundleStateProvider<SP, BSDP>
{
fn state_root(&self, bundle_state: &BundleStateWithReceipts) -> ProviderResult<B256> {
let mut state = self.bundle_state_data_provider.state().clone();
fn state_root(&self, bundle_state: &BundleState) -> ProviderResult<B256> {
let mut state = self.bundle_state_data_provider.state().state().clone();
state.extend(bundle_state.clone());
self.state_provider.state_root(&state)
}
fn state_root_with_updates(
&self,
bundle_state: &BundleStateWithReceipts,
bundle_state: &BundleState,
) -> ProviderResult<(B256, TrieUpdates)> {
let mut state = self.bundle_state_data_provider.state().clone();
let mut state = self.bundle_state_data_provider.state().state().clone();
state.extend(bundle_state.clone());
self.state_provider.state_root_with_updates(&state)
}

View File

@ -1,7 +1,6 @@
use crate::{
providers::{state::macros::delegate_provider_impls, StaticFileProvider},
AccountReader, BlockHashReader, BundleStateWithReceipts, ProviderError, StateProvider,
StateRootProvider,
AccountReader, BlockHashReader, ProviderError, StateProvider, StateRootProvider,
};
use reth_db::{
cursor::{DbCursorRO, DbDupCursorRO},
@ -17,6 +16,7 @@ use reth_primitives::{
StaticFileSegment, StorageKey, StorageValue, B256,
};
use reth_trie::{updates::TrieUpdates, HashedPostState};
use revm::db::BundleState;
use std::fmt::Debug;
/// State provider for a given block number which takes a tx reference.
@ -257,18 +257,15 @@ impl<'b, TX: DbTx> BlockHashReader for HistoricalStateProviderRef<'b, TX> {
}
impl<'b, TX: DbTx> StateRootProvider for HistoricalStateProviderRef<'b, TX> {
fn state_root(&self, state: &BundleStateWithReceipts) -> ProviderResult<B256> {
fn state_root(&self, state: &BundleState) -> ProviderResult<B256> {
let mut revert_state = self.revert_state()?;
revert_state.extend(state.hash_state_slow());
revert_state.extend(HashedPostState::from_bundle_state(&state.state));
revert_state.state_root(self.tx).map_err(|err| ProviderError::Database(err.into()))
}
fn state_root_with_updates(
&self,
state: &BundleStateWithReceipts,
) -> ProviderResult<(B256, TrieUpdates)> {
fn state_root_with_updates(&self, state: &BundleState) -> ProviderResult<(B256, TrieUpdates)> {
let mut revert_state = self.revert_state()?;
revert_state.extend(state.hash_state_slow());
revert_state.extend(HashedPostState::from_bundle_state(&state.state));
revert_state
.state_root_with_updates(self.tx)
.map_err(|err| ProviderError::Database(err.into()))

View File

@ -1,6 +1,6 @@
use crate::{
providers::{state::macros::delegate_provider_impls, StaticFileProvider},
AccountReader, BlockHashReader, BundleStateWithReceipts, StateProvider, StateRootProvider,
AccountReader, BlockHashReader, StateProvider, StateRootProvider,
};
use reth_db::{
cursor::{DbCursorRO, DbDupCursorRO},
@ -12,28 +12,29 @@ use reth_primitives::{
trie::AccountProof, Account, Address, BlockNumber, Bytecode, StaticFileSegment, StorageKey,
StorageValue, B256,
};
use reth_trie::{proof::Proof, updates::TrieUpdates};
use reth_trie::{proof::Proof, updates::TrieUpdates, HashedPostState};
use revm::db::BundleState;
/// State provider over latest state that takes tx reference.
#[derive(Debug)]
pub struct LatestStateProviderRef<'b, TX: DbTx> {
/// database transaction
db: &'b TX,
tx: &'b TX,
/// Static File provider
static_file_provider: StaticFileProvider,
}
impl<'b, TX: DbTx> LatestStateProviderRef<'b, TX> {
/// Create new state provider
pub fn new(db: &'b TX, static_file_provider: StaticFileProvider) -> Self {
Self { db, static_file_provider }
pub fn new(tx: &'b TX, static_file_provider: StaticFileProvider) -> Self {
Self { tx, static_file_provider }
}
}
impl<'b, TX: DbTx> AccountReader for LatestStateProviderRef<'b, TX> {
/// Get basic account information.
fn basic_account(&self, address: Address) -> ProviderResult<Option<Account>> {
self.db.get::<tables::PlainAccountState>(address).map_err(Into::into)
self.tx.get::<tables::PlainAccountState>(address).map_err(Into::into)
}
}
@ -44,7 +45,7 @@ impl<'b, TX: DbTx> BlockHashReader for LatestStateProviderRef<'b, TX> {
StaticFileSegment::Headers,
number,
|static_file| static_file.block_hash(number),
|| Ok(self.db.get::<tables::CanonicalHeaders>(number)?),
|| Ok(self.tx.get::<tables::CanonicalHeaders>(number)?),
)
}
@ -58,7 +59,7 @@ impl<'b, TX: DbTx> BlockHashReader for LatestStateProviderRef<'b, TX> {
start..end,
|static_file, range, _| static_file.canonical_hashes_range(range.start, range.end),
|range, _| {
self.db
self.tx
.cursor_read::<tables::CanonicalHeaders>()
.map(|mut cursor| {
cursor
@ -74,20 +75,18 @@ impl<'b, TX: DbTx> BlockHashReader for LatestStateProviderRef<'b, TX> {
}
impl<'b, TX: DbTx> StateRootProvider for LatestStateProviderRef<'b, TX> {
fn state_root(&self, bundle_state: &BundleStateWithReceipts) -> ProviderResult<B256> {
bundle_state
.hash_state_slow()
.state_root(self.db)
fn state_root(&self, bundle_state: &BundleState) -> ProviderResult<B256> {
HashedPostState::from_bundle_state(&bundle_state.state)
.state_root(self.tx)
.map_err(|err| ProviderError::Database(err.into()))
}
fn state_root_with_updates(
&self,
bundle_state: &BundleStateWithReceipts,
bundle_state: &BundleState,
) -> ProviderResult<(B256, TrieUpdates)> {
bundle_state
.hash_state_slow()
.state_root_with_updates(self.db)
HashedPostState::from_bundle_state(&bundle_state.state)
.state_root_with_updates(self.tx)
.map_err(|err| ProviderError::Database(err.into()))
}
}
@ -99,7 +98,7 @@ impl<'b, TX: DbTx> StateProvider for LatestStateProviderRef<'b, TX> {
account: Address,
storage_key: StorageKey,
) -> ProviderResult<Option<StorageValue>> {
let mut cursor = self.db.cursor_dup_read::<tables::PlainStorageState>()?;
let mut cursor = self.tx.cursor_dup_read::<tables::PlainStorageState>()?;
if let Some(entry) = cursor.seek_by_key_subkey(account, storage_key)? {
if entry.key == storage_key {
return Ok(Some(entry.value))
@ -110,11 +109,11 @@ impl<'b, TX: DbTx> StateProvider for LatestStateProviderRef<'b, TX> {
/// Get account code by its hash
fn bytecode_by_hash(&self, code_hash: B256) -> ProviderResult<Option<Bytecode>> {
self.db.get::<tables::Bytecodes>(code_hash).map_err(Into::into)
self.tx.get::<tables::Bytecodes>(code_hash).map_err(Into::into)
}
fn proof(&self, address: Address, slots: &[B256]) -> ProviderResult<AccountProof> {
Ok(Proof::new(self.db)
Ok(Proof::new(self.tx)
.account_proof(address, slots)
.map_err(Into::<reth_db::DatabaseError>::into)?)
}

View File

@ -31,8 +31,8 @@ macro_rules! delegate_provider_impls {
$crate::providers::state::macros::delegate_impls_to_as_ref!(
for $target =>
StateRootProvider $(where [$($generics)*])? {
fn state_root(&self, state: &crate::BundleStateWithReceipts) -> reth_interfaces::provider::ProviderResult<reth_primitives::B256>;
fn state_root_with_updates(&self, state: &crate::BundleStateWithReceipts) -> reth_interfaces::provider::ProviderResult<(reth_primitives::B256, reth_trie::updates::TrieUpdates)>;
fn state_root(&self, state: &revm::db::BundleState) -> reth_interfaces::provider::ProviderResult<reth_primitives::B256>;
fn state_root_with_updates(&self, state: &revm::db::BundleState) -> reth_interfaces::provider::ProviderResult<(reth_primitives::B256, reth_trie::updates::TrieUpdates)>;
}
AccountReader $(where [$($generics)*])? {
fn basic_account(&self, address: reth_primitives::Address) -> reth_interfaces::provider::ProviderResult<Option<reth_primitives::Account>>;

View File

@ -1,5 +1,4 @@
use crate::{
bundle_state::BundleStateWithReceipts,
traits::{BlockSource, ReceiptProvider},
AccountReader, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt,
BundleStateDataProvider, ChainSpecProvider, ChangeSetReader, EvmEnvProvider, HeaderProvider,
@ -18,7 +17,10 @@ use reth_primitives::{
U256,
};
use reth_trie::updates::TrieUpdates;
use revm::primitives::{BlockEnv, CfgEnvWithHandlerCfg};
use revm::{
db::BundleState,
primitives::{BlockEnv, CfgEnvWithHandlerCfg},
};
use std::{
collections::{BTreeMap, HashMap},
ops::{RangeBounds, RangeInclusive},
@ -516,13 +518,13 @@ impl AccountReader for MockEthProvider {
}
impl StateRootProvider for MockEthProvider {
fn state_root(&self, _bundle_state: &BundleStateWithReceipts) -> ProviderResult<B256> {
fn state_root(&self, _bundle_state: &BundleState) -> ProviderResult<B256> {
Ok(B256::default())
}
fn state_root_with_updates(
&self,
_bundle_state: &BundleStateWithReceipts,
_bundle_state: &BundleState,
) -> ProviderResult<(B256, TrieUpdates)> {
Ok((B256::default(), Default::default()))
}

View File

@ -1,5 +1,4 @@
use crate::{
bundle_state::BundleStateWithReceipts,
traits::{BlockSource, ReceiptProvider},
AccountReader, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt,
ChainSpecProvider, ChangeSetReader, EvmEnvProvider, HeaderProvider, PruneCheckpointReader,
@ -20,7 +19,10 @@ use reth_primitives::{
MAINNET, U256,
};
use reth_trie::updates::TrieUpdates;
use revm::primitives::{BlockEnv, CfgEnvWithHandlerCfg};
use revm::{
db::BundleState,
primitives::{BlockEnv, CfgEnvWithHandlerCfg},
};
use std::{
ops::{RangeBounds, RangeInclusive},
sync::Arc,
@ -285,13 +287,13 @@ impl ChangeSetReader for NoopProvider {
}
impl StateRootProvider for NoopProvider {
fn state_root(&self, _state: &BundleStateWithReceipts) -> ProviderResult<B256> {
fn state_root(&self, _state: &BundleState) -> ProviderResult<B256> {
Ok(B256::default())
}
fn state_root_with_updates(
&self,
_bundle_state: &BundleStateWithReceipts,
_bundle_state: &BundleState,
) -> ProviderResult<(B256, TrieUpdates)> {
Ok((B256::default(), TrieUpdates::default()))
}

View File

@ -7,6 +7,7 @@ use reth_primitives::{
Bytecode, StorageKey, StorageValue, B256, KECCAK_EMPTY, U256,
};
use reth_trie::updates::TrieUpdates;
use revm::db::BundleState;
/// Type alias of boxed [StateProvider].
pub type StateProviderBox = Box<dyn StateProvider>;
@ -235,12 +236,12 @@ pub trait StateRootProvider: Send + Sync {
/// NOTE: It is recommended to provide a different implementation from
/// `state_root_with_updates` since it affects the memory usage during state root
/// computation.
fn state_root(&self, bundle_state: &BundleStateWithReceipts) -> ProviderResult<B256>;
fn state_root(&self, bundle_state: &BundleState) -> ProviderResult<B256>;
/// Returns the state root of the BundleState on top of the current state with trie
/// updates to be committed to the database.
fn state_root_with_updates(
&self,
bundle_state: &BundleStateWithReceipts,
bundle_state: &BundleState,
) -> ProviderResult<(B256, TrieUpdates)>;
}