use crate::{
evm::FrameTr,
execution,
post_execution::{self, build_result_gas},
pre_execution::{self, apply_eip7702_auth_list},
validation, EvmTr, FrameResult, ItemOrResult,
};
use context::{
result::{ExecutionResult, FromStringError},
LocalContextTr,
};
use context_interface::{
context::{take_error, ContextError},
result::{HaltReasonTr, InvalidHeader, InvalidTransaction, ResultGas},
Cfg, ContextTr, Database, JournalTr, Transaction,
};
use interpreter::{interpreter_action::FrameInit, Gas, InitialAndFloorGas, SharedMemory};
use primitives::U256;
pub trait EvmTrError<EVM: EvmTr>:
From<InvalidTransaction>
+ From<InvalidHeader>
+ From<<<EVM::Context as ContextTr>::Db as Database>::Error>
+ From<ContextError<<<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>
+ From<ContextError<<<EVM::Context as ContextTr>::Db as Database>::Error>>
+ FromStringError,
> EvmTrError<EVM> for T
{
}
pub trait Handler {
type Evm: EvmTr<
Context: ContextTr<Journal: JournalTr, Local: LocalContextTr>,
Frame: FrameTr<FrameInit = FrameInit, FrameResult = FrameResult>,
>;
type Error: EvmTrError<Self::Evm>;
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| {
let gas = exec_result.gas();
let result_gas = build_result_gas(gas, init_and_floor_gas);
self.execution_result(evm, exec_result, result_gas)
}) {
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 mut init_and_floor_gas = self.validate(evm)?;
let eip7702_refund = self.pre_execution(evm, &mut init_and_floor_gas)?;
let eip7702_regular_refund = eip7702_refund as i64;
let mut exec_result = self.execution(evm, &init_and_floor_gas)?;
let result_gas = self.post_execution(
evm,
&mut exec_result,
init_and_floor_gas,
eip7702_regular_refund,
)?;
self.execution_result(evm, exec_result, result_gas)
}
#[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,
init_and_floor_gas: &mut InitialAndFloorGas,
) -> Result<u64, Self::Error> {
self.validate_against_state_and_deduct_caller(evm, init_and_floor_gas)?;
self.load_accounts(evm)?;
let gas = self.apply_eip7702_auth_list(evm, init_and_floor_gas)?;
Ok(gas)
}
#[inline]
fn 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.run_exec_loop(evm, first_frame_input)?;
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<ResultGas, Self::Error> {
self.refund(evm, exec_result, eip7702_gas_refund);
let result_gas = post_execution::build_result_gas(exec_result.gas(), init_and_floor_gas);
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(result_gas)
}
#[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: &mut Self::Evm,
) -> Result<InitialAndFloorGas, Self::Error> {
let ctx = evm.ctx_ref();
let gas = validation::validate_initial_tx_gas(
ctx.tx(),
ctx.cfg().spec().into(),
ctx.cfg().is_eip7623_disabled(),
ctx.cfg().is_amsterdam_eip8037_enabled(),
ctx.cfg().tx_gas_limit_cap(),
)?;
Ok(gas)
}
#[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,
init_and_floor_gas: &mut InitialAndFloorGas,
) -> Result<u64, Self::Error> {
apply_eip7702_auth_list(evm.ctx_mut(), init_and_floor_gas)
}
#[inline]
fn validate_against_state_and_deduct_caller(
&self,
evm: &mut Self::Evm,
_init_and_floor_gas: &mut InitialAndFloorGas,
) -> 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,
reservoir: u64,
) -> Result<FrameInit, Self::Error> {
let ctx = evm.ctx_mut();
let mut memory = SharedMemory::new_with_buffer(ctx.local().shared_memory_buffer().clone());
memory.set_memory_limit(ctx.cfg().memory_limit());
let frame_input = execution::create_init_frame(ctx, gas_limit, reservoir)?;
Ok(FrameInit {
depth: 0,
memory,
frame_input,
})
}
#[inline]
fn last_frame_result(
&mut self,
evm: &mut Self::Evm,
frame_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::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();
let reservoir = gas.reservoir();
let state_gas_spent = gas.state_gas_spent();
*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);
}
if instruction_result.is_ok() {
gas.set_state_gas_spent(state_gas_spent);
gas.set_reservoir(reservoir);
} else {
gas.set_state_gas_spent(0);
gas.set_reservoir(reservoir + state_gas_spent);
}
Ok(())
}
#[inline]
fn 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.frame_init(first_frame_input)?;
if let ItemOrResult::Result(frame_result) = res {
return Ok(frame_result);
}
loop {
let call_or_result = evm.frame_run()?;
let result = match call_or_result {
ItemOrResult::Item(init) => {
match evm.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);
}
}
}
#[inline]
fn eip7623_check_gas_floor(
&self,
_evm: &mut Self::Evm,
exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::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::Evm as EvmTr>::Frame as FrameTr>::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::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
) -> Result<(), Self::Error> {
post_execution::reimburse_caller(evm.ctx(), exec_result.gas(), U256::ZERO)
.map_err(From::from)
}
#[inline]
fn reward_beneficiary(
&self,
evm: &mut Self::Evm,
exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
) -> Result<(), Self::Error> {
post_execution::reward_beneficiary(evm.ctx(), exec_result.gas()).map_err(From::from)
}
#[inline]
fn execution_result(
&mut self,
evm: &mut Self::Evm,
result: <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
result_gas: ResultGas,
) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
take_error::<Self::Error, _>(evm.ctx().error())?;
let exec_result = post_execution::output(evm.ctx(), result, result_gas);
evm.ctx().journal_mut().commit_tx();
evm.ctx().local_mut().clear();
evm.frame_stack().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();
evm.frame_stack().clear();
Err(error)
}
}