Skip to content

Commit

Permalink
chore: inspector stack from evm-inspectors (#7076)
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniPopes authored Mar 9, 2024
1 parent a8345e3 commit 4aecad8
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 12 deletions.
7 changes: 4 additions & 3 deletions crates/node-core/src/node_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ use reth_provider::{
CanonStateSubscriptions, HeaderProvider, HeaderSyncMode, ProviderFactory,
StageCheckpointReader,
};
use reth_revm::EvmProcessorFactory;
use reth_revm::{
stack::{Hook, InspectorStackConfig},
EvmProcessorFactory,
};
use reth_stages::{
prelude::*,
stages::{
Expand All @@ -68,7 +71,6 @@ use reth_transaction_pool::{
blobstore::{DiskFileBlobStore, DiskFileBlobStoreConfig},
EthTransactionPool, TransactionPool, TransactionValidationTaskExecutor,
};
use revm_inspectors::stack::Hook;
use secp256k1::SecretKey;
use std::{
net::{SocketAddr, SocketAddrV4},
Expand Down Expand Up @@ -803,7 +805,6 @@ impl NodeConfig {
}

let (tip_tx, tip_rx) = watch::channel(B256::ZERO);
use revm_inspectors::stack::InspectorStackConfig;
let factory = reth_revm::EvmProcessorFactory::new(self.chain.clone(), evm_config);

let stack_config = InspectorStackConfig {
Expand Down
15 changes: 9 additions & 6 deletions crates/revm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@ pub mod state_change;
/// revm executor factory.
pub use factory::EvmProcessorFactory;

/// reexport for convenience
pub use revm_inspectors::*;

/// Re-export everything
pub use revm::{self, *};

/// Ethereum DAO hardfork state change data.
pub mod eth_dao_fork;

/// An inspector stack abstracting the implementation details of
/// each inspector and allowing to hook on block/transaction execution,
/// used in the main Reth executor.
pub mod stack;

/// Optimism-specific implementation and utilities for the executor
#[cfg(feature = "optimism")]
pub mod optimism;

// Convenience re-exports.
pub use revm::{self, *};
pub use revm_inspectors::*;
6 changes: 3 additions & 3 deletions crates/revm/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ where

self.db_mut().set_state_clear_flag(state_clear_flag);

let mut cfg: CfgEnvWithHandlerCfg =
let mut cfg =
CfgEnvWithHandlerCfg::new_with_spec_id(self.evm.cfg().clone(), self.evm.spec_id());
EvmConfig::fill_cfg_and_block_env(
&mut cfg,
Expand Down Expand Up @@ -260,15 +260,15 @@ where
fill_op_tx_env(self.evm.tx_mut(), transaction, sender, envelope_buf.into());
}

let hash = transaction.hash();
let hash = transaction.hash_ref();
let should_inspect = self.evm.context.external.should_inspect(self.evm.env(), hash);
let out = if should_inspect {
// push inspector handle register.
self.evm.handler.append_handler_register_plain(inspector_handle_register);
let output = self.evm.transact();
tracing::trace!(
target: "evm",
?hash, ?output, ?transaction, env = ?self.evm.context.evm.env,
%hash, ?output, ?transaction, env = ?self.evm.context.evm.env,
"Executed transaction"
);
// pop last handle register
Expand Down
202 changes: 202 additions & 0 deletions crates/revm/src/stack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
use revm::{
inspectors::CustomPrintTracer,
interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter},
primitives::{Address, Env, Log, B256, U256},
Database, EvmContext, Inspector,
};
use std::fmt::Debug;

/// A hook to inspect the execution of the EVM.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Hook {
/// No hook.
#[default]
None,
/// Hook on a specific block.
Block(u64),
/// Hook on a specific transaction hash.
Transaction(B256),
/// Hooks on every transaction in a block.
All,
}

impl Hook {
/// Returns `true` if this hook should be used.
#[inline]
pub fn is_enabled(&self, block_number: u64, tx_hash: &B256) -> bool {
match self {
Hook::None => false,
Hook::Block(block) => block_number == *block,
Hook::Transaction(hash) => hash == tx_hash,
Hook::All => true,
}
}
}

/// An inspector that calls multiple inspectors in sequence.
#[derive(Clone, Default)]
pub struct InspectorStack {
/// An inspector that prints the opcode traces to the console.
pub custom_print_tracer: Option<CustomPrintTracer>,
/// The provided hook
pub hook: Hook,
}

impl Debug for InspectorStack {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("InspectorStack")
.field("custom_print_tracer", &self.custom_print_tracer.is_some())
.field("hook", &self.hook)
.finish()
}
}

impl InspectorStack {
/// Creates a new inspector stack with the given configuration.
#[inline]
pub fn new(config: InspectorStackConfig) -> Self {
Self {
hook: config.hook,
custom_print_tracer: config.use_printer_tracer.then(Default::default),
}
}

/// Returns `true` if this inspector should be used.
#[inline]
pub fn should_inspect(&self, env: &Env, tx_hash: &B256) -> bool {
self.custom_print_tracer.is_some() &&
self.hook.is_enabled(env.block.number.saturating_to(), tx_hash)
}
}

/// Configuration for the inspectors.
#[derive(Clone, Copy, Debug, Default)]
pub struct InspectorStackConfig {
/// Enable revm inspector printer.
/// In execution this will print opcode level traces directly to console.
pub use_printer_tracer: bool,

/// Hook on a specific block or transaction.
pub hook: Hook,
}

/// Helper macro to call the same method on multiple inspectors without resorting to dynamic
/// dispatch.
#[macro_export]
macro_rules! call_inspectors {
([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr $(,)?) => {{$(
if let Some($id) = $inspector {
$call
}
)+}}
}

impl<DB> Inspector<DB> for InspectorStack
where
DB: Database,
{
#[inline]
fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<DB>) {
call_inspectors!([&mut self.custom_print_tracer], |inspector| {
inspector.initialize_interp(interp, context);
});
}

#[inline]
fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<DB>) {
call_inspectors!([&mut self.custom_print_tracer], |inspector| {
inspector.step(interp, context);
});
}

#[inline]
fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<DB>) {
call_inspectors!([&mut self.custom_print_tracer], |inspector| {
inspector.step_end(interp, context);
});
}

#[inline]
fn log(&mut self, context: &mut EvmContext<DB>, log: &Log) {
call_inspectors!([&mut self.custom_print_tracer], |inspector| {
inspector.log(context, log);
});
}

#[inline]
fn call(
&mut self,
context: &mut EvmContext<DB>,
inputs: &mut CallInputs,
) -> Option<CallOutcome> {
call_inspectors!([&mut self.custom_print_tracer], |inspector| {
if let Some(outcome) = inspector.call(context, inputs) {
return Some(outcome);
}
});

None
}

#[inline]
fn call_end(
&mut self,
context: &mut EvmContext<DB>,
inputs: &CallInputs,
outcome: CallOutcome,
) -> CallOutcome {
call_inspectors!([&mut self.custom_print_tracer], |inspector| {
let new_ret = inspector.call_end(context, inputs, outcome.clone());

// If the inspector returns a different ret or a revert with a non-empty message,
// we assume it wants to tell us something
if new_ret != outcome {
return new_ret;
}
});

outcome
}

#[inline]
fn create(
&mut self,
context: &mut EvmContext<DB>,
inputs: &mut CreateInputs,
) -> Option<CreateOutcome> {
call_inspectors!([&mut self.custom_print_tracer], |inspector| {
if let Some(out) = inspector.create(context, inputs) {
return Some(out);
}
});

None
}

#[inline]
fn create_end(
&mut self,
context: &mut EvmContext<DB>,
inputs: &CreateInputs,
outcome: CreateOutcome,
) -> CreateOutcome {
call_inspectors!([&mut self.custom_print_tracer], |inspector| {
let new_ret = inspector.create_end(context, inputs, outcome.clone());

// If the inspector returns a different ret or a revert with a non-empty message,
// we assume it wants to tell us something
if new_ret != outcome {
return new_ret;
}
});

outcome
}

#[inline]
fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
call_inspectors!([&mut self.custom_print_tracer], |inspector| {
Inspector::<DB>::selfdestruct(inspector, contract, target, value);
});
}
}

0 comments on commit 4aecad8

Please sign in to comment.