Skip to content
24 changes: 24 additions & 0 deletions crates/inspector/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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:
Expand Down Expand Up @@ -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<ExecutionResult<Self::HaltReason>, 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.
Expand Down
88 changes: 87 additions & 1 deletion crates/inspector/src/inspect.rs
Original file line number Diff line number Diff line change
@@ -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.
///
Expand Down Expand Up @@ -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::ExecutionResult, Self::Error> {
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<Self::ExecutionResult, Self::Error>;

/// 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<ExecResultAndState<Self::ExecutionResult, Self::State>, 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<ExecResultAndState<Self::ExecutionResult, Self::State>, 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::ExecutionResult, Self::Error> {
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<ExecResultAndState<Self::ExecutionResult, Self::State>, 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))
}
}
71 changes: 70 additions & 1 deletion crates/inspector/src/inspector_tests.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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);
}
}
2 changes: 1 addition & 1 deletion crates/inspector/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down
34 changes: 31 additions & 3 deletions crates/inspector/src/mainnet_inspect.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -57,6 +58,33 @@ where
{
}

// Implementing InspectSystemCallEvm for Evm
impl<CTX, INSP, INST, PRECOMPILES> InspectSystemCallEvm
for Evm<CTX, INSP, INST, PRECOMPILES, EthFrame<EthInterpreter>>
where
CTX: ContextSetters
+ ContextTr<Journal: JournalTr<State = EvmState> + JournalExt, Tx: SystemCallTx>,
INSP: Inspector<CTX, EthInterpreter>,
INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
{
fn inspect_one_system_call_with_caller(
&mut self,
caller: Address,
system_contract_address: Address,
data: Bytes,
) -> Result<Self::ExecutionResult, Self::Error> {
// 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<CTX, INSP, I, P> InspectorEvmTr for Evm<CTX, INSP, I, P, EthFrame<EthInterpreter>>
where
Expand Down
27 changes: 26 additions & 1 deletion crates/op-revm/src/api/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -142,3 +144,26 @@ where
h.run_system_call(self)
}
}

impl<CTX, INSP, PRECOMPILE> InspectSystemCallEvm
for OpEvm<CTX, INSP, EthInstructions<EthInterpreter, CTX>, PRECOMPILE>
where
CTX: OpContextTr<Journal: JournalExt, Tx: SystemCallTx> + ContextSetters,
INSP: Inspector<CTX, EthInterpreter>,
PRECOMPILE: PrecompileProvider<CTX, Output = InterpreterResult>,
{
fn inspect_one_system_call_with_caller(
&mut self,
caller: Address,
system_contract_address: Address,
data: Bytes,
) -> Result<Self::ExecutionResult, Self::Error> {
self.0.ctx.set_tx(CTX::Tx::new_system_tx_with_caller(
caller,
system_contract_address,
data,
));
let mut h = OpHandler::<_, _, EthFrame<EthInterpreter>>::new();
h.inspect_run_system_call(self)
}
}
38 changes: 38 additions & 0 deletions crates/op-revm/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion crates/revm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Loading