Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions crates/engine/tree/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ workspace = true
[dependencies]
# reth
reth-chain-state.workspace = true
reth-chainspec = { workspace = true, optional = true }
reth-chainspec.workspace = true
reth-consensus.workspace = true
reth-db.workspace = true
reth-engine-primitives = { workspace = true, features = ["std"] }
Expand Down Expand Up @@ -79,7 +79,6 @@ reth-tracing = { workspace = true, optional = true }
# reth
reth-evm-ethereum = { workspace = true, features = ["test-utils"] }
reth-chain-state = { workspace = true, features = ["test-utils"] }
reth-chainspec.workspace = true
reth-db-common.workspace = true
reth-ethereum-consensus.workspace = true
metrics-util = { workspace = true, features = ["debugging"] }
Expand Down
3 changes: 2 additions & 1 deletion crates/engine/tree/benches/state_root_task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use alloy_primitives::{Address, B256};
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use proptest::test_runner::TestRunner;
use rand::Rng;
use reth_chainspec::ChainSpec;
use reth_chainspec::{ChainSpec, MAINNET};
use reth_db_common::init::init_genesis;
use reth_engine_tree::tree::{
executor::WorkloadExecutor, precompile_cache::PrecompileCacheMap, PayloadProcessor,
Expand Down Expand Up @@ -220,6 +220,7 @@ fn bench_state_root(c: &mut Criterion) {
EthEvmConfig::new(factory.chain_spec()),
&TreeConfig::default(),
PrecompileCacheMap::default(),
MAINNET.clone(),
);
let provider = BlockchainProvider::new(factory).unwrap();

Expand Down
17 changes: 13 additions & 4 deletions crates/engine/tree/src/tree/cached_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use reth_trie::{
updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof,
MultiProofTargets, StorageMultiProof, StorageProof, TrieInput,
};
use revm_primitives::map::DefaultHashBuilder;
use revm_primitives::{hardfork::SpecId, map::DefaultHashBuilder};
use std::{sync::Arc, time::Duration};
use tracing::{debug_span, instrument, trace};

Expand Down Expand Up @@ -442,7 +442,11 @@ impl ExecutionCache {
///
/// Returns an error if the state updates are inconsistent and should be discarded.
#[instrument(level = "debug", target = "engine::caching", skip_all)]
pub(crate) fn insert_state(&self, state_updates: &BundleState) -> Result<(), ()> {
pub(crate) fn insert_state(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs doc updates why this needs an evm specid

&self,
state_updates: &BundleState,
spec: &SpecId,
) -> Result<(), ()> {
let _enter =
debug_span!(target: "engine::tree", "contracts", len = state_updates.contracts.len())
.entered();
Expand All @@ -467,8 +471,13 @@ impl ExecutionCache {
continue
}

// If the account was destroyed, invalidate from the account / storage caches
if account.was_destroyed() {
// If the account was destroyed, invalidate from the account / storage caches.
//
// Post-cancun when EIP-6780 is live, an account can be destroyed only when it's created
// in the same transaction. This guarantees that we will not have such accounts
// and storage slots in our cache, because Revm doesn't go through the
// Database for freshly created accounts. Hence we can safely ignore invalidating them.
Comment on lines +476 to +479
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesnt mention state, can we elaborate why this is okay to not invalidate state

if account.was_destroyed() && !spec.is_enabled_in(revm_primitives::hardfork::CANCUN) {
// Invalidate the account cache entry if destroyed
self.account_cache.invalidate(addr);

Expand Down
50 changes: 33 additions & 17 deletions crates/engine/tree/src/tree/payload_processor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ use multiproof::{SparseTrieUpdate, *};
use parking_lot::RwLock;
use prewarm::PrewarmMetrics;
use rayon::prelude::*;
use reth_chainspec::EthereumHardforks;
use reth_evm::{
execute::{ExecutableTxFor, WithTxEnv},
ConfigureEvm, EvmEnvFor, ExecutableTxIterator, ExecutableTxTuple, OnStateHook, SpecFor,
TxEnvFor,
};
use reth_execution_types::ExecutionOutcome;
use reth_primitives_traits::NodePrimitives;
use reth_primitives_traits::{HeaderTy, NodePrimitives};
use reth_provider::{BlockReader, DatabaseProviderROFactory, StateProviderFactory, StateReader};
use reth_revm::{db::BundleState, state::EvmState};
use reth_trie::{hashed_cursor::HashedCursorFactory, trie_cursor::TrieCursorFactory};
Expand All @@ -42,6 +43,7 @@ use reth_trie_sparse::{
ClearedSparseStateTrie, SparseStateTrie, SparseTrie,
};
use reth_trie_sparse_parallel::{ParallelSparseTrie, ParallelismThresholds};
use revm_primitives::hardfork::SpecId;
use std::{
collections::BTreeMap,
sync::{
Expand Down Expand Up @@ -102,7 +104,7 @@ type IteratorPayloadHandle<Evm, I, N> = PayloadHandle<

/// Entrypoint for executing the payload.
#[derive(Debug)]
pub struct PayloadProcessor<Evm>
pub struct PayloadProcessor<Evm, C>
where
Evm: ConfigureEvm,
{
Expand All @@ -118,6 +120,7 @@ where
disable_transaction_prewarming: bool,
/// Whether state cache should be disable
disable_state_cache: bool,
chain_spec: Arc<C>,
/// Determines how to configure the evm for execution.
evm_config: Evm,
/// Whether precompile cache should be disabled.
Expand All @@ -137,7 +140,7 @@ where
prewarm_max_concurrency: usize,
}

impl<N, Evm> PayloadProcessor<Evm>
impl<N, Evm, C> PayloadProcessor<Evm, C>
where
N: NodePrimitives,
Evm: ConfigureEvm<Primitives = N>,
Expand All @@ -153,13 +156,15 @@ where
evm_config: Evm,
config: &TreeConfig,
precompile_cache_map: PrecompileCacheMap<SpecFor<Evm>>,
chain_spec: Arc<C>,
) -> Self {
Self {
executor,
execution_cache: Default::default(),
trie_metrics: Default::default(),
cross_block_cache_size: config.cross_block_cache_size(),
disable_transaction_prewarming: config.disable_prewarming(),
chain_spec,
evm_config,
disable_state_cache: config.disable_state_cache(),
precompile_cache_disabled: config.precompile_cache_disabled(),
Expand All @@ -171,10 +176,11 @@ where
}
}

impl<N, Evm> PayloadProcessor<Evm>
impl<N, Evm, C> PayloadProcessor<Evm, C>
where
N: NodePrimitives,
Evm: ConfigureEvm<Primitives = N> + 'static,
C: EthereumHardforks,
{
/// Spawns all background tasks and returns a handle connected to the tasks.
///
Expand Down Expand Up @@ -540,6 +546,7 @@ where
/// hitting the database, maintaining performance consistency.
pub(crate) fn on_inserted_executed_block(
&self,
header: &HeaderTy<N>,
block_with_parent: BlockWithParent,
bundle_state: &BundleState,
) {
Expand All @@ -566,7 +573,7 @@ where

// Insert the block's bundle state into cache
let new_cache = SavedCache::new(block_with_parent.block.hash, caches, cache_metrics);
if new_cache.cache().insert_state(bundle_state).is_err() {
if new_cache.cache().insert_state(bundle_state, &alloy_evm::spec(&self.chain_spec, header)).is_err() {
*cached = None;
debug!(target: "engine::caching", "cleared execution cache on update error");
return;
Expand Down Expand Up @@ -809,6 +816,8 @@ pub struct ExecutionEnv<Evm: ConfigureEvm> {
pub hash: B256,
/// Hash of the parent block.
pub parent_hash: B256,
/// Spec id associated with the EVM for the block being executed.
pub spec_id: SpecId,
}

impl<Evm: ConfigureEvm> Default for ExecutionEnv<Evm>
Expand All @@ -820,6 +829,7 @@ where
evm_env: Default::default(),
hash: Default::default(),
parent_hash: Default::default(),
spec_id: Default::default(),
}
}
}
Expand All @@ -835,10 +845,11 @@ mod tests {
precompile_cache::PrecompileCacheMap,
StateProviderBuilder, TreeConfig,
};
use alloy_consensus::Header;
use alloy_eips::eip1898::{BlockNumHash, BlockWithParent};
use alloy_evm::block::StateChangeSource;
use rand::Rng;
use reth_chainspec::ChainSpec;
use reth_chainspec::{ChainSpec, MAINNET};
use reth_db_common::init::init_genesis;
use reth_ethereum_primitives::TransactionSigned;
use reth_evm::OnStateHook;
Expand Down Expand Up @@ -934,21 +945,22 @@ mod tests {
EthEvmConfig::new(Arc::new(ChainSpec::default())),
&TreeConfig::default(),
PrecompileCacheMap::default(),
MAINNET.clone(),
);

let parent_hash = B256::from([1u8; 32]);
let block_hash = B256::from([10u8; 32]);
let header = Header::default();
let block_hash = header.hash_slow();
let block_with_parent = BlockWithParent {
block: BlockNumHash { hash: block_hash, number: 1 },
parent: parent_hash,
block: BlockNumHash { hash: block_hash, number: header.number },
parent: header.parent_hash,
};
let bundle_state = BundleState::default();

// Cache should be empty initially
assert!(payload_processor.execution_cache.get_cache_for(block_hash).is_none());

// Update cache with inserted block
payload_processor.on_inserted_executed_block(block_with_parent, &bundle_state);
payload_processor.on_inserted_executed_block(&header, block_with_parent, &bundle_state);

// Cache should now exist for the block hash
let cached = payload_processor.execution_cache.get_cache_for(block_hash);
Expand All @@ -963,24 +975,27 @@ mod tests {
EthEvmConfig::new(Arc::new(ChainSpec::default())),
&TreeConfig::default(),
PrecompileCacheMap::default(),
MAINNET.clone(),
);

// Setup: populate cache with block 1
let block1_hash = B256::from([1u8; 32]);
let header1 = Header { number: 1, ..Default::default() };
let block1_hash = header1.hash_slow();
payload_processor
.execution_cache
.update_with_guard(|slot| *slot = Some(make_saved_cache(block1_hash)));

// Try to insert block 3 with wrong parent (should skip and keep block 1's cache)
let wrong_parent = B256::from([99u8; 32]);
let block3_hash = B256::from([3u8; 32]);
let header3 =
Header { parent_hash: B256::from([99u8; 32]), number: 3, ..Default::default() };
let block3_hash = header3.hash_slow();
let block_with_parent = BlockWithParent {
block: BlockNumHash { hash: block3_hash, number: 3 },
parent: wrong_parent,
block: BlockNumHash { hash: block3_hash, number: header3.number },
parent: header3.parent_hash,
};
let bundle_state = BundleState::default();

payload_processor.on_inserted_executed_block(block_with_parent, &bundle_state);
payload_processor.on_inserted_executed_block(&header3, block_with_parent, &bundle_state);

// Cache should still be for block 1 (unchanged)
let cached = payload_processor.execution_cache.get_cache_for(block1_hash);
Expand Down Expand Up @@ -1096,6 +1111,7 @@ mod tests {
EthEvmConfig::new(factory.chain_spec()),
&TreeConfig::default(),
PrecompileCacheMap::default(),
MAINNET.clone(),
);

let provider_factory = BlockchainProvider::new(factory).unwrap();
Expand Down
3 changes: 2 additions & 1 deletion crates/engine/tree/src/tree/payload_processor/prewarm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,8 @@ where

// Insert state into cache while holding the lock
// Access the BundleState through the shared ExecutionOutcome
if new_cache.cache().insert_state(execution_outcome.state()).is_err() {
if new_cache.cache().insert_state(execution_outcome.state(), &env.spec_id).is_err()
{
// Clear the cache on error to prevent having a polluted cache
*cached = None;
debug!(target: "engine::caching", "cleared execution cache on update error");
Expand Down
29 changes: 25 additions & 4 deletions crates/engine/tree/src/tree/payload_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ use alloy_consensus::transaction::Either;
use alloy_eip7928::BlockAccessList;
use alloy_eips::{eip1898::BlockWithParent, NumHash};
use alloy_evm::Evm;
use alloy_primitives::B256;
use alloy_primitives::{BlockTimestamp, B256};
use rayon::prelude::*;
use reth_chain_state::{CanonicalInMemoryState, DeferredTrieData, ExecutedBlock};
use reth_chainspec::EthereumHardforks;
use reth_consensus::{ConsensusError, FullConsensus};
use reth_engine_primitives::{
ConfigureEngineEvm, ExecutableTxIterator, ExecutionPayload, InvalidBlockHook, PayloadValidator,
Expand All @@ -34,7 +35,7 @@ use reth_primitives_traits::{
SealedHeader, SignerRecoverable,
};
use reth_provider::{
providers::OverlayStateProviderFactory, BlockExecutionOutput, BlockReader,
providers::OverlayStateProviderFactory, BlockExecutionOutput, BlockReader, ChainSpecProvider,
DatabaseProviderFactory, DatabaseProviderROFactory, ExecutionOutcome, HashedPostStateProvider,
ProviderError, PruneCheckpointReader, StageCheckpointReader, StateProvider,
StateProviderFactory, StateReader, TrieReader,
Expand Down Expand Up @@ -108,6 +109,7 @@ impl<'a, N: NodePrimitives> TreeCtx<'a, N> {
pub struct BasicEngineValidator<P, Evm, V>
where
Evm: ConfigureEvm,
P: ChainSpecProvider<ChainSpec: EthereumHardforks>,
{
/// Provider for database access.
provider: P,
Expand All @@ -118,7 +120,7 @@ where
/// Configuration for the tree.
config: TreeConfig,
/// Payload processor for state root computation.
payload_processor: PayloadProcessor<Evm>,
payload_processor: PayloadProcessor<Evm, P::ChainSpec>,
/// Precompile cache map.
precompile_cache_map: PrecompileCacheMap<SpecFor<Evm>>,
/// Precompile cache metrics.
Expand All @@ -141,6 +143,7 @@ where
+ StateProviderFactory
+ StateReader
+ HashedPostStateProvider
+ ChainSpecProvider<ChainSpec: EthereumHardforks>
+ Clone
+ 'static,
Evm: ConfigureEvm<Primitives = N> + 'static,
Expand All @@ -161,6 +164,7 @@ where
evm_config.clone(),
&config,
precompile_cache_map.clone(),
provider.chain_spec(),
);
Self {
provider,
Expand Down Expand Up @@ -390,7 +394,14 @@ where
.in_scope(|| self.evm_env_for(&input))
.map_err(NewPayloadError::other)?;

let env = ExecutionEnv { evm_env, hash: input.hash(), parent_hash: input.parent_hash() };
let spec_id = alloy_evm::spec_by_timestamp_and_block_number(
&provider_builder.provider_factory.chain_spec(),
input.timestamp(),
input.num_hash().number,
);
Comment on lines +397 to +401
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not really a fan of is this because this is super eth specific

but this should work as long as everyone implements the eth fork activations correctly


let env =
ExecutionEnv { evm_env, hash: input.hash(), parent_hash: input.parent_hash(), spec_id };

// Plan the strategy used for state root computation.
let strategy = self.plan_state_root_computation();
Expand Down Expand Up @@ -1185,6 +1196,7 @@ where
+ StateProviderFactory
+ StateReader
+ HashedPostStateProvider
+ ChainSpecProvider<ChainSpec: EthereumHardforks>
+ Clone
+ 'static,
N: NodePrimitives,
Expand Down Expand Up @@ -1226,6 +1238,7 @@ where

fn on_inserted_executed_block(&self, block: ExecutedBlock<N>) {
self.payload_processor.on_inserted_executed_block(
block.recovered_block.header(),
block.recovered_block.block_with_parent(),
block.execution_output.state(),
);
Expand Down Expand Up @@ -1258,6 +1271,14 @@ impl<T: PayloadTypes> BlockOrPayload<T> {
}
}

/// Returns the timestamp of the block.
pub fn timestamp(&self) -> BlockTimestamp {
match self {
Self::Payload(payload) => payload.timestamp(),
Self::Block(block) => block.timestamp(),
}
}

/// Returns the parent hash of the block.
pub fn parent_hash(&self) -> B256 {
match self {
Expand Down
1 change: 1 addition & 0 deletions crates/node/builder/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1319,6 +1319,7 @@ where
impl<Node, EV> EngineValidatorBuilder<Node> for BasicEngineValidatorBuilder<EV>
where
Node: FullNodeComponents<
Types: NodeTypes<ChainSpec: EthereumHardforks>,
Evm: ConfigureEngineEvm<
<<Node::Types as NodeTypes>::Payload as PayloadTypes>::ExecutionData,
>,
Expand Down
Loading