diff --git a/crates/inspector/src/handler.rs b/crates/inspector/src/handler.rs index 10b07570ac..aad34460cc 100644 --- a/crates/inspector/src/handler.rs +++ b/crates/inspector/src/handler.rs @@ -13,6 +13,8 @@ use state::bytecode::opcode; /// /// Similar how [`Handler::run`] method serves as the entry point, /// [`InspectorHandler::inspect_run`] method serves as the entry point for inspection. +/// For system calls, [`InspectorHandler::inspect_run_system_call`] provides inspection +/// support similar to [`Handler::run_system_call`]. /// /// Notice that when inspection is run it skips few functions from handler, this can be /// a problem if custom EVM is implemented and some of skipped functions have changed logic. @@ -24,6 +26,7 @@ use state::bytecode::opcode; /// * [`Handler::execution`] replaced with [`InspectorHandler::inspect_execution`] /// * [`Handler::run_exec_loop`] replaced with [`InspectorHandler::inspect_run_exec_loop`] /// * `run_exec_loop` calls `inspect_frame_init` and `inspect_frame_run` that call inspector inside. +/// * [`Handler::run_system_call`] replaced with [`InspectorHandler::inspect_run_system_call`] pub trait InspectorHandler: Handler where Self::Evm: @@ -121,6 +124,27 @@ where } } } + + /// Run system call with inspection support. + /// + /// This method acts as [`Handler::run_system_call`] method for inspection. + /// Similar to [`InspectorHandler::inspect_run`] but skips validation and pre-execution phases, + /// going directly to execution with inspection support. + fn inspect_run_system_call( + &mut self, + evm: &mut Self::Evm, + ) -> Result, Self::Error> { + // dummy values that are not used. + let init_and_floor_gas = InitialAndFloorGas::new(0, 0); + // call execution with inspection and then output. + match self + .inspect_execution(evm, &init_and_floor_gas) + .and_then(|exec_result| self.execution_result(evm, exec_result)) + { + out @ Ok(_) => out, + Err(e) => self.catch_error(evm, e), + } + } } /// Handles the start of a frame by calling the appropriate inspector method. diff --git a/crates/inspector/src/inspect.rs b/crates/inspector/src/inspect.rs index 5ac1ada4a6..79beb2c6b8 100644 --- a/crates/inspector/src/inspect.rs +++ b/crates/inspector/src/inspect.rs @@ -1,5 +1,6 @@ use context::result::ExecResultAndState; -use handler::{ExecuteCommitEvm, ExecuteEvm}; +use handler::{system_call::SYSTEM_ADDRESS, ExecuteCommitEvm, ExecuteEvm, SystemCallEvm}; +use primitives::{Address, Bytes}; /// InspectEvm is a API that allows inspecting the EVM. /// @@ -76,3 +77,88 @@ pub trait InspectCommitEvm: InspectEvm + ExecuteCommitEvm { Ok(output) } } + +/// InspectSystemCallEvm is an API that allows inspecting system calls in the EVM. +/// +/// It extends [`InspectEvm`] and [`SystemCallEvm`] traits to provide inspection +/// capabilities for system transactions, enabling tracing and debugging of +/// system calls similar to regular transactions. +pub trait InspectSystemCallEvm: InspectEvm + SystemCallEvm { + /// Inspect a system call with the current inspector. + /// + /// Similar to [`InspectEvm::inspect_one_tx`] but for system calls. + /// Uses [`SYSTEM_ADDRESS`] as the caller. + fn inspect_one_system_call( + &mut self, + system_contract_address: Address, + data: Bytes, + ) -> Result { + self.inspect_one_system_call_with_caller(SYSTEM_ADDRESS, system_contract_address, data) + } + + /// Inspect a system call with the current inspector and a custom caller. + /// + /// Similar to [`InspectEvm::inspect_one_tx`] but for system calls with a custom caller. + fn inspect_one_system_call_with_caller( + &mut self, + caller: Address, + system_contract_address: Address, + data: Bytes, + ) -> Result; + + /// Inspect a system call and finalize the state. + /// + /// Similar to [`InspectEvm::inspect_tx`] but for system calls. + fn inspect_system_call( + &mut self, + system_contract_address: Address, + data: Bytes, + ) -> Result, Self::Error> { + let output = self.inspect_one_system_call(system_contract_address, data)?; + let state = self.finalize(); + Ok(ExecResultAndState::new(output, state)) + } + + /// Inspect a system call with a custom caller and finalize the state. + /// + /// Similar to [`InspectEvm::inspect_tx`] but for system calls with a custom caller. + fn inspect_system_call_with_caller( + &mut self, + caller: Address, + system_contract_address: Address, + data: Bytes, + ) -> Result, Self::Error> { + let output = + self.inspect_one_system_call_with_caller(caller, system_contract_address, data)?; + let state = self.finalize(); + Ok(ExecResultAndState::new(output, state)) + } + + /// Inspect a system call with a given inspector. + /// + /// Similar to [`InspectEvm::inspect_one`] but for system calls. + fn inspect_one_system_call_with_inspector( + &mut self, + system_contract_address: Address, + data: Bytes, + inspector: Self::Inspector, + ) -> Result { + self.set_inspector(inspector); + self.inspect_one_system_call(system_contract_address, data) + } + + /// Inspect a system call with a given inspector and finalize the state. + /// + /// Similar to [`InspectEvm::inspect`] but for system calls. + fn inspect_system_call_with_inspector( + &mut self, + system_contract_address: Address, + data: Bytes, + inspector: Self::Inspector, + ) -> Result, Self::Error> { + let output = + self.inspect_one_system_call_with_inspector(system_contract_address, data, inspector)?; + let state = self.finalize(); + Ok(ExecResultAndState::new(output, state)) + } +} diff --git a/crates/inspector/src/inspector_tests.rs b/crates/inspector/src/inspector_tests.rs index 29cfa54c94..5916a75c86 100644 --- a/crates/inspector/src/inspector_tests.rs +++ b/crates/inspector/src/inspector_tests.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod tests { - use crate::{InspectEvm, Inspector}; + use crate::{InspectEvm, InspectSystemCallEvm, Inspector}; use context::{Context, TxEnv}; use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}; use handler::{MainBuilder, MainContext}; @@ -731,4 +731,73 @@ mod tests { "Should have jumped to JUMPDEST" ); } + + #[test] + fn test_system_call_inspection_basic() { + // PUSH1 0x42, SSTORE, STOP + let code = Bytes::from(vec![ + opcode::PUSH1, + 0x42, + opcode::PUSH1, + 0x00, + opcode::SSTORE, + opcode::STOP, + ]); + + let bytecode = Bytecode::new_raw(code); + let ctx = Context::mainnet().with_db(BenchmarkDB::new_bytecode(bytecode)); + let mut evm = ctx.build_mainnet_with_inspector(TestInspector::new()); + + let result = evm + .inspect_system_call(BENCH_TARGET, Bytes::default()) + .unwrap(); + + assert!(result.result.is_success()); + assert!(evm.inspector.get_step_count() > 0); + assert!(!result.state.is_empty()); + } + + #[test] + fn test_system_call_inspection_api_variants() { + let code = vec![ + opcode::CALLER, + opcode::PUSH1, + 0x00, + opcode::MSTORE, + opcode::PUSH1, + 0x20, + opcode::PUSH1, + 0x00, + opcode::RETURN, + ]; + + let bytecode = Bytecode::new_raw(Bytes::from(code)); + let ctx = Context::mainnet().with_db(BenchmarkDB::new_bytecode(bytecode)); + let mut evm = ctx.build_mainnet_with_inspector(TestInspector::new()); + + // Test inspect_one_system_call + let result = evm + .inspect_one_system_call(BENCH_TARGET, Bytes::default()) + .unwrap(); + assert!(result.is_success()); + + // Test inspect_one_system_call_with_caller + let custom_caller = address!("0x1234567890123456789012345678901234567890"); + let result = evm + .inspect_one_system_call_with_caller(custom_caller, BENCH_TARGET, Bytes::default()) + .unwrap(); + assert!(result.is_success()); + + // Test inspect_one_system_call_with_inspector + let result = evm + .inspect_one_system_call_with_inspector( + BENCH_TARGET, + Bytes::default(), + TestInspector::new(), + ) + .unwrap(); + assert!(result.is_success()); + + assert!(evm.inspector.get_step_count() > 0); + } } diff --git a/crates/inspector/src/lib.rs b/crates/inspector/src/lib.rs index 26998967f6..93072de4bd 100644 --- a/crates/inspector/src/lib.rs +++ b/crates/inspector/src/lib.rs @@ -30,7 +30,7 @@ pub mod inspectors { pub use count_inspector::CountInspector; pub use handler::{inspect_instructions, InspectorHandler}; -pub use inspect::{InspectCommitEvm, InspectEvm}; +pub use inspect::{InspectCommitEvm, InspectEvm, InspectSystemCallEvm}; pub use inspector::*; pub use noop::NoOpInspector; pub use traits::*; diff --git a/crates/inspector/src/mainnet_inspect.rs b/crates/inspector/src/mainnet_inspect.rs index 33afea5512..6b78bf7440 100644 --- a/crates/inspector/src/mainnet_inspect.rs +++ b/crates/inspector/src/mainnet_inspect.rs @@ -1,14 +1,15 @@ use crate::{ - inspect::{InspectCommitEvm, InspectEvm}, + inspect::{InspectCommitEvm, InspectEvm, InspectSystemCallEvm}, Inspector, InspectorEvmTr, InspectorHandler, JournalExt, }; use context::{ContextSetters, ContextTr, Evm, JournalTr}; use database_interface::DatabaseCommit; use handler::{ - instructions::InstructionProvider, EthFrame, EvmTr, EvmTrError, Handler, MainnetHandler, - PrecompileProvider, + instructions::InstructionProvider, system_call::SystemCallTx, EthFrame, EvmTr, EvmTrError, + Handler, MainnetHandler, PrecompileProvider, }; use interpreter::{interpreter::EthInterpreter, InterpreterResult}; +use primitives::{Address, Bytes}; use state::EvmState; // Implementing InspectorHandler for MainnetHandler. @@ -57,6 +58,33 @@ where { } +// Implementing InspectSystemCallEvm for Evm +impl InspectSystemCallEvm + for Evm> +where + CTX: ContextSetters + + ContextTr + JournalExt, Tx: SystemCallTx>, + INSP: Inspector, + INST: InstructionProvider, + PRECOMPILES: PrecompileProvider, +{ + fn inspect_one_system_call_with_caller( + &mut self, + caller: Address, + system_contract_address: Address, + data: Bytes, + ) -> Result { + // Set system call transaction fields similar to transact_system_call_with_caller + self.set_tx(CTX::Tx::new_system_tx_with_caller( + caller, + system_contract_address, + data, + )); + // Use inspect_run_system_call instead of run_system_call for inspection + MainnetHandler::default().inspect_run_system_call(self) + } +} + // Implementing InspectorEvmTr for Evm impl InspectorEvmTr for Evm> where diff --git a/crates/op-revm/src/api/exec.rs b/crates/op-revm/src/api/exec.rs index 2e8f24b232..204973509e 100644 --- a/crates/op-revm/src/api/exec.rs +++ b/crates/op-revm/src/api/exec.rs @@ -13,7 +13,9 @@ use revm::{ instructions::EthInstructions, system_call::SystemCallEvm, EthFrame, Handler, PrecompileProvider, SystemCallTx, }, - inspector::{InspectCommitEvm, InspectEvm, Inspector, InspectorHandler, JournalExt}, + inspector::{ + InspectCommitEvm, InspectEvm, InspectSystemCallEvm, Inspector, InspectorHandler, JournalExt, + }, interpreter::{interpreter::EthInterpreter, InterpreterResult}, primitives::{Address, Bytes}, state::EvmState, @@ -142,3 +144,26 @@ where h.run_system_call(self) } } + +impl InspectSystemCallEvm + for OpEvm, PRECOMPILE> +where + CTX: OpContextTr + ContextSetters, + INSP: Inspector, + PRECOMPILE: PrecompileProvider, +{ + fn inspect_one_system_call_with_caller( + &mut self, + caller: Address, + system_contract_address: Address, + data: Bytes, + ) -> Result { + self.0.ctx.set_tx(CTX::Tx::new_system_tx_with_caller( + caller, + system_contract_address, + data, + )); + let mut h = OpHandler::<_, _, EthFrame>::new(); + h.inspect_run_system_call(self) + } +} diff --git a/crates/op-revm/tests/integration.rs b/crates/op-revm/tests/integration.rs index 088bf5a8cc..52bd9a976a 100644 --- a/crates/op-revm/tests/integration.rs +++ b/crates/op-revm/tests/integration.rs @@ -1075,6 +1075,44 @@ fn test_log_inspector() { compare_or_save_testdata("test_log_inspector.json", &output); } +#[test] +fn test_system_call_inspection() { + use revm::InspectSystemCallEvm; + + let ctx = Context::op(); + + let mut evm = ctx.build_op_with_inspector(LogInspector::default()); + + // Test system call inspection + let result = evm + .inspect_one_system_call(BENCH_TARGET, Bytes::default()) + .unwrap(); + + // Should succeed + assert!(result.is_success()); + + // Test system call inspection with caller + let custom_caller = Address::from([0x12; 20]); + let result = evm + .inspect_one_system_call_with_caller(custom_caller, BENCH_TARGET, Bytes::default()) + .unwrap(); + + // Should also succeed + assert!(result.is_success()); + + // Test system call inspection with inspector + let result = evm + .inspect_one_system_call_with_inspector( + BENCH_TARGET, + Bytes::default(), + LogInspector::default(), + ) + .unwrap(); + + // Should succeed + assert!(result.is_success()); +} + #[test] fn test_system_call() { let ctx = Context::op(); diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 6f4df789fc..759af925ae 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -35,5 +35,5 @@ pub use handler::{ ExecuteCommitEvm, ExecuteEvm, MainBuilder, MainContext, MainnetEvm, SystemCallCommitEvm, SystemCallEvm, }; -pub use inspector::{InspectCommitEvm, InspectEvm, Inspector}; +pub use inspector::{InspectCommitEvm, InspectEvm, InspectSystemCallEvm, Inspector}; pub use precompile::install_crypto;