use crate::{Inspector, InspectorEvmTr, JournalExt};
use context::journaled_state::JournalCheckpoint;
use context::{result::ExecutionResult, ContextTr, JournalEntry, JournalTr};
use handler::{
evm::FrameTr, execution::runtime_oog_unwind, post_execution::build_result_gas, EvmTr,
FrameResult, Handler, ItemOrResult,
};
use interpreter::{
instructions::{GasTable, InstructionTable},
interpreter_types::LoopControl,
FrameInput, GasTracker, Host, InitialAndFloorGas, InstructionResult, Interpreter,
InterpreterAction, InterpreterTypes,
};
use primitives::hints_util::cold_path;
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 init_and_floor_gas = self.validate(evm)?;
let mut gas = self.tx_gas(evm, &init_and_floor_gas);
let pre_execution = self.pre_execution(evm, &mut gas)?;
let mut refund = 0;
let mut exec_result = None;
if let Some(pre_execution) = pre_execution {
refund = pre_execution.eip7702_refund as i64;
exec_result = self.inspect_execution(evm, pre_execution.checkpoint, &mut gas)?;
}
let mut frame_result = match exec_result {
Some(exec_result) => exec_result,
None => self.runtime_oog_result(evm, &init_and_floor_gas, &mut gas)?,
};
let result_gas = self.post_execution(evm, &mut frame_result, init_and_floor_gas, refund)?;
self.execution_result(evm, frame_result, result_gas)
}
fn inspect_execution(
&mut self,
evm: &mut Self::Evm,
checkpoint: JournalCheckpoint,
gas: &mut GasTracker,
) -> Result<Option<FrameResult>, Self::Error> {
let Some(first_frame_input) = self.first_frame_input(evm, gas)? else {
runtime_oog_unwind(evm.ctx(), checkpoint)?;
return Ok(None);
};
evm.ctx().journal_mut().checkpoint_commit();
let mut frame_result = self.inspect_run_exec_loop(evm, first_frame_input)?;
self.last_frame_result(evm, &mut frame_result, gas)?;
Ok(Some(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);
let mut gas = self.tx_gas(evm, &init_and_floor_gas);
let checkpoint = evm.ctx().journal_mut().checkpoint();
match self
.inspect_execution(evm, checkpoint, &mut gas)
.and_then(|exec_result| {
let exec_result = match exec_result {
Some(exec_result) => exec_result,
None => self.runtime_oog_result(evm, &init_and_floor_gas, &mut gas)?,
};
let gas = exec_result.gas();
let result_gas = build_result_gas(false, gas, init_and_floor_gas);
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>,
gas_table: &GasTable,
) -> InterpreterAction
where
CTX: ContextTr<Journal: JournalExt> + Host,
IT: InterpreterTypes,
{
let mut instruction_journal_i = None;
loop {
inspector.step(interpreter, context);
if interpreter.bytecode.is_end() {
cold_path();
break;
}
instruction_journal_i = Some(context.journal().journal().len());
let logs_i = context.journal().logs().len();
if let Err(e) = interpreter.step(instructions, gas_table, context) {
cold_path();
if interpreter.bytecode.action().is_none() {
interpreter.halt(e);
}
}
if context.journal().logs().len() != logs_i {
cold_path();
inspect_logs(Some(interpreter), context, &mut inspector, logs_i);
}
inspector.step_end(interpreter, context);
if interpreter.bytecode.is_end() {
cold_path();
break;
}
}
let next_action = interpreter.take_next_action();
if let InterpreterAction::Return(result) = &next_action {
if result.result == InstructionResult::SelfDestruct {
if let Some(journal_i) = instruction_journal_i {
inspect_selfdestruct(context, &mut inspector, journal_i);
}
}
}
next_action
}
#[inline(never)]
#[cold]
pub(crate) fn inspect_logs<CTX, IT>(
interpreter: Option<&mut Interpreter<IT>>,
context: &mut CTX,
inspector: &mut impl Inspector<CTX, IT>,
logs_i: usize,
) where
CTX: ContextTr<Journal: JournalExt>,
IT: InterpreterTypes,
{
let logs = context.journal_mut().logs()[logs_i..].to_vec();
match interpreter {
Some(interpreter) => {
for log in logs {
inspector.log_full(interpreter, context, log);
}
}
None => {
for log in logs {
inspector.log(context, log);
}
}
}
}
#[inline(never)]
#[cold]
fn inspect_selfdestruct<CTX, IT>(
context: &mut CTX,
inspector: &mut impl Inspector<CTX, IT>,
journal_i: usize,
) where
CTX: ContextTr<Journal: JournalExt> + Host,
IT: InterpreterTypes,
{
let entry = context
.journal_mut()
.journal()
.get(journal_i..)
.and_then(|entries| entries.last());
if let Some(
JournalEntry::AccountDestroyed {
address: contract,
target: to,
had_balance: balance,
..
}
| JournalEntry::BalanceTransfer {
from: contract,
to,
balance,
..
},
) = entry
{
inspector.selfdestruct(*contract, *to, *balance);
}
}