Skip to content

Commit

Permalink
feat(perf): integrate OnStateHook in executor
Browse files Browse the repository at this point in the history
  • Loading branch information
fgimenez committed Sep 30, 2024
1 parent 6d57b9e commit 238b4e8
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 18 deletions.
54 changes: 48 additions & 6 deletions crates/ethereum/evm/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use reth_evm::{
BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput,
BlockExecutorProvider, BlockValidationError, Executor, ProviderError,
},
system_calls::SystemCaller,
system_calls::{NoopHook, OnStateHook, SystemCaller},
ConfigureEvm,
};
use reth_execution_types::ExecutionOutcome;
Expand Down Expand Up @@ -130,17 +130,23 @@ where
///
/// It does __not__ apply post-execution changes that do not require an [EVM](Evm), for that see
/// [`EthBlockExecutor::post_execution`].
fn execute_state_transitions<Ext, DB>(
fn execute_state_transitions<Ext, DB, F>(
&self,
block: &BlockWithSenders,
mut evm: Evm<'_, Ext, &mut State<DB>>,
state_hook: Option<F>,
) -> Result<EthExecuteOutput, BlockExecutionError>
where
DB: Database,
DB::Error: Into<ProviderError> + Display,
F: OnStateHook,
{
let mut system_caller = SystemCaller::new(&self.evm_config, &self.chain_spec);

if let Some(hook) = state_hook {
system_caller = system_caller.with_state_hook(hook);
}

system_caller.apply_pre_execution_changes(block, &mut evm)?;

// execute transactions
Expand Down Expand Up @@ -169,7 +175,10 @@ where
error: Box::new(new_err),
}
})?;
evm.db_mut().commit(state);
evm.db_mut().commit(state.clone());
if let Some(mut hook) = state_hook {
hook.on_state(&ResultAndState { result: result.clone(), state });
}

// append gas used
cumulative_gas_used += result.gas_used();
Expand Down Expand Up @@ -260,25 +269,37 @@ where
EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, Default::default())
}

fn execute_without_verification(
&mut self,
block: &BlockWithSenders,
total_difficulty: U256,
) -> Result<EthExecuteOutput, BlockExecutionError> {
self.execute_without_verification_with_state_hook(block, total_difficulty, None::<NoopHook>)
}

/// Execute a single block and apply the state changes to the internal state.
///
/// Returns the receipts of the transactions in the block, the total gas used and the list of
/// EIP-7685 [requests](Request).
///
/// Returns an error if execution fails.
fn execute_without_verification(
fn execute_without_verification_with_state_hook<F>(
&mut self,
block: &BlockWithSenders,
total_difficulty: U256,
) -> Result<EthExecuteOutput, BlockExecutionError> {
state_hook: Option<F>,
) -> Result<EthExecuteOutput, BlockExecutionError>
where
F: OnStateHook,
{
// 1. prepare state on new block
self.on_new_block(&block.header);

// 2. configure the evm and execute
let env = self.evm_env_for_block(&block.header, total_difficulty);
let output = {
let evm = self.executor.evm_config.evm_with_env(&mut self.state, env);
self.executor.execute_state_transitions(block, evm)
self.executor.execute_state_transitions(block, evm, state_hook)
}?;

// 3. apply post execution changes
Expand Down Expand Up @@ -368,6 +389,27 @@ where
witness(&self.state);
Ok(BlockExecutionOutput { state: self.state.take_bundle(), receipts, requests, gas_used })
}

fn execute_with_state_hook<F>(
mut self,
input: Self::Input<'_>,
state_hook: F,
) -> Result<Self::Output, Self::Error>
where
F: OnStateHook,
{
let BlockExecutionInput { block, total_difficulty } = input;
let EthExecuteOutput { receipts, requests, gas_used } = self
.execute_without_verification_with_state_hook(
block,
total_difficulty,
Some(state_hook),
)?;

// NOTE: we need to merge keep the reverts for the bundle retention
self.state.merge_transitions(BundleRetention::Reverts);
Ok(BlockExecutionOutput { state: self.state.take_bundle(), receipts, requests, gas_used })
}
}
/// An executor for a batch of blocks.
///
Expand Down
19 changes: 18 additions & 1 deletion crates/evm/src/either.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

use core::fmt::Display;

