use crate::{Inspector, InspectorEvmTr, JournalExt};
use context::{
result::{ExecutionResult, ResultGas},
Cfg, ContextTr, JournalEntry, JournalTr, Transaction,
};
use handler::{evm::FrameTr, EvmTr, FrameResult, Handler, ItemOrResult};
use interpreter::{
instructions::InstructionTable,
interpreter_types::{Jumps, LoopControl},
FrameInput, Host, InitialAndFloorGas, InstructionResult, Interpreter, InterpreterAction,
InterpreterTypes,
};
use state::bytecode::opcode;
pub trait InspectorHandler: Handler
where
Self::Evm:
InspectorEvmTr<Inspector: Inspector<<<Self as Handler>::Evm as EvmTr>::Context, Self::IT>>,
{
type IT: InterpreterTypes;
fn inspect_run(
&mut self,
evm: &mut Self::Evm,
) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
match self.inspect_run_without_catch_error(evm) {
Ok(output) => Ok(output),
Err(e) => self.catch_error(evm, e),
}
}
fn inspect_run_without_catch_error(
&mut self,
evm: &mut Self::Evm,
) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
let mut init_and_floor_gas = self.validate(evm)?;
let eip7702_regular_refund = self.pre_execution(evm, &mut init_and_floor_gas)? as i64;
let mut frame_result = self.inspect_execution(evm, &init_and_floor_gas)?;
let result_gas = self.post_execution(
evm,
&mut frame_result,
init_and_floor_gas,
eip7702_regular_refund,
)?;
self.execution_result(evm, frame_result, result_gas)
}
fn inspect_execution(
&mut self,
evm: &mut Self::Evm,
init_and_floor_gas: &InitialAndFloorGas,
) -> Result<FrameResult, Self::Error> {
let (gas_limit, reservoir) = init_and_floor_gas.initial_gas_and_reservoir(
evm.ctx().tx().gas_limit(),
evm.ctx().cfg().tx_gas_limit_cap(),
evm.ctx().cfg().is_amsterdam_eip8037_enabled(),
);
let first_frame_input = self.first_frame_input(evm, gas_limit, reservoir)?;
let mut frame_result = self.inspect_run_exec_loop(evm, first_frame_input)?;
self.last_frame_result(evm, &mut frame_result)?;
Ok(frame_result)
}
fn inspect_run_exec_loop(
&mut self,
evm: &mut Self::Evm,
first_frame_input: <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameInit,
) -> Result<FrameResult, Self::Error> {
let res = evm.inspect_frame_init(first_frame_input)?;
if let ItemOrResult::Result(frame_result) = res {
return Ok(frame_result);
}
loop {
let call_or_result = evm.inspect_frame_run()?;
let result = match call_or_result {
ItemOrResult::Item(init) => {
match evm.inspect_frame_init(init)? {
ItemOrResult::Item(_) => {
continue;
}
ItemOrResult::Result(result) => result,
}
}
ItemOrResult::Result(result) => result,
};
if let Some(result) = evm.frame_return_result(result)? {
return Ok(result);
}
}
}
fn inspect_run_system_call(
&mut self,
evm: &mut Self::Evm,
) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
let init_and_floor_gas = InitialAndFloorGas::new(0, 0);
match self
.inspect_execution(evm, &init_and_floor_gas)
.and_then(|exec_result| {
let gas = exec_result.gas();
let result_gas = ResultGas::default()
.with_total_gas_spent(gas.total_gas_spent())
.with_refunded(gas.refunded() as u64)
.with_state_gas_spent(gas.state_gas_spent());
self.execution_result(evm, exec_result, result_gas)
}) {
out @ Ok(_) => out,
Err(e) => self.catch_error(evm, e),
}
}
}
pub fn frame_start<CTX, INTR: InterpreterTypes>(
context: &mut CTX,
inspector: &mut impl Inspector<CTX, INTR, FrameInput, FrameResult>,
frame_input: &mut FrameInput,
) -> Option<FrameResult> {
if let Some(result) = inspector.frame_start(context, frame_input) {
return Some(result);
}
match frame_input {
FrameInput::Call(i) => {
if let Some(output) = inspector.call(context, i) {
return Some(FrameResult::Call(output));
}
}
FrameInput::Create(i) => {
if let Some(output) = inspector.create(context, i) {
return Some(FrameResult::Create(output));
}
}
FrameInput::Empty => unreachable!(),
}
None
}
pub fn frame_end<CTX, INTR: InterpreterTypes>(
context: &mut CTX,
inspector: &mut impl Inspector<CTX, INTR, FrameInput, FrameResult>,
frame_input: &FrameInput,
frame_output: &mut FrameResult,
) {
match frame_output {
FrameResult::Call(outcome) => {
let FrameInput::Call(i) = frame_input else {
panic!("FrameInput::Call expected {frame_input:?}");
};
inspector.call_end(context, i, outcome);
}
FrameResult::Create(outcome) => {
let FrameInput::Create(i) = frame_input else {
panic!("FrameInput::Create expected {frame_input:?}");
};
inspector.create_end(context, i, outcome);
}
}
inspector.frame_end(context, frame_input, frame_output);
}
pub fn inspect_instructions<CTX, IT>(
context: &mut CTX,
interpreter: &mut Interpreter<IT>,
mut inspector: impl Inspector<CTX, IT>,
instructions: &InstructionTable<IT, CTX>,
) -> InterpreterAction
where
CTX: ContextTr<Journal: JournalExt> + Host,
IT: InterpreterTypes,
{
loop {
inspector.step(interpreter, context);
if interpreter.bytecode.is_end() {
break;
}
let opcode = interpreter.bytecode.opcode();
interpreter.step(instructions, context);
if (opcode::LOG0..=opcode::LOG4).contains(&opcode) {
inspect_log(interpreter, context, &mut inspector);
}
inspector.step_end(interpreter, context);
if interpreter.bytecode.is_end() {
break;
}
}
let next_action = interpreter.take_next_action();
if let InterpreterAction::Return(result) = &next_action {
if result.result == InstructionResult::SelfDestruct {
inspect_selfdestruct(context, &mut inspector);
}
}
next_action
}
#[inline(never)]
#[cold]
fn inspect_log<CTX, IT>(
interpreter: &mut Interpreter<IT>,
context: &mut CTX,
inspector: &mut impl Inspector<CTX, IT>,
) where
CTX: ContextTr<Journal: JournalExt> + Host,
IT: InterpreterTypes,
{
if interpreter
.bytecode
.action()
.as_ref()
.is_some_and(|x| x.is_return())
{
return;
}
let log = context.journal_mut().logs().last().unwrap().clone();
inspector.log_full(interpreter, context, log);
}
#[inline(never)]
#[cold]
fn inspect_selfdestruct<CTX, IT>(context: &mut CTX, inspector: &mut impl Inspector<CTX, IT>)
where
CTX: ContextTr<Journal: JournalExt> + Host,
IT: InterpreterTypes,
{
if let Some(
JournalEntry::AccountDestroyed {
address: contract,
target: to,
had_balance: balance,
..
}
| JournalEntry::BalanceTransfer {
from: contract,
to,
balance,
..
},
) = context.journal_mut().journal().last()
{
inspector.selfdestruct(*contract, *to, *balance);
}
}