use crate::EvmTr;
use crate::{
execution, post_execution, pre_execution, validation, Frame, FrameInitOrResult, FrameOrResult,
FrameResult, ItemOrResult,
};
use context::result::{ExecutionResult, FromStringError};
use context::LocalContextTr;
use context_interface::context::ContextError;
use context_interface::ContextTr;
use context_interface::{
result::{HaltReasonTr, InvalidHeader, InvalidTransaction},
Cfg, Database, JournalTr, Transaction,
};
use interpreter::{FrameInput, Gas, InitialAndFloorGas};
use primitives::U256;
use state::EvmState;
use std::{vec, vec::Vec};
pub trait EvmTrError<EVM: EvmTr>:
From<InvalidTransaction>
+ From<InvalidHeader>
+ From<<<EVM::Context as ContextTr>::Db as Database>::Error>
+ FromStringError
{
}
impl<
EVM: EvmTr,
T: From<InvalidTransaction>
+ From<InvalidHeader>
+ From<<<EVM::Context as ContextTr>::Db as Database>::Error>
+ FromStringError,
> EvmTrError<EVM> for T
{
}
pub trait Handler {
type Evm: EvmTr<Context: ContextTr<Journal: JournalTr<State = EvmState>>>;
type Error: EvmTrError<Self::Evm>;
type Frame: Frame<
Evm = Self::Evm,
Error = Self::Error,
FrameResult = FrameResult,
FrameInit = FrameInput,
>;
type HaltReason: HaltReasonTr;
#[inline]
fn run(
&mut self,
evm: &mut Self::Evm,
) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
match self.run_without_catch_error(evm) {
Ok(output) => Ok(output),
Err(e) => self.catch_error(evm, e),
}
}
#[inline]
fn 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
.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),
}
}
#[inline]
fn 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 eip7702_refund = self.pre_execution(evm)? as i64;
let mut exec_result = self.execution(evm, &init_and_floor_gas)?;
self.post_execution(evm, &mut exec_result, init_and_floor_gas, eip7702_refund)?;
self.execution_result(evm, exec_result)
}
#[inline]
fn validate(&self, evm: &mut Self::Evm) -> Result<InitialAndFloorGas, Self::Error> {
self.validate_env(evm)?;
self.validate_initial_tx_gas(evm)
}
#[inline]
fn pre_execution(&self, evm: &mut Self::Evm) -> Result<u64, Self::Error> {
self.validate_against_state_and_deduct_caller(evm)?;
self.load_accounts(evm)?;
self.apply_eip7873_eof_initcodes(evm)?;
let gas = self.apply_eip7702_auth_list(evm)?;
Ok(gas)
}
#[inline]
fn execution(
&mut self,
evm: &mut Self::Evm,
init_and_floor_gas: &InitialAndFloorGas,
) -> Result<FrameResult, Self::Error> {
let gas_limit = evm.ctx().tx().gas_limit() - init_and_floor_gas.initial_gas;
let first_frame_input = self.first_frame_input(evm, gas_limit)?;
let first_frame = self.first_frame_init(evm, first_frame_input)?;
let mut frame_result = match first_frame {
ItemOrResult::Item(frame) => self.run_exec_loop(evm, frame)?,
ItemOrResult::Result(result) => result,
};
self.last_frame_result(evm, &mut frame_result)?;
Ok(frame_result)
}
#[inline]
fn post_execution(
&self,
evm: &mut Self::Evm,
exec_result: &mut FrameResult,
init_and_floor_gas: InitialAndFloorGas,
eip7702_gas_refund: i64,
) -> Result<(), Self::Error> {
self.refund(evm, exec_result, eip7702_gas_refund);
self.eip7623_check_gas_floor(evm, exec_result, init_and_floor_gas);
self.reimburse_caller(evm, exec_result)?;
self.reward_beneficiary(evm, exec_result)?;
Ok(())
}
#[inline]
fn validate_env(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> {
validation::validate_env(evm.ctx())
}
#[inline]
fn validate_initial_tx_gas(&self, evm: &Self::Evm) -> Result<InitialAndFloorGas, Self::Error> {
let ctx = evm.ctx_ref();
validation::validate_initial_tx_gas(ctx.tx(), ctx.cfg().spec().into()).map_err(From::from)
}
#[inline]
fn load_accounts(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> {
pre_execution::load_accounts(evm)
}
#[inline]
fn apply_eip7702_auth_list(&self, evm: &mut Self::Evm) -> Result<u64, Self::Error> {
pre_execution::apply_eip7702_auth_list(evm.ctx())
}
#[inline]
fn apply_eip7873_eof_initcodes(&self, _evm: &mut Self::Evm) -> Result<(), Self::Error> {
Ok(())
}
#[inline]
fn validate_against_state_and_deduct_caller(
&self,
evm: &mut Self::Evm,
) -> Result<(), Self::Error> {
pre_execution::validate_against_state_and_deduct_caller(evm.ctx())
}
#[inline]
fn first_frame_input(
&mut self,
evm: &mut Self::Evm,
gas_limit: u64,
) -> Result<FrameInput, Self::Error> {
let ctx: &<<Self as Handler>::Evm as EvmTr>::Context = evm.ctx_ref();
Ok(execution::create_init_frame(
ctx.tx(),
ctx.cfg().spec().into(),
gas_limit,
))
}
#[inline]
fn last_frame_result(
&mut self,
evm: &mut Self::Evm,
frame_result: &mut <Self::Frame as Frame>::FrameResult,
) -> Result<(), Self::Error> {
let instruction_result = frame_result.interpreter_result().result;
let gas = frame_result.gas_mut();
let remaining = gas.remaining();
let refunded = gas.refunded();
*gas = Gas::new_spent(evm.ctx().tx().gas_limit());
if instruction_result.is_ok_or_revert() {
gas.erase_cost(remaining);
}
if instruction_result.is_ok() {
gas.record_refund(refunded);
}
Ok(())
}
#[inline]
fn first_frame_init(
&mut self,
evm: &mut Self::Evm,
frame_input: <Self::Frame as Frame>::FrameInit,
) -> Result<FrameOrResult<Self::Frame>, Self::Error> {
Self::Frame::init_first(evm, frame_input)
}
#[inline]
fn frame_init(
&mut self,
frame: &mut Self::Frame,
evm: &mut Self::Evm,
frame_input: <Self::Frame as Frame>::FrameInit,
) -> Result<FrameOrResult<Self::Frame>, Self::Error> {
Frame::init(frame, evm, frame_input)
}
#[inline]
fn frame_call(
&mut self,
frame: &mut Self::Frame,
evm: &mut Self::Evm,
) -> Result<FrameInitOrResult<Self::Frame>, Self::Error> {
Frame::run(frame, evm)
}
#[inline]
fn frame_return_result(
&mut self,
frame: &mut Self::Frame,
evm: &mut Self::Evm,
result: <Self::Frame as Frame>::FrameResult,
) -> Result<(), Self::Error> {
Self::Frame::return_result(frame, evm, result)
}
#[inline]
fn run_exec_loop(
&mut self,
evm: &mut Self::Evm,
frame: Self::Frame,
) -> Result<FrameResult, Self::Error> {
let mut frame_stack: Vec<Self::Frame> = vec![frame];
loop {
let frame = frame_stack.last_mut().unwrap();
let call_or_result = self.frame_call(frame, evm)?;
let result = match call_or_result {
ItemOrResult::Item(init) => {
match self.frame_init(frame, evm, init)? {
ItemOrResult::Item(new_frame) => {
frame_stack.push(new_frame);
continue;
}
ItemOrResult::Result(result) => result,
}
}
ItemOrResult::Result(result) => {
frame_stack.pop();
result
}
};
let Some(frame) = frame_stack.last_mut() else {
return Ok(result);
};
self.frame_return_result(frame, evm, result)?;
}
}
#[inline]
fn eip7623_check_gas_floor(
&self,
_evm: &mut Self::Evm,
exec_result: &mut <Self::Frame as Frame>::FrameResult,
init_and_floor_gas: InitialAndFloorGas,
) {
post_execution::eip7623_check_gas_floor(exec_result.gas_mut(), init_and_floor_gas)
}
#[inline]
fn refund(
&self,
evm: &mut Self::Evm,
exec_result: &mut <Self::Frame as Frame>::FrameResult,
eip7702_refund: i64,
) {
let spec = evm.ctx().cfg().spec().into();
post_execution::refund(spec, exec_result.gas_mut(), eip7702_refund)
}
#[inline]
fn reimburse_caller(
&self,
evm: &mut Self::Evm,
exec_result: &mut <Self::Frame as Frame>::FrameResult,
) -> Result<(), Self::Error> {
post_execution::reimburse_caller(evm.ctx(), exec_result.gas_mut(), U256::ZERO)
.map_err(From::from)
}
#[inline]
fn reward_beneficiary(
&self,
evm: &mut Self::Evm,
exec_result: &mut <Self::Frame as Frame>::FrameResult,
) -> Result<(), Self::Error> {
post_execution::reward_beneficiary(evm.ctx(), exec_result.gas_mut()).map_err(From::from)
}
#[inline]
fn execution_result(
&mut self,
evm: &mut Self::Evm,
result: <Self::Frame as Frame>::FrameResult,
) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
match core::mem::replace(evm.ctx().error(), Ok(())) {
Err(ContextError::Db(e)) => return Err(e.into()),
Err(ContextError::Custom(e)) => return Err(Self::Error::from_string(e)),
Ok(_) => (),
}
let exec_result = post_execution::output(evm.ctx(), result);
evm.ctx().journal_mut().commit_tx();
evm.ctx().local_mut().clear();
Ok(exec_result)
}
#[inline]
fn catch_error(
&self,
evm: &mut Self::Evm,
error: Self::Error,
) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
evm.ctx().local_mut().clear();
evm.ctx().journal_mut().discard_tx();
Err(error)
}
}