use crate::execute::{BatchExecutor, BlockExecutorProvider, Executor};
use crate::{
execute::{BatchExecutor, BlockExecutorProvider, Executor},
system_calls::OnStateHook,
};
use alloy_primitives::BlockNumber;
use reth_execution_errors::BlockExecutionError;
use reth_execution_types::{BlockExecutionInput, BlockExecutionOutput, ExecutionOutcome};
Expand Down Expand Up @@ -87,6 +90,20 @@ where
Self::Right(b) => b.execute_with_state_witness(input, witness),
}
}

fn execute_with_state_hook<F>(
self,
input: Self::Input<'_>,
state_hook: F,
) -> Result<Self::Output, Self::Error>
where
F: OnStateHook,
{
match self {
Self::Left(a) => a.execute_with_state_hook(input, state_hook),
Self::Right(b) => b.execute_with_state_hook(input, state_hook),
}
}
}

impl<A, B, DB> BatchExecutor<DB> for Either<A, B>
Expand Down
23 changes: 23 additions & 0 deletions crates/evm/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use reth_prune_types::PruneModes;
use revm::State;
use revm_primitives::db::Database;

use crate::system_calls::OnStateHook;

/// A general purpose executor trait that executes an input (e.g. block) and produces an output
/// (e.g. state changes and receipts).
///
Expand Down Expand Up @@ -43,6 +45,16 @@ pub trait Executor<DB> {
) -> Result<Self::Output, Self::Error>
where
F: FnMut(&State<DB>);

/// Executes the EVM with the given input and accepts a state hook closure that is invoked with
/// the EVM state after execution.
fn execute_with_state_hook<F>(
self,
input: Self::Input<'_>,
state_hook: F,
) -> Result<Self::Output, Self::Error>
where
F: OnStateHook;
}

/// A general purpose executor that can execute multiple inputs in sequence, validate the outputs,
Expand Down Expand Up @@ -199,6 +211,17 @@ mod tests {
{
Err(BlockExecutionError::msg("execution unavailable for tests"))
}

fn execute_with_state_hook<F>(
self,
_: Self::Input<'_>,
_: F,
) -> Result<Self::Output, Self::Error>
where
F: OnStateHook,
{
Err(BlockExecutionError::msg("execution unavailable for tests"))
}
}

impl<DB> BatchExecutor<DB> for TestExecutor<DB> {
Expand Down
16 changes: 15 additions & 1 deletion crates/evm/src/noop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use reth_storage_errors::provider::ProviderError;
use revm::State;
use revm_primitives::db::Database;

use crate::execute::{BatchExecutor, BlockExecutorProvider, Executor};
use crate::{
execute::{BatchExecutor, BlockExecutorProvider, Executor},
system_calls::OnStateHook,
};

const UNAVAILABLE_FOR_NOOP: &str = "execution unavailable for noop";

Expand Down Expand Up @@ -58,6 +61,17 @@ impl<DB> Executor<DB> for NoopBlockExecutorProvider {
{
Err(BlockExecutionError::msg(UNAVAILABLE_FOR_NOOP))
}

fn execute_with_state_hook<F>(
self,
_: Self::Input<'_>,
_: F,
) -> Result<Self::Output, Self::Error>
where
F: OnStateHook,
{
Err(BlockExecutionError::msg(UNAVAILABLE_FOR_NOOP))
}
}

impl<DB> BatchExecutor<DB> for NoopBlockExecutorProvider {
Expand Down
4 changes: 2 additions & 2 deletions crates/evm/src/system_calls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ impl OnStateHook for NoopHook {
///
/// This can be used to chain system transaction calls.
#[allow(missing_debug_implementations)]
pub struct SystemCaller<'a, EvmConfig, Chainspec, Hook = NoopHook> {
pub struct SystemCaller<'a, EvmConfig, Chainspec, Hook> {
evm_config: &'a EvmConfig,
chain_spec: Chainspec,
/// Optional hook to be called after each state change.
hook: Option<Hook>,
}

impl<'a, EvmConfig, Chainspec> SystemCaller<'a, EvmConfig, Chainspec> {
impl<'a, EvmConfig, Chainspec, Hook> SystemCaller<'a, EvmConfig, Chainspec, Hook> {
/// Create a new system caller with the given EVM config, database, and chain spec, and creates
/// the EVM with the given initialized config and block environment.
pub const fn new(evm_config: &'a EvmConfig, chain_spec: Chainspec) -> Self {
Expand Down
18 changes: 16 additions & 2 deletions crates/evm/src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//! Helpers for testing.

use crate::execute::{
BatchExecutor, BlockExecutionInput, BlockExecutionOutput, BlockExecutorProvider, Executor,
use crate::{
execute::{
BatchExecutor, BlockExecutionInput, BlockExecutionOutput, BlockExecutorProvider, Executor,
},
system_calls::OnStateHook,
};
use alloy_primitives::BlockNumber;
use parking_lot::Mutex;
Expand Down Expand Up @@ -73,6 +76,17 @@ impl<DB> Executor<DB> for MockExecutorProvider {
{
unimplemented!()
}

fn execute_with_state_hook<F>(
self,
_: Self::Input<'_>,
_: F,
) -> Result<Self::Output, Self::Error>
where
F: OnStateHook,
{
unimplemented!()
}
}

impl<DB> BatchExecutor<DB> for MockExecutorProvider {
Expand Down
59 changes: 53 additions & 6 deletions crates/optimism/evm/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use reth_evm::{
BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput,
BlockExecutorProvider, BlockValidationError, Executor, ProviderError,
},
system_calls::SystemCaller,
system_calls::{NoopHook, OnStateHook, SystemCaller},
ConfigureEvm,
};
use reth_execution_types::ExecutionOutcome;
Expand Down Expand Up @@ -111,16 +111,22 @@ where
/// # Note
///
/// It does __not__ apply post-execution changes.
fn execute_pre_and_transactions<Ext, DB>(
fn execute_pre_and_transactions<Ext, DB, F>(
&self,
block: &BlockWithSenders,
mut evm: Evm<'_, Ext, &mut State<DB>>,
state_hook: Option<F>,
) -> Result<(Vec<Receipt>, u64), BlockExecutionError>
where
DB: Database<Error: Into<ProviderError> + Display>,
F: OnStateHook,
{
let mut system_caller = SystemCaller::new(&self.evm_config, &self.chain_spec);

if let Some(hook) = state_hook {
system_caller = system_caller.with_state_hook(hook);
}

// apply pre execution changes
system_caller.apply_beacon_root_contract_call(
block.timestamp,
Expand Down Expand Up @@ -193,7 +199,10 @@ where
"Executed transaction"
);

evm.db_mut().commit(state);
evm.db_mut().commit(state.clone());
if let Some(mut hook) = state_hook {
hook.on_state(ResultAndState { result: result.clone(), state });
}

// append gas used
cumulative_gas_used += result.gas_used();
Expand Down Expand Up @@ -278,16 +287,28 @@ where
EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, Default::default())
}

fn execute_without_verification(
&mut self,
block: &BlockWithSenders,
total_difficulty: U256,
) -> Result<EthExecuteOutput, BlockExecutionError> {
self.execute_without_verification_with_state_hook(block, total_difficulty, None::<NoopHook>)
}

/// Execute a single block and apply the state changes to the internal state.
///
/// Returns the receipts of the transactions in the block and the total gas used.
///
/// Returns an error if execution fails.
fn execute_without_verification(
fn execute_without_verification_with_state_hook<F>(
&mut self,
block: &BlockWithSenders,
total_difficulty: U256,
) -> Result<(Vec<Receipt>, u64), BlockExecutionError> {
state_hook: Option<F>,
) -> Result<(Vec<Receipt>, u64), BlockExecutionError>
where
F: OnStateHook,
{
// 1. prepare state on new block
self.on_new_block(&block.header);

Expand All @@ -296,7 +317,7 @@ where

let (receipts, gas_used) = {
let evm = self.executor.evm_config.evm_with_env(&mut self.state, env);
self.executor.execute_pre_and_transactions(block, evm)
self.executor.execute_pre_and_transactions(block, evm, state_hook)
}?;

// 3. apply post execution changes
Expand Down Expand Up @@ -383,6 +404,32 @@ where
gas_used,
})
}

fn execute_with_state_hook<F>(
mut self,
input: Self::Input<'_>,
state_hook: F,
) -> Result<Self::Output, Self::Error>
where
F: OnStateHook,
{
let BlockExecutionInput { block, total_difficulty } = input;
let (receipts, gas_used) = self.execute_without_verification_with_state_hook(
block,
total_difficulty,
Some(state_hook),
)?;

// NOTE: we need to merge keep the reverts for the bundle retention
self.state.merge_transitions(BundleRetention::Reverts);

Ok(BlockExecutionOutput {
state: self.state.take_bundle(),
receipts,
requests: vec![],
gas_used,
})
}
}

/// An executor for a batch of blocks.
Expand Down

0 comments on commit 238b4e8

Please sign in to comment.