diff --git a/crates/node-core/src/node_config.rs b/crates/node-core/src/node_config.rs index d0eb5a717c2d..08e59655cc92 100644 --- a/crates/node-core/src/node_config.rs +++ b/crates/node-core/src/node_config.rs @@ -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::{ @@ -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}, @@ -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 { diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index a9891b50e9bd..d0e180fb74ae 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -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::*; diff --git a/crates/revm/src/processor.rs b/crates/revm/src/processor.rs index 7a77a24148f7..8283dff48281 100644 --- a/crates/revm/src/processor.rs +++ b/crates/revm/src/processor.rs @@ -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, @@ -260,7 +260,7 @@ 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. @@ -268,7 +268,7 @@ where 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 diff --git a/crates/revm/src/stack.rs b/crates/revm/src/stack.rs new file mode 100644 index 000000000000..9d4b6b02a877 --- /dev/null +++ b/crates/revm/src/stack.rs @@ -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, + /// 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 Inspector for InspectorStack +where + DB: Database, +{ + #[inline] + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + call_inspectors!([&mut self.custom_print_tracer], |inspector| { + inspector.initialize_interp(interp, context); + }); + } + + #[inline] + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + call_inspectors!([&mut self.custom_print_tracer], |inspector| { + inspector.step(interp, context); + }); + } + + #[inline] + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + call_inspectors!([&mut self.custom_print_tracer], |inspector| { + inspector.step_end(interp, context); + }); + } + + #[inline] + fn log(&mut self, context: &mut EvmContext, log: &Log) { + call_inspectors!([&mut self.custom_print_tracer], |inspector| { + inspector.log(context, log); + }); + } + + #[inline] + fn call( + &mut self, + context: &mut EvmContext, + inputs: &mut CallInputs, + ) -> Option { + 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, + 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, + inputs: &mut CreateInputs, + ) -> Option { + 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, + 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::::selfdestruct(inspector, contract, target, value); + }); + } +}