use core::cmp::min;
use crate::{
constants::{self},
ExternalEnvTypes, HostExt, JournalInspectTr, MegaContext, MegaSpecId,
};
use alloy_evm::Database;
use alloy_primitives::{keccak256, Bytes, U256};
use revm::{
context::ContextTr,
handler::instructions::{EthInstructions, InstructionProvider},
interpreter::{
as_usize_or_fail, gas, gas_or_fail,
instructions::{self, control, utility::IntoAddress},
interpreter::EthInterpreter,
interpreter_types::{InputsTr, LoopControl, MemoryTr, RuntimeFlag},
resize_memory, CallScheme, FrameInput, Instruction, InstructionContext, InstructionResult,
InstructionTable, InterpreterAction, InterpreterTypes, SStoreResult, Stack,
},
primitives::KECCAK_EMPTY,
};
#[derive(Clone)]
pub struct MegaInstructions<DB: Database, ExtEnvs: ExternalEnvTypes> {
spec: MegaSpecId,
inner: EthInstructions<EthInterpreter, MegaContext<DB, ExtEnvs>>,
}
impl<DB: Database, ExtEnvs: ExternalEnvTypes> core::fmt::Debug for MegaInstructions<DB, ExtEnvs> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("MegaethInstructions").field("spec", &self.spec).finish_non_exhaustive()
}
}
impl<DB: Database, ExtEnvs: ExternalEnvTypes> MegaInstructions<DB, ExtEnvs> {
pub fn new(spec: MegaSpecId) -> Self {
let instruction_table = match spec {
MegaSpecId::EQUIVALENCE => EthInstructions::new_mainnet(),
MegaSpecId::MINI_REX => EthInstructions::new(mini_rex::instruction_table::<
EthInterpreter,
MegaContext<DB, ExtEnvs>,
>()),
MegaSpecId::REX | MegaSpecId::REX1 => EthInstructions::new(rex::instruction_table::<
EthInterpreter,
MegaContext<DB, ExtEnvs>,
>()),
MegaSpecId::REX2 => EthInstructions::new(rex2::instruction_table::<
EthInterpreter,
MegaContext<DB, ExtEnvs>,
>()),
MegaSpecId::REX3 => EthInstructions::new(rex3::instruction_table::<
EthInterpreter,
MegaContext<DB, ExtEnvs>,
>()),
MegaSpecId::REX4 => EthInstructions::new(rex4::instruction_table::<
EthInterpreter,
MegaContext<DB, ExtEnvs>,
>()),
MegaSpecId::REX5 => EthInstructions::new(rex5::instruction_table::<
EthInterpreter,
MegaContext<DB, ExtEnvs>,
>()),
};
Self { spec, inner: instruction_table }
}
}
impl<DB: Database, ExtEnvs: ExternalEnvTypes> InstructionProvider
for MegaInstructions<DB, ExtEnvs>
{
type Context = MegaContext<DB, ExtEnvs>;
type InterpreterTypes = EthInterpreter;
fn instruction_table(&self) -> &InstructionTable<Self::InterpreterTypes, Self::Context> {
self.inner.instruction_table()
}
}
mod rex {
use super::*;
pub(super) const fn instruction_table<
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ContextTr + JournalInspectTr + ?Sized,
>() -> [Instruction<WIRE, H>; 256]
where
WIRE::Stack: StackInspectTr,
{
use revm::bytecode::opcode::*;
let mut table = mini_rex::instruction_table::<WIRE, H>();
table[CALLCODE as usize] = forward_gas_ext::call_code;
table[DELEGATECALL as usize] = forward_gas_ext::delegate_call;
table[STATICCALL as usize] = forward_gas_ext::static_call;
table
}
}
mod rex2 {
use super::*;
pub(super) const fn instruction_table<
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ContextTr + JournalInspectTr + ?Sized,
>() -> [Instruction<WIRE, H>; 256]
where
WIRE::Stack: StackInspectTr,
{
use revm::bytecode::opcode::*;
let mut table = rex::instruction_table::<WIRE, H>();
table[SELFDESTRUCT as usize] = compute_gas_ext::selfdestruct;
table
}
}
mod rex3 {
use super::*;
pub(super) const fn instruction_table<
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ContextTr + JournalInspectTr + ?Sized,
>() -> [Instruction<WIRE, H>; 256]
where
WIRE::Stack: StackInspectTr,
{
use revm::bytecode::opcode::*;
let mut table = rex2::instruction_table::<WIRE, H>();
table[SLOAD as usize] = volatile_data_ext::sload;
table
}
}
mod rex4 {
use super::*;
pub(super) const fn instruction_table<
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ContextTr + JournalInspectTr + ?Sized,
>() -> [Instruction<WIRE, H>; 256]
where
WIRE::Stack: StackInspectTr,
{
use revm::bytecode::opcode::*;
let mut table = rex3::instruction_table::<WIRE, H>();
table[CALL as usize] = volatile_data_ext::call;
table[STATICCALL as usize] = volatile_data_ext::static_call;
table[DELEGATECALL as usize] = volatile_data_ext::delegate_call;
table[CALLCODE as usize] = volatile_data_ext::call_code;
table[SELFDESTRUCT as usize] = volatile_data_ext::selfdestruct;
table[SELFBALANCE as usize] = volatile_data_ext::selfbalance;
table
}
}
mod rex5 {
use super::*;
pub(super) const fn instruction_table<
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ContextTr + JournalInspectTr + ?Sized,
>() -> [Instruction<WIRE, H>; 256]
where
WIRE::Stack: StackInspectTr,
{
use revm::bytecode::opcode::*;
let mut table = rex4::instruction_table::<WIRE, H>();
table[SELFDESTRUCT as usize] = volatile_data_ext::selfdestruct_rex5;
table
}
}
macro_rules! compute_gas {
($interpreter:expr, $additional_limit:expr, $gas_used:expr $(,$ret:expr)?) => {
if !$additional_limit.record_compute_gas($gas_used) {
$interpreter.halt($additional_limit.exceeding_instruction_result());
return $($ret)?;
}
};
}
macro_rules! run_inner_instruction_or_abort {
($inner_fn:path, $context:expr) => {
let ctx = InstructionContext::<'_, H, WIRE> {
interpreter: &mut *$context.interpreter,
host: &mut *$context.host,
};
$inner_fn(ctx);
if $context
.interpreter
.bytecode
.instruction_result()
.is_some_and(|result| result.is_error())
{
return;
}
};
}
mod mini_rex {
use super::*;
pub(super) const fn instruction_table<
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ContextTr + JournalInspectTr + ?Sized,
>() -> [Instruction<WIRE, H>; 256] {
use revm::bytecode::opcode::*;
let mut table = [control::unknown as Instruction<WIRE, H>; 256];
table[STOP as usize] = compute_gas_ext::stop;
table[ADD as usize] = compute_gas_ext::add;
table[MUL as usize] = compute_gas_ext::mul;
table[SUB as usize] = compute_gas_ext::sub;
table[DIV as usize] = compute_gas_ext::div;
table[SDIV as usize] = compute_gas_ext::sdiv;
table[MOD as usize] = compute_gas_ext::rem;
table[SMOD as usize] = compute_gas_ext::smod;
table[ADDMOD as usize] = compute_gas_ext::addmod;
table[MULMOD as usize] = compute_gas_ext::mulmod;
table[EXP as usize] = compute_gas_ext::exp;
table[SIGNEXTEND as usize] = compute_gas_ext::signextend;
table[LT as usize] = compute_gas_ext::lt;
table[GT as usize] = compute_gas_ext::gt;
table[SLT as usize] = compute_gas_ext::slt;
table[SGT as usize] = compute_gas_ext::sgt;
table[EQ as usize] = compute_gas_ext::eq;
table[ISZERO as usize] = compute_gas_ext::iszero;
table[AND as usize] = compute_gas_ext::bitand;
table[OR as usize] = compute_gas_ext::bitor;
table[XOR as usize] = compute_gas_ext::bitxor;
table[NOT as usize] = compute_gas_ext::not;
table[BYTE as usize] = compute_gas_ext::byte;
table[SHL as usize] = compute_gas_ext::shl;
table[SHR as usize] = compute_gas_ext::shr;
table[SAR as usize] = compute_gas_ext::sar;
table[CLZ as usize] = compute_gas_ext::clz;
table[KECCAK256 as usize] = compute_gas_ext::keccak256;
table[ADDRESS as usize] = compute_gas_ext::address;
table[BALANCE as usize] = volatile_data_ext::balance;
table[ORIGIN as usize] = compute_gas_ext::origin;
table[CALLER as usize] = compute_gas_ext::caller;
table[CALLVALUE as usize] = compute_gas_ext::callvalue;
table[CALLDATALOAD as usize] = compute_gas_ext::calldataload;
table[CALLDATASIZE as usize] = compute_gas_ext::calldatasize;
table[CALLDATACOPY as usize] = compute_gas_ext::calldatacopy;
table[CODESIZE as usize] = compute_gas_ext::codesize;
table[CODECOPY as usize] = compute_gas_ext::codecopy;
table[GASPRICE as usize] = compute_gas_ext::gasprice;
table[EXTCODESIZE as usize] = volatile_data_ext::extcodesize;
table[EXTCODECOPY as usize] = volatile_data_ext::extcodecopy;
table[EXTCODEHASH as usize] = volatile_data_ext::extcodehash;
table[RETURNDATASIZE as usize] = compute_gas_ext::returndatasize;
table[RETURNDATACOPY as usize] = compute_gas_ext::returndatacopy;
table[BLOCKHASH as usize] = volatile_data_ext::blockhash;
table[COINBASE as usize] = volatile_data_ext::coinbase;
table[TIMESTAMP as usize] = volatile_data_ext::timestamp;
table[NUMBER as usize] = volatile_data_ext::block_number;
table[DIFFICULTY as usize] = volatile_data_ext::difficulty;
table[GASLIMIT as usize] = volatile_data_ext::gas_limit_opcode;
table[CHAINID as usize] = compute_gas_ext::chainid;
table[SELFBALANCE as usize] = compute_gas_ext::selfbalance;
table[BASEFEE as usize] = volatile_data_ext::basefee;
table[BLOBBASEFEE as usize] = volatile_data_ext::blobbasefee;
table[BLOBHASH as usize] = volatile_data_ext::blobhash;
table[POP as usize] = compute_gas_ext::pop;
table[MLOAD as usize] = compute_gas_ext::mload;
table[MSTORE as usize] = compute_gas_ext::mstore;
table[MSTORE8 as usize] = compute_gas_ext::mstore8;
table[SLOAD as usize] = compute_gas_ext::sload;
table[SSTORE as usize] = additional_limit_ext::sstore;
table[JUMP as usize] = compute_gas_ext::jump;
table[JUMPI as usize] = compute_gas_ext::jumpi;
table[PC as usize] = compute_gas_ext::pc;
table[MSIZE as usize] = compute_gas_ext::msize;
table[GAS as usize] = compute_gas_ext::gas;
table[JUMPDEST as usize] = compute_gas_ext::jumpdest;
table[TLOAD as usize] = compute_gas_ext::tload;
table[TSTORE as usize] = compute_gas_ext::tstore;
table[MCOPY as usize] = compute_gas_ext::mcopy;
table[PUSH0 as usize] = compute_gas_ext::push0;
table[PUSH1 as usize] = compute_gas_ext::push1;
table[PUSH2 as usize] = compute_gas_ext::push2;
table[PUSH3 as usize] = compute_gas_ext::push3;
table[PUSH4 as usize] = compute_gas_ext::push4;
table[PUSH5 as usize] = compute_gas_ext::push5;
table[PUSH6 as usize] = compute_gas_ext::push6;
table[PUSH7 as usize] = compute_gas_ext::push7;
table[PUSH8 as usize] = compute_gas_ext::push8;
table[PUSH9 as usize] = compute_gas_ext::push9;
table[PUSH10 as usize] = compute_gas_ext::push10;
table[PUSH11 as usize] = compute_gas_ext::push11;
table[PUSH12 as usize] = compute_gas_ext::push12;
table[PUSH13 as usize] = compute_gas_ext::push13;
table[PUSH14 as usize] = compute_gas_ext::push14;
table[PUSH15 as usize] = compute_gas_ext::push15;
table[PUSH16 as usize] = compute_gas_ext::push16;
table[PUSH17 as usize] = compute_gas_ext::push17;
table[PUSH18 as usize] = compute_gas_ext::push18;
table[PUSH19 as usize] = compute_gas_ext::push19;
table[PUSH20 as usize] = compute_gas_ext::push20;
table[PUSH21 as usize] = compute_gas_ext::push21;
table[PUSH22 as usize] = compute_gas_ext::push22;
table[PUSH23 as usize] = compute_gas_ext::push23;
table[PUSH24 as usize] = compute_gas_ext::push24;
table[PUSH25 as usize] = compute_gas_ext::push25;
table[PUSH26 as usize] = compute_gas_ext::push26;
table[PUSH27 as usize] = compute_gas_ext::push27;
table[PUSH28 as usize] = compute_gas_ext::push28;
table[PUSH29 as usize] = compute_gas_ext::push29;
table[PUSH30 as usize] = compute_gas_ext::push30;
table[PUSH31 as usize] = compute_gas_ext::push31;
table[PUSH32 as usize] = compute_gas_ext::push32;
table[DUP1 as usize] = compute_gas_ext::dup1;
table[DUP2 as usize] = compute_gas_ext::dup2;
table[DUP3 as usize] = compute_gas_ext::dup3;
table[DUP4 as usize] = compute_gas_ext::dup4;
table[DUP5 as usize] = compute_gas_ext::dup5;
table[DUP6 as usize] = compute_gas_ext::dup6;
table[DUP7 as usize] = compute_gas_ext::dup7;
table[DUP8 as usize] = compute_gas_ext::dup8;
table[DUP9 as usize] = compute_gas_ext::dup9;
table[DUP10 as usize] = compute_gas_ext::dup10;
table[DUP11 as usize] = compute_gas_ext::dup11;
table[DUP12 as usize] = compute_gas_ext::dup12;
table[DUP13 as usize] = compute_gas_ext::dup13;
table[DUP14 as usize] = compute_gas_ext::dup14;
table[DUP15 as usize] = compute_gas_ext::dup15;
table[DUP16 as usize] = compute_gas_ext::dup16;
table[SWAP1 as usize] = compute_gas_ext::swap1;
table[SWAP2 as usize] = compute_gas_ext::swap2;
table[SWAP3 as usize] = compute_gas_ext::swap3;
table[SWAP4 as usize] = compute_gas_ext::swap4;
table[SWAP5 as usize] = compute_gas_ext::swap5;
table[SWAP6 as usize] = compute_gas_ext::swap6;
table[SWAP7 as usize] = compute_gas_ext::swap7;
table[SWAP8 as usize] = compute_gas_ext::swap8;
table[SWAP9 as usize] = compute_gas_ext::swap9;
table[SWAP10 as usize] = compute_gas_ext::swap10;
table[SWAP11 as usize] = compute_gas_ext::swap11;
table[SWAP12 as usize] = compute_gas_ext::swap12;
table[SWAP13 as usize] = compute_gas_ext::swap13;
table[SWAP14 as usize] = compute_gas_ext::swap14;
table[SWAP15 as usize] = compute_gas_ext::swap15;
table[SWAP16 as usize] = compute_gas_ext::swap16;
table[LOG0 as usize] = additional_limit_ext::log::<0, _, _>;
table[LOG1 as usize] = additional_limit_ext::log::<1, _, _>;
table[LOG2 as usize] = additional_limit_ext::log::<2, _, _>;
table[LOG3 as usize] = additional_limit_ext::log::<3, _, _>;
table[LOG4 as usize] = additional_limit_ext::log::<4, _, _>;
table[CREATE as usize] = forward_gas_ext::create;
table[CREATE2 as usize] = forward_gas_ext::create2;
table[CALL as usize] = forward_gas_ext::call;
table[CALLCODE as usize] = compute_gas_ext::call_code;
table[DELEGATECALL as usize] = compute_gas_ext::delegate_call;
table[STATICCALL as usize] = compute_gas_ext::static_call;
table[INVALID as usize] = compute_gas_ext::invalid;
table[RETURN as usize] = compute_gas_ext::ret;
table[REVERT as usize] = compute_gas_ext::revert;
table[SELFDESTRUCT as usize] = control::invalid;
table
}
}
pub mod forward_gas_ext {
use super::*;
macro_rules! wrap_gas_cap {
($fn_name:ident, $opcode_name:expr, $wrapped_fn:path, $has_transfer_logic:expr) => {
#[doc = concat!("`", $opcode_name, "` opcode with 98/100 gas forwarding rule.")]
#[inline]
pub fn $fn_name<
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ContextTr + JournalInspectTr + ?Sized,
>(
context: InstructionContext<'_, H, WIRE>,
) {
let has_transfer = $has_transfer_logic(&context);
run_inner_instruction_or_abort!($wrapped_fn, context);
match context.interpreter.bytecode.action() {
Some(InterpreterAction::NewFrame(FrameInput::Call(call_inputs))) => {
let child_gas = call_inputs.gas_limit as u128;
let transfer_gas_stipend =
if has_transfer { gas::CALL_STIPEND as u128 } else { 0 };
let forwarded_gas = child_gas - transfer_gas_stipend;
let parent_original_gas_left =
context.interpreter.gas.remaining() as u128 + forwarded_gas;
let forwarded_gas_cap =
parent_original_gas_left - parent_original_gas_left * 2 / 100;
let capped_forwarded_gas = min(forwarded_gas, forwarded_gas_cap);
let gas_to_return = forwarded_gas - capped_forwarded_gas;
let new_child_gas = capped_forwarded_gas + transfer_gas_stipend;
context.interpreter.gas.erase_cost(gas_to_return as u64);
call_inputs.gas_limit = new_child_gas as u64;
}
Some(InterpreterAction::NewFrame(FrameInput::Create(create_inputs))) => {
let child_gas = create_inputs.gas_limit as u128;
let forwarded_gas = child_gas;
let parent_original_gas_left =
context.interpreter.gas.remaining() as u128 + forwarded_gas;
let forwarded_gas_cap =
parent_original_gas_left - parent_original_gas_left * 2 / 100;
let capped_forwarded_gas = min(forwarded_gas, forwarded_gas_cap);
let gas_to_return = forwarded_gas - capped_forwarded_gas;
context.interpreter.gas.erase_cost(gas_to_return as u64);
create_inputs.gas_limit = capped_forwarded_gas as u64;
}
_ => {}
}
}
};
}
#[inline]
fn check_call_has_transfer<
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ?Sized,
>(
context: &InstructionContext<'_, H, WIRE>,
) -> bool {
if let Some(value) = context.interpreter.stack.inspect::<2>() {
!value.is_zero()
} else {
false
}
}
#[inline]
fn no_transfer<WIRE: InterpreterTypes<Stack: StackInspectTr>, H: HostExt + ?Sized>(
_context: &InstructionContext<'_, H, WIRE>,
) -> bool {
false
}
wrap_gas_cap!(call, "CALL", storage_gas_ext::call, check_call_has_transfer);
wrap_gas_cap!(call_code, "CALLCODE", storage_gas_ext::call_code, check_call_has_transfer);
wrap_gas_cap!(delegate_call, "DELEGATECALL", storage_gas_ext::delegate_call, no_transfer);
wrap_gas_cap!(static_call, "STATICCALL", storage_gas_ext::static_call, no_transfer);
wrap_gas_cap!(create, "CREATE", storage_gas_ext::create::<WIRE, false, H>, no_transfer);
wrap_gas_cap!(create2, "CREATE2", storage_gas_ext::create::<WIRE, true, H>, no_transfer);
}
pub mod volatile_data_ext {
use super::*;
use alloy_primitives::Address;
use crate::{
volatile_data_access_disabled_revert_data, VolatileDataAccessType, ORACLE_CONTRACT_ADDRESS,
};
macro_rules! apply_compute_gas_limit {
($context:expr) => {
let compute_gas_limit =
$context.host.volatile_data_tracker().borrow().get_compute_gas_limit();
if let Some(limit) = compute_gas_limit {
$context.host.additional_limit().borrow_mut().set_compute_gas_limit(limit);
}
};
}
macro_rules! wrap_op_detain_gas_unconditional {
($fn_name:ident, $opcode_name:expr, $original_fn:path, $access_type:expr) => {
#[doc = concat!("`", $opcode_name, "` opcode with compute gas limit enforcement on volatile data access.")]
#[inline]
pub fn $fn_name<WIRE: InterpreterTypes, H: HostExt + ?Sized>(
context: InstructionContext<'_, H, WIRE>,
) {
if context.host.volatile_access_disabled() {
context.interpreter.bytecode.set_action(InterpreterAction::new_return(
InstructionResult::Revert,
volatile_data_access_disabled_revert_data($access_type),
context.interpreter.gas,
));
return;
}
run_inner_instruction_or_abort!($original_fn, context);
apply_compute_gas_limit!(context);
}
};
}
macro_rules! wrap_op_detain_gas_conditional {
($fn_name:ident, $opcode_name:expr, $original_fn:path) => {
#[doc = concat!("`", $opcode_name, "` opcode with compute gas limit enforcement on volatile data access.")]
#[inline]
pub fn $fn_name<
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ContextTr + JournalInspectTr + ?Sized,
>(
context: InstructionContext<'_, H, WIRE>,
) {
if let Some(addr_word) = context.interpreter.stack.inspect::<0>() {
let target: Address = addr_word.into_address();
let beneficiary = context.host.beneficiary_address();
if target == beneficiary && context.host.volatile_access_disabled() {
context.interpreter.bytecode.set_action(InterpreterAction::new_return(
InstructionResult::Revert,
volatile_data_access_disabled_revert_data(
VolatileDataAccessType::Beneficiary,
),
context.interpreter.gas,
));
return;
}
}
run_inner_instruction_or_abort!($original_fn, context);
apply_compute_gas_limit!(context);
}
};
}
wrap_op_detain_gas_unconditional!(
timestamp,
"TIMESTAMP",
compute_gas_ext::timestamp,
VolatileDataAccessType::Timestamp
);
wrap_op_detain_gas_unconditional!(
block_number,
"NUMBER",
compute_gas_ext::number,
VolatileDataAccessType::BlockNumber
);
wrap_op_detain_gas_unconditional!(
difficulty,
"DIFFICULTY",
compute_gas_ext::difficulty,
VolatileDataAccessType::Difficulty
);
wrap_op_detain_gas_unconditional!(
gas_limit_opcode,
"GASLIMIT",
compute_gas_ext::gaslimit,
VolatileDataAccessType::GasLimit
);
wrap_op_detain_gas_unconditional!(
basefee,
"BASEFEE",
compute_gas_ext::basefee,
VolatileDataAccessType::BaseFee
);
wrap_op_detain_gas_unconditional!(
coinbase,
"COINBASE",
compute_gas_ext::coinbase,
VolatileDataAccessType::Coinbase
);
wrap_op_detain_gas_unconditional!(
blockhash,
"BLOCKHASH",
compute_gas_ext::blockhash,
VolatileDataAccessType::BlockHash
);
wrap_op_detain_gas_unconditional!(
blobbasefee,
"BLOBBASEFEE",
compute_gas_ext::blobbasefee,
VolatileDataAccessType::BlobBaseFee
);
wrap_op_detain_gas_unconditional!(
blobhash,
"BLOBHASH",
compute_gas_ext::blobhash,
VolatileDataAccessType::BlobHash
);
wrap_op_detain_gas_conditional!(balance, "BALANCE", compute_gas_ext::balance);
wrap_op_detain_gas_conditional!(extcodesize, "EXTCODESIZE", compute_gas_ext::extcodesize);
wrap_op_detain_gas_conditional!(extcodecopy, "EXTCODECOPY", compute_gas_ext::extcodecopy);
wrap_op_detain_gas_conditional!(extcodehash, "EXTCODEHASH", compute_gas_ext::extcodehash);
wrap_op_detain_gas_conditional!(selfdestruct, "SELFDESTRUCT", compute_gas_ext::selfdestruct);
wrap_op_detain_gas_conditional!(
selfdestruct_rex5,
"SELFDESTRUCT",
super::storage_gas_ext::selfdestruct
);
#[inline]
pub fn selfbalance<WIRE: InterpreterTypes, H: HostExt + ?Sized>(
context: InstructionContext<'_, H, WIRE>,
) {
let target = context.interpreter.input.target_address();
let beneficiary = context.host.beneficiary_address();
if target == beneficiary && context.host.volatile_access_disabled() {
context.interpreter.bytecode.set_action(InterpreterAction::new_return(
InstructionResult::Revert,
volatile_data_access_disabled_revert_data(VolatileDataAccessType::Beneficiary),
context.interpreter.gas,
));
return;
}
run_inner_instruction_or_abort!(compute_gas_ext::selfbalance, context);
apply_compute_gas_limit!(context);
}
#[inline]
pub fn sload<WIRE: InterpreterTypes, H: HostExt + ?Sized>(
context: InstructionContext<'_, H, WIRE>,
) {
let target = context.interpreter.input.target_address();
if target == ORACLE_CONTRACT_ADDRESS && context.host.volatile_access_disabled() {
context.interpreter.bytecode.set_action(InterpreterAction::new_return(
InstructionResult::Revert,
volatile_data_access_disabled_revert_data(VolatileDataAccessType::Oracle),
context.interpreter.gas,
));
return;
}
run_inner_instruction_or_abort!(compute_gas_ext::sload, context);
apply_compute_gas_limit!(context);
}
macro_rules! wrap_call_volatile_check {
($fn_name:ident, $opcode_name:expr, $inner_fn:path) => {
#[doc = concat!("`", $opcode_name, "` opcode with volatile data access disabled check for beneficiary.")]
#[inline]
pub fn $fn_name<
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ContextTr + JournalInspectTr + ?Sized,
>(
context: InstructionContext<'_, H, WIRE>,
) {
if let Some(addr_word) = context.interpreter.stack.inspect::<1>() {
let target: Address = addr_word.into_address();
let beneficiary = context.host.beneficiary_address();
if target == beneficiary && context.host.volatile_access_disabled() {
context.interpreter.bytecode.set_action(InterpreterAction::new_return(
InstructionResult::Revert,
volatile_data_access_disabled_revert_data(
VolatileDataAccessType::Beneficiary,
),
context.interpreter.gas,
));
return;
}
}
{
let ctx = InstructionContext::<'_, H, WIRE> {
interpreter: &mut *context.interpreter,
host: &mut *context.host,
};
$inner_fn(ctx);
}
apply_compute_gas_limit!(context);
}
};
}
wrap_call_volatile_check!(call, "CALL", forward_gas_ext::call);
wrap_call_volatile_check!(static_call, "STATICCALL", forward_gas_ext::static_call);
wrap_call_volatile_check!(delegate_call, "DELEGATECALL", forward_gas_ext::delegate_call);
wrap_call_volatile_check!(call_code, "CALLCODE", forward_gas_ext::call_code);
}
pub mod additional_limit_ext {
use super::*;
pub fn sstore<
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ContextTr + JournalInspectTr + ?Sized,
>(
context: InstructionContext<'_, H, WIRE>,
) {
let target_address = context.interpreter.input.target_address();
let Some(index) = context.interpreter.stack.inspect::<0>() else {
context.interpreter.halt(InstructionResult::StackUnderflow);
return;
};
let mega_spec = context.host.spec_id();
let Ok(slot) = context.host.inspect_storage(mega_spec, target_address, index) else {
context.interpreter.halt(InstructionResult::FatalExternalError);
return;
};
let (original_value, present_value) = (slot.original_value(), slot.present_value());
let Some(new_value) = context.interpreter.stack.inspect::<1>() else {
context.interpreter.halt(InstructionResult::StackUnderflow);
return;
};
let loaded_data = SStoreResult { original_value, present_value, new_value };
run_inner_instruction_or_abort!(storage_gas_ext::sstore, context);
let additional_limit = context.host.additional_limit();
let mut additional_limit = additional_limit.borrow_mut();
if !additional_limit.on_sstore(target_address, index, &loaded_data) {
context.interpreter.halt(additional_limit.exceeding_instruction_result());
}
}
pub fn log<
const N: usize,
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ContextTr + JournalInspectTr + ?Sized,
>(
context: InstructionContext<'_, H, WIRE>,
) {
let Some(len) = context.interpreter.stack.inspect::<1>() else {
context.interpreter.halt(InstructionResult::StackUnderflow);
return;
};
let len = as_usize_or_fail!(context.interpreter, len);
run_inner_instruction_or_abort!(storage_gas_ext::log::<N, WIRE, H>, context);
let additional_limit = context.host.additional_limit();
let mut additional_limit = additional_limit.borrow_mut();
if !additional_limit.on_log(N as u64, len as u64) {
context.interpreter.halt(additional_limit.exceeding_instruction_result());
}
}
}
pub mod storage_gas_ext {
use super::*;
use alloy_primitives::Address;
fn storage_addr_from_to(_mega_spec: MegaSpecId, _current: Address, to: Address) -> Address {
to
}
fn storage_addr_for_callcode(mega_spec: MegaSpecId, current: Address, to: Address) -> Address {
if mega_spec.is_enabled(MegaSpecId::REX5) {
current
} else {
to
}
}
macro_rules! wrap_call_with_storage_gas {
($fn_name:ident, $opcode_name:expr, $wrapped_fn:path, $has_transfer_logic:expr) => {
wrap_call_with_storage_gas!(
$fn_name,
$opcode_name,
$wrapped_fn,
$has_transfer_logic,
storage_addr_from_to
);
};
($fn_name:ident, $opcode_name:expr, $wrapped_fn:path, $has_transfer_logic:expr, $select_addr:path) => {
#[doc = concat!("`", $opcode_name, "` opcode implementation modified from `revm` with compute gas tracking and dynamically-scaled storage gas costs.")]
pub fn $fn_name<
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ContextTr + JournalInspectTr + ?Sized,
>(
context: InstructionContext<'_, H, WIRE>,
) {
let spec = context.interpreter.runtime_flag.spec_id();
let Some(to) = context.interpreter.stack.inspect::<1>() else {
context.interpreter.halt(InstructionResult::StackUnderflow);
return;
};
let to = to.into_address();
let mega_spec = context.host.spec_id();
let current_address = context.interpreter.input.target_address();
let storage_address = $select_addr(mega_spec, current_address, to);
let Ok(storage_account) = (if mega_spec.is_enabled(MegaSpecId::REX5) {
context.host.inspect_account(storage_address, false)
} else {
context.host.inspect_account_delegated(mega_spec, storage_address)
}) else {
context.interpreter.halt(InstructionResult::FatalExternalError);
return;
};
let is_empty = storage_account.state_clear_aware_is_empty(spec);
let has_transfer = if $has_transfer_logic {
let Some(value) = context.interpreter.stack.inspect::<2>() else {
context.interpreter.halt(InstructionResult::StackUnderflow);
return;
};
!value.is_zero()
} else {
false
};
if is_empty && has_transfer {
let Some(new_account_storage_gas) =
context.host.new_account_storage_gas(storage_address)
else {
context.interpreter.halt(InstructionResult::FatalExternalError);
return;
};
let drained = context
.host
.additional_limit()
.borrow_mut()
.try_consume_storage_stipend(new_account_storage_gas);
gas!(context.interpreter, new_account_storage_gas - drained);
}
run_inner_instruction_or_abort!($wrapped_fn, context);
}
};
}
wrap_call_with_storage_gas!(call, "CALL", compute_gas_ext::call, true);
wrap_call_with_storage_gas!(
delegate_call,
"DELEGATECALL",
compute_gas_ext::delegate_call,
false
);
wrap_call_with_storage_gas!(static_call, "STATICCALL", compute_gas_ext::static_call, false);
wrap_call_with_storage_gas!(
call_code,
"CALLCODE",
compute_gas_ext::call_code,
true,
storage_addr_for_callcode
);
pub fn create<
WIRE: InterpreterTypes<Stack: StackInspectTr>,
const IS_CREATE2: bool,
H: HostExt + ContextTr + JournalInspectTr + ?Sized,
>(
context: InstructionContext<'_, H, WIRE>,
) {
let spec = context.host.spec_id();
let creator_address = context.interpreter.input.target_address();
let Ok(creator) = (if spec.is_enabled(MegaSpecId::REX5) {
context.host.inspect_account(creator_address, false)
} else {
context.host.inspect_account_delegated(spec, creator_address)
}) else {
context.interpreter.halt(InstructionResult::FatalExternalError);
return;
};
let mut resize_gas: u64 = 0;
let is_rex5_enabled = spec.is_enabled(MegaSpecId::REX5);
let created_address = if IS_CREATE2 {
let Some(initcode_offset) = context.interpreter.stack.inspect::<1>() else {
context.interpreter.halt(InstructionResult::StackUnderflow);
return;
};
let Some(initcode_len) = context.interpreter.stack.inspect::<2>() else {
context.interpreter.halt(InstructionResult::StackUnderflow);
return;
};
let rex5_salt = if is_rex5_enabled {
let Some(salt) = context.interpreter.stack.inspect::<3>() else {
context.interpreter.halt(InstructionResult::StackUnderflow);
return;
};
Some(salt)
} else {
None
};
let initcode_hash = if is_rex5_enabled && initcode_len.is_zero() {
KECCAK_EMPTY
} else {
let initcode_offset = as_usize_or_fail!(context.interpreter, initcode_offset);
let initcode_len = as_usize_or_fail!(context.interpreter, initcode_len);
let gas_before_resize = context.interpreter.gas.remaining();
resize_memory!(context.interpreter, initcode_offset, initcode_len);
resize_gas = gas_before_resize.saturating_sub(context.interpreter.gas.remaining());
if is_rex5_enabled && resize_gas > 0 {
let mut additional_limit = context.host.additional_limit().borrow_mut();
compute_gas!(context.interpreter, additional_limit, resize_gas);
resize_gas = 0;
}
let code = Bytes::copy_from_slice(
context.interpreter.memory.slice_len(initcode_offset, initcode_len).as_ref(),
);
keccak256(&code)
};
let salt = if let Some(s) = rex5_salt {
s
} else {
let Some(salt) = context.interpreter.stack.inspect::<3>() else {
context.interpreter.halt(InstructionResult::StackUnderflow);
return;
};
salt
};
creator_address.create2(salt.to_be_bytes(), initcode_hash)
} else {
creator_address.create(creator.info.nonce)
};
let create_contract_storage_gas = if spec.is_enabled(MegaSpecId::REX) {
context.host.create_contract_storage_gas(created_address)
} else {
context.host.new_account_storage_gas(created_address)
};
let Some(create_contract_storage_gas) = create_contract_storage_gas else {
context.interpreter.halt(InstructionResult::FatalExternalError);
return;
};
let drained = context
.host
.additional_limit()
.borrow_mut()
.try_consume_storage_stipend(create_contract_storage_gas);
gas!(context.interpreter, create_contract_storage_gas - drained);
if IS_CREATE2 {
run_inner_instruction_or_abort!(compute_gas_ext::create2, context);
} else {
run_inner_instruction_or_abort!(compute_gas_ext::create, context);
}
if resize_gas > 0 {
let mut additional_limit = context.host.additional_limit().borrow_mut();
compute_gas!(context.interpreter, additional_limit, resize_gas);
}
}
pub fn log<
const N: usize,
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ?Sized,
>(
context: InstructionContext<'_, H, WIRE>,
) {
let Some(len) = context.interpreter.stack.inspect::<1>() else {
context.interpreter.halt(InstructionResult::StackUnderflow);
return;
};
let len = as_usize_or_fail!(context.interpreter, len);
let log_storage_cost = {
let topic_cost = constants::mini_rex::LOG_TOPIC_STORAGE_GAS.checked_mul(N as u64);
let data_cost = constants::mini_rex::LOG_DATA_STORAGE_GAS.checked_mul(len as u64);
topic_cost.and_then(|topic| data_cost.and_then(|cost| cost.checked_add(topic)))
};
let log_storage_cost = log_storage_cost.map(|amount| {
let drained =
context.host.additional_limit().borrow_mut().try_consume_storage_stipend(amount);
amount - drained
});
gas_or_fail!(context.interpreter, log_storage_cost);
match N {
0 => {
run_inner_instruction_or_abort!(compute_gas_ext::log0, context);
}
1 => {
run_inner_instruction_or_abort!(compute_gas_ext::log1, context);
}
2 => {
run_inner_instruction_or_abort!(compute_gas_ext::log2, context);
}
3 => {
run_inner_instruction_or_abort!(compute_gas_ext::log3, context);
}
4 => {
run_inner_instruction_or_abort!(compute_gas_ext::log4, context);
}
_ => {
context.interpreter.halt(InstructionResult::InvalidFEOpcode);
}
}
}
pub fn sstore<
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ContextTr + JournalInspectTr + ?Sized,
>(
context: InstructionContext<'_, H, WIRE>,
) {
let target_address = context.interpreter.input.target_address();
let Some(index) = context.interpreter.stack.inspect::<0>() else {
context.interpreter.halt(InstructionResult::StackUnderflow);
return;
};
let mega_spec = context.host.spec_id();
let Ok(slot) = context.host.inspect_storage(mega_spec, target_address, index) else {
context.interpreter.halt(InstructionResult::FatalExternalError);
return;
};
let (original_value, present_value) = (slot.original_value(), slot.present_value());
let Some(new_value) = context.interpreter.stack.inspect::<1>() else {
context.interpreter.halt(InstructionResult::StackUnderflow);
return;
};
if original_value.is_zero() && present_value.is_zero() && !new_value.is_zero() {
let Some(sstore_set_storage_gas) =
context.host.sstore_set_storage_gas(target_address, index)
else {
context.interpreter.halt(InstructionResult::FatalExternalError);
return;
};
let drained = context
.host
.additional_limit()
.borrow_mut()
.try_consume_storage_stipend(sstore_set_storage_gas);
gas!(context.interpreter, sstore_set_storage_gas - drained);
}
run_inner_instruction_or_abort!(compute_gas_ext::sstore, context);
}
pub fn selfdestruct<
WIRE: InterpreterTypes<Stack: StackInspectTr>,
H: HostExt + ContextTr + JournalInspectTr + ?Sized,
>(
context: InstructionContext<'_, H, WIRE>,
) {
let eth_spec = context.interpreter.runtime_flag.spec_id();
let Some(target) = context.interpreter.stack.inspect::<0>() else {
context.interpreter.halt(InstructionResult::StackUnderflow);
return;
};
let target = target.into_address();
let Ok(target_account) = context.host.inspect_account(target, false) else {
context.interpreter.halt(InstructionResult::FatalExternalError);
return;
};
let is_empty = target_account.state_clear_aware_is_empty(eth_spec);
let caller = context.interpreter.input.target_address();
let Ok(caller_account) = context.host.inspect_account(caller, false) else {
context.interpreter.halt(InstructionResult::FatalExternalError);
return;
};
let has_value = !caller_account.info.balance.is_zero();
if is_empty && has_value {
let Some(cost) = context.host.new_account_storage_gas(target) else {
context.interpreter.halt(InstructionResult::FatalExternalError);
return;
};
let drained =
context.host.additional_limit().borrow_mut().try_consume_storage_stipend(cost);
gas!(context.interpreter, cost - drained);
context.host.additional_limit().borrow_mut().on_selfdestruct_new_account();
}
run_inner_instruction_or_abort!(compute_gas_ext::selfdestruct, context);
}
}
pub mod compute_gas_ext {
use super::*;
macro_rules! wrap_op_compute_gas {
($fn_name:ident, $opcode_name:expr, $original_fn:path) => {
#[doc = concat!("`", $opcode_name, "` opcode with compute gas tracking.")]
#[inline]
pub fn $fn_name<WIRE: InterpreterTypes, H: HostExt + ?Sized>(
context: InstructionContext<'_, H, WIRE>,
) {
let gas_before = context.interpreter.gas.remaining();
run_inner_instruction_or_abort!($original_fn, context);
let mut gas_used = gas_before.saturating_sub(context.interpreter.gas.remaining());
match context.interpreter.bytecode.action() {
Some(InterpreterAction::NewFrame(FrameInput::Call(call_inputs))) => {
let stipend_from_revm = if context
.host
.spec_id()
.is_enabled(MegaSpecId::REX5) &&
matches!(call_inputs.scheme, CallScheme::Call | CallScheme::CallCode) &&
call_inputs.transfers_value()
{
gas::CALL_STIPEND
} else {
0
};
let parent_contributed =
call_inputs.gas_limit.saturating_sub(stipend_from_revm);
gas_used = gas_used.saturating_sub(parent_contributed);
}
Some(InterpreterAction::NewFrame(FrameInput::Create(create_inputs))) => {
gas_used = gas_used.saturating_sub(create_inputs.gas_limit);
}
_ => {}
}
let mut additional_limit = context.host.additional_limit().borrow_mut();
compute_gas!(context.interpreter, additional_limit, gas_used);
}
};
}
wrap_op_compute_gas!(stop, "STOP", instructions::control::stop);
wrap_op_compute_gas!(add, "ADD", instructions::arithmetic::add);
wrap_op_compute_gas!(mul, "MUL", instructions::arithmetic::mul);
wrap_op_compute_gas!(sub, "SUB", instructions::arithmetic::sub);
wrap_op_compute_gas!(div, "DIV", instructions::arithmetic::div);
wrap_op_compute_gas!(sdiv, "SDIV", instructions::arithmetic::sdiv);
wrap_op_compute_gas!(rem, "MOD", instructions::arithmetic::rem);
wrap_op_compute_gas!(smod, "SMOD", instructions::arithmetic::smod);
wrap_op_compute_gas!(addmod, "ADDMOD", instructions::arithmetic::addmod);
wrap_op_compute_gas!(mulmod, "MULMOD", instructions::arithmetic::mulmod);
wrap_op_compute_gas!(exp, "EXP", instructions::arithmetic::exp);
wrap_op_compute_gas!(signextend, "SIGNEXTEND", instructions::arithmetic::signextend);
wrap_op_compute_gas!(lt, "LT", instructions::bitwise::lt);
wrap_op_compute_gas!(gt, "GT", instructions::bitwise::gt);
wrap_op_compute_gas!(slt, "SLT", instructions::bitwise::slt);
wrap_op_compute_gas!(sgt, "SGT", instructions::bitwise::sgt);
wrap_op_compute_gas!(eq, "EQ", instructions::bitwise::eq);
wrap_op_compute_gas!(iszero, "ISZERO", instructions::bitwise::iszero);
wrap_op_compute_gas!(bitand, "AND", instructions::bitwise::bitand);
wrap_op_compute_gas!(bitor, "OR", instructions::bitwise::bitor);
wrap_op_compute_gas!(bitxor, "XOR", instructions::bitwise::bitxor);
wrap_op_compute_gas!(not, "NOT", instructions::bitwise::not);
wrap_op_compute_gas!(byte, "BYTE", instructions::bitwise::byte);
wrap_op_compute_gas!(shl, "SHL", instructions::bitwise::shl);
wrap_op_compute_gas!(shr, "SHR", instructions::bitwise::shr);
wrap_op_compute_gas!(sar, "SAR", instructions::bitwise::sar);
wrap_op_compute_gas!(clz, "CLZ", instructions::bitwise::clz);
wrap_op_compute_gas!(keccak256, "KECCAK256", instructions::system::keccak256);
wrap_op_compute_gas!(address, "ADDRESS", instructions::system::address);
wrap_op_compute_gas!(balance, "BALANCE", instructions::host::balance);
wrap_op_compute_gas!(origin, "ORIGIN", instructions::tx_info::origin);
wrap_op_compute_gas!(caller, "CALLER", instructions::system::caller);
wrap_op_compute_gas!(callvalue, "CALLVALUE", instructions::system::callvalue);
wrap_op_compute_gas!(calldataload, "CALLDATALOAD", instructions::system::calldataload);
wrap_op_compute_gas!(calldatasize, "CALLDATASIZE", instructions::system::calldatasize);
wrap_op_compute_gas!(calldatacopy, "CALLDATACOPY", instructions::system::calldatacopy);
wrap_op_compute_gas!(codesize, "CODESIZE", instructions::system::codesize);
wrap_op_compute_gas!(codecopy, "CODECOPY", instructions::system::codecopy);
wrap_op_compute_gas!(gasprice, "GASPRICE", instructions::tx_info::gasprice);
wrap_op_compute_gas!(extcodesize, "EXTCODESIZE", instructions::host::extcodesize);
wrap_op_compute_gas!(extcodecopy, "EXTCODECOPY", instructions::host::extcodecopy);
wrap_op_compute_gas!(returndatasize, "RETURNDATASIZE", instructions::system::returndatasize);
wrap_op_compute_gas!(returndatacopy, "RETURNDATACOPY", instructions::system::returndatacopy);
wrap_op_compute_gas!(extcodehash, "EXTCODEHASH", instructions::host::extcodehash);
wrap_op_compute_gas!(blockhash, "BLOCKHASH", instructions::host::blockhash);
wrap_op_compute_gas!(coinbase, "COINBASE", instructions::block_info::coinbase);
wrap_op_compute_gas!(timestamp, "TIMESTAMP", instructions::block_info::timestamp);
wrap_op_compute_gas!(number, "NUMBER", instructions::block_info::block_number);
wrap_op_compute_gas!(difficulty, "DIFFICULTY", instructions::block_info::difficulty);
wrap_op_compute_gas!(gaslimit, "GASLIMIT", instructions::block_info::gaslimit);
wrap_op_compute_gas!(chainid, "CHAINID", instructions::block_info::chainid);
wrap_op_compute_gas!(selfbalance, "SELFBALANCE", instructions::host::selfbalance);
wrap_op_compute_gas!(basefee, "BASEFEE", instructions::block_info::basefee);
wrap_op_compute_gas!(blobhash, "BLOBHASH", instructions::tx_info::blob_hash);
wrap_op_compute_gas!(blobbasefee, "BLOBBASEFEE", instructions::block_info::blob_basefee);
wrap_op_compute_gas!(pop, "POP", instructions::stack::pop);
wrap_op_compute_gas!(mload, "MLOAD", instructions::memory::mload);
wrap_op_compute_gas!(mstore, "MSTORE", instructions::memory::mstore);
wrap_op_compute_gas!(mstore8, "MSTORE8", instructions::memory::mstore8);
wrap_op_compute_gas!(sload, "SLOAD", instructions::host::sload);
wrap_op_compute_gas!(sstore, "SSTORE", instructions::host::sstore);
wrap_op_compute_gas!(jump, "JUMP", instructions::control::jump);
wrap_op_compute_gas!(jumpi, "JUMPI", instructions::control::jumpi);
wrap_op_compute_gas!(pc, "PC", instructions::control::pc);
wrap_op_compute_gas!(msize, "MSIZE", instructions::memory::msize);
wrap_op_compute_gas!(gas, "GAS", instructions::system::gas);
wrap_op_compute_gas!(jumpdest, "JUMPDEST", instructions::control::jumpdest);
wrap_op_compute_gas!(tload, "TLOAD", instructions::host::tload);
wrap_op_compute_gas!(tstore, "TSTORE", instructions::host::tstore);
wrap_op_compute_gas!(mcopy, "MCOPY", instructions::memory::mcopy);
wrap_op_compute_gas!(push0, "PUSH0", instructions::stack::push0);
wrap_op_compute_gas!(push1, "PUSH1", instructions::stack::push::<1, _, _>);
wrap_op_compute_gas!(push2, "PUSH2", instructions::stack::push::<2, _, _>);
wrap_op_compute_gas!(push3, "PUSH3", instructions::stack::push::<3, _, _>);
wrap_op_compute_gas!(push4, "PUSH4", instructions::stack::push::<4, _, _>);
wrap_op_compute_gas!(push5, "PUSH5", instructions::stack::push::<5, _, _>);
wrap_op_compute_gas!(push6, "PUSH6", instructions::stack::push::<6, _, _>);
wrap_op_compute_gas!(push7, "PUSH7", instructions::stack::push::<7, _, _>);
wrap_op_compute_gas!(push8, "PUSH8", instructions::stack::push::<8, _, _>);
wrap_op_compute_gas!(push9, "PUSH9", instructions::stack::push::<9, _, _>);
wrap_op_compute_gas!(push10, "PUSH10", instructions::stack::push::<10, _, _>);
wrap_op_compute_gas!(push11, "PUSH11", instructions::stack::push::<11, _, _>);
wrap_op_compute_gas!(push12, "PUSH12", instructions::stack::push::<12, _, _>);
wrap_op_compute_gas!(push13, "PUSH13", instructions::stack::push::<13, _, _>);
wrap_op_compute_gas!(push14, "PUSH14", instructions::stack::push::<14, _, _>);
wrap_op_compute_gas!(push15, "PUSH15", instructions::stack::push::<15, _, _>);
wrap_op_compute_gas!(push16, "PUSH16", instructions::stack::push::<16, _, _>);
wrap_op_compute_gas!(push17, "PUSH17", instructions::stack::push::<17, _, _>);
wrap_op_compute_gas!(push18, "PUSH18", instructions::stack::push::<18, _, _>);
wrap_op_compute_gas!(push19, "PUSH19", instructions::stack::push::<19, _, _>);
wrap_op_compute_gas!(push20, "PUSH20", instructions::stack::push::<20, _, _>);
wrap_op_compute_gas!(push21, "PUSH21", instructions::stack::push::<21, _, _>);
wrap_op_compute_gas!(push22, "PUSH22", instructions::stack::push::<22, _, _>);
wrap_op_compute_gas!(push23, "PUSH23", instructions::stack::push::<23, _, _>);
wrap_op_compute_gas!(push24, "PUSH24", instructions::stack::push::<24, _, _>);
wrap_op_compute_gas!(push25, "PUSH25", instructions::stack::push::<25, _, _>);
wrap_op_compute_gas!(push26, "PUSH26", instructions::stack::push::<26, _, _>);
wrap_op_compute_gas!(push27, "PUSH27", instructions::stack::push::<27, _, _>);
wrap_op_compute_gas!(push28, "PUSH28", instructions::stack::push::<28, _, _>);
wrap_op_compute_gas!(push29, "PUSH29", instructions::stack::push::<29, _, _>);
wrap_op_compute_gas!(push30, "PUSH30", instructions::stack::push::<30, _, _>);
wrap_op_compute_gas!(push31, "PUSH31", instructions::stack::push::<31, _, _>);
wrap_op_compute_gas!(push32, "PUSH32", instructions::stack::push::<32, _, _>);
wrap_op_compute_gas!(dup1, "DUP1", instructions::stack::dup::<1, _, _>);
wrap_op_compute_gas!(dup2, "DUP2", instructions::stack::dup::<2, _, _>);
wrap_op_compute_gas!(dup3, "DUP3", instructions::stack::dup::<3, _, _>);
wrap_op_compute_gas!(dup4, "DUP4", instructions::stack::dup::<4, _, _>);
wrap_op_compute_gas!(dup5, "DUP5", instructions::stack::dup::<5, _, _>);
wrap_op_compute_gas!(dup6, "DUP6", instructions::stack::dup::<6, _, _>);
wrap_op_compute_gas!(dup7, "DUP7", instructions::stack::dup::<7, _, _>);
wrap_op_compute_gas!(dup8, "DUP8", instructions::stack::dup::<8, _, _>);
wrap_op_compute_gas!(dup9, "DUP9", instructions::stack::dup::<9, _, _>);
wrap_op_compute_gas!(dup10, "DUP10", instructions::stack::dup::<10, _, _>);
wrap_op_compute_gas!(dup11, "DUP11", instructions::stack::dup::<11, _, _>);
wrap_op_compute_gas!(dup12, "DUP12", instructions::stack::dup::<12, _, _>);
wrap_op_compute_gas!(dup13, "DUP13", instructions::stack::dup::<13, _, _>);
wrap_op_compute_gas!(dup14, "DUP14", instructions::stack::dup::<14, _, _>);
wrap_op_compute_gas!(dup15, "DUP15", instructions::stack::dup::<15, _, _>);
wrap_op_compute_gas!(dup16, "DUP16", instructions::stack::dup::<16, _, _>);
wrap_op_compute_gas!(swap1, "SWAP1", instructions::stack::swap::<1, _, _>);
wrap_op_compute_gas!(swap2, "SWAP2", instructions::stack::swap::<2, _, _>);
wrap_op_compute_gas!(swap3, "SWAP3", instructions::stack::swap::<3, _, _>);
wrap_op_compute_gas!(swap4, "SWAP4", instructions::stack::swap::<4, _, _>);
wrap_op_compute_gas!(swap5, "SWAP5", instructions::stack::swap::<5, _, _>);
wrap_op_compute_gas!(swap6, "SWAP6", instructions::stack::swap::<6, _, _>);
wrap_op_compute_gas!(swap7, "SWAP7", instructions::stack::swap::<7, _, _>);
wrap_op_compute_gas!(swap8, "SWAP8", instructions::stack::swap::<8, _, _>);
wrap_op_compute_gas!(swap9, "SWAP9", instructions::stack::swap::<9, _, _>);
wrap_op_compute_gas!(swap10, "SWAP10", instructions::stack::swap::<10, _, _>);
wrap_op_compute_gas!(swap11, "SWAP11", instructions::stack::swap::<11, _, _>);
wrap_op_compute_gas!(swap12, "SWAP12", instructions::stack::swap::<12, _, _>);
wrap_op_compute_gas!(swap13, "SWAP13", instructions::stack::swap::<13, _, _>);
wrap_op_compute_gas!(swap14, "SWAP14", instructions::stack::swap::<14, _, _>);
wrap_op_compute_gas!(swap15, "SWAP15", instructions::stack::swap::<15, _, _>);
wrap_op_compute_gas!(swap16, "SWAP16", instructions::stack::swap::<16, _, _>);
wrap_op_compute_gas!(log0, "LOG0", instructions::host::log::<0, _>);
wrap_op_compute_gas!(log1, "LOG1", instructions::host::log::<1, _>);
wrap_op_compute_gas!(log2, "LOG2", instructions::host::log::<2, _>);
wrap_op_compute_gas!(log3, "LOG3", instructions::host::log::<3, _>);
wrap_op_compute_gas!(log4, "LOG4", instructions::host::log::<4, _>);
wrap_op_compute_gas!(create, "CREATE", instructions::contract::create::<_, false, _>);
wrap_op_compute_gas!(call, "CALL", instructions::contract::call);
wrap_op_compute_gas!(call_code, "CALLCODE", instructions::contract::call_code);
wrap_op_compute_gas!(ret, "RETURN", instructions::control::ret);
wrap_op_compute_gas!(delegate_call, "DELEGATECALL", instructions::contract::delegate_call);
wrap_op_compute_gas!(create2, "CREATE2", instructions::contract::create::<_, true, _>);
wrap_op_compute_gas!(static_call, "STATICCALL", instructions::contract::static_call);
wrap_op_compute_gas!(revert, "REVERT", instructions::control::revert);
wrap_op_compute_gas!(invalid, "INVALID", instructions::control::invalid);
wrap_op_compute_gas!(selfdestruct, "SELFDESTRUCT", instructions::host::selfdestruct);
}
pub trait StackInspectTr {
fn inspect<const N: usize>(&self) -> Option<U256>;
}
impl StackInspectTr for Stack {
fn inspect<const N: usize>(&self) -> Option<U256> {
if N >= self.len() {
return None;
}
let index = self.len() - 1 - N;
Some(unsafe { *self.data().get_unchecked(index) })
}
}