diff --git a/crates/ee-tests/src/lib.rs b/crates/ee-tests/src/lib.rs index db3d469d0d..42b8d48dbf 100644 --- a/crates/ee-tests/src/lib.rs +++ b/crates/ee-tests/src/lib.rs @@ -98,12 +98,8 @@ where // Compare the output objects directly if *output != expected { - // If they don't match, generate a nicer error by pretty-printing both as JSON - // This helps with debugging by showing the exact differences - let expected_pretty = serde_json::to_string_pretty(&expected).unwrap(); - panic!( - "Value does not match testdata.\nExpected:\n{expected_pretty}\n\nActual:\n{output_json}" + "Value does not match testdata.\nExpected:\n{expected_json}\n\nActual:\n{output_json}" ); } } diff --git a/crates/handler/src/frame.rs b/crates/handler/src/frame.rs index 56416f08d0..dd2cd5d9e8 100644 --- a/crates/handler/src/frame.rs +++ b/crates/handler/src/frame.rs @@ -727,47 +727,3 @@ pub fn return_create( interpreter_result.result = InstructionResult::Return; } - -/* -pub fn return_eofcreate( - journal: &mut JOURNAL, - checkpoint: JournalCheckpoint, - interpreter_result: &mut InterpreterResult, - address: Address, - max_code_size: usize, -) { - // Note we still execute RETURN opcode and return the bytes. - // In EOF those opcodes should abort execution. - // - // In RETURN gas is still protecting us from ddos and in oog, - // behaviour will be same as if it failed on return. - // - // Bytes of RETURN will drained in `insert_eofcreate_outcome`. - if interpreter_result.result != InstructionResult::ReturnContract { - journal.checkpoint_revert(checkpoint); - return; - } - - if interpreter_result.output.len() > max_code_size { - journal.checkpoint_revert(checkpoint); - interpreter_result.result = InstructionResult::CreateContractSizeLimit; - return; - } - - // Deduct gas for code deployment. - let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT; - if !interpreter_result.gas.record_cost(gas_for_code) { - journal.checkpoint_revert(checkpoint); - interpreter_result.result = InstructionResult::OutOfGas; - return; - } - - journal.checkpoint_commit(); - - // Decode bytecode has a performance hit, but it has reasonable restrains. - let bytecode = Eof::decode(interpreter_result.output.clone()).expect("Eof is already verified"); - - // Eof bytecode is going to be hashed. - journal.set_code(address, Bytecode::Eof(Arc::new(bytecode))); -} - */ diff --git a/crates/handler/src/instructions.rs b/crates/handler/src/instructions.rs index c6d8008882..b45582f0d4 100644 --- a/crates/handler/src/instructions.rs +++ b/crates/handler/src/instructions.rs @@ -19,12 +19,12 @@ pub trait InstructionProvider { /// Ethereum instruction contains list of mainnet instructions that is used for Interpreter execution. #[derive(Debug)] -pub struct EthInstructions { +pub struct EthInstructions { /// Table containing instruction implementations indexed by opcode. pub instruction_table: Box>, } -impl Clone for EthInstructions +impl Clone for EthInstructions where WIRE: InterpreterTypes, { diff --git a/crates/interpreter/src/gas.rs b/crates/interpreter/src/gas.rs index e270224069..37a0421835 100644 --- a/crates/interpreter/src/gas.rs +++ b/crates/interpreter/src/gas.rs @@ -153,19 +153,15 @@ impl Gas { false } - /// Record memory expansion - #[inline] - #[must_use = "internally uses record_cost that flags out of gas error"] - pub fn record_memory_expansion(&mut self, new_len: usize) -> MemoryExtensionResult { - let Some(additional_cost) = self.memory.record_new_len(new_len) else { - return MemoryExtensionResult::Same; - }; - - if !self.record_cost(additional_cost) { - return MemoryExtensionResult::OutOfGas; - } - - MemoryExtensionResult::Extended + /// Records an explicit cost. In case of underflow the gas will wrap around cost. + /// + /// Returns `true` if the gas limit is exceeded. + #[inline(always)] + #[must_use = "In case of not enough gas, the interpreter should halt with an out-of-gas error"] + pub fn record_cost_unsafe(&mut self, cost: u64) -> bool { + let oog = self.remaining < cost; + self.remaining = self.remaining.wrapping_sub(cost); + oog } } diff --git a/crates/interpreter/src/instruction_context.rs b/crates/interpreter/src/instruction_context.rs index d8aa146829..37e48a6565 100644 --- a/crates/interpreter/src/instruction_context.rs +++ b/crates/interpreter/src/instruction_context.rs @@ -1,6 +1,4 @@ -use crate::{interpreter_types::Jumps, Interpreter, InterpreterTypes}; - -use super::Instruction; +use crate::{Interpreter, InterpreterTypes}; /// Context passed to instruction implementations containing the host and interpreter. /// This struct provides access to both the host interface for external state operations @@ -20,22 +18,3 @@ impl std::fmt::Debug for InstructionContext<'_ .finish() } } - -impl InstructionContext<'_, H, ITy> { - /// Executes the instruction at the current instruction pointer. - /// - /// Internally it will increment instruction pointer by one. - #[inline] - pub(crate) fn step(self, instruction_table: &[Instruction; 256]) { - // Get current opcode. - let opcode = self.interpreter.bytecode.opcode(); - - // SAFETY: In analysis we are doing padding of bytecode so that we are sure that last - // byte instruction is STOP so we are safe to just increment program_counter bcs on last instruction - // it will do noop and just stop execution of this contract - self.interpreter.bytecode.relative_jump(1); - - // Execute instruction. - instruction_table[opcode as usize](self) - } -} diff --git a/crates/interpreter/src/instructions.rs b/crates/interpreter/src/instructions.rs index 63ff98f6b6..790a526f77 100644 --- a/crates/interpreter/src/instructions.rs +++ b/crates/interpreter/src/instructions.rs @@ -30,184 +30,222 @@ pub mod utility; use crate::{interpreter_types::InterpreterTypes, Host, InstructionContext}; /// EVM opcode function signature. -pub type Instruction = fn(InstructionContext<'_, H, W>); +#[derive(Debug, PartialEq, Eq)] +pub struct Instruction { + fn_: fn(InstructionContext<'_, H, W>), + static_gas: u64, +} + +impl Instruction { + /// Creates a new instruction with the given function and static gas cost. + #[inline] + pub const fn new(fn_: fn(InstructionContext<'_, H, W>), static_gas: u64) -> Self { + Self { fn_, static_gas } + } + + /// Creates an unknown/invalid instruction. + #[inline] + pub const fn unknown() -> Self { + Self { + fn_: control::unknown, + static_gas: 0, + } + } + + /// Executes the instruction with the given context. + #[inline(always)] + pub fn execute(self, ctx: InstructionContext<'_, H, W>) { + (self.fn_)(ctx) + } + + /// Returns the static gas cost of this instruction. + #[inline(always)] + pub const fn static_gas(&self) -> u64 { + self.static_gas + } +} + +impl Copy for Instruction {} +impl Clone for Instruction { + fn clone(&self) -> Self { + *self + } +} /// Instruction table is list of instruction function pointers mapped to 256 EVM opcodes. pub type InstructionTable = [Instruction; 256]; /// Returns the default instruction table for the given interpreter types and host. #[inline] -pub const fn instruction_table( -) -> [Instruction; 256] { +pub const fn instruction_table() -> [Instruction; 256] { const { instruction_table_impl::() } } -const fn instruction_table_impl( -) -> [Instruction; 256] { +const fn instruction_table_impl() -> [Instruction; 256] { use bytecode::opcode::*; - let mut table = [control::unknown as Instruction; 256]; - - table[STOP as usize] = control::stop; - table[ADD as usize] = arithmetic::add; - table[MUL as usize] = arithmetic::mul; - table[SUB as usize] = arithmetic::sub; - table[DIV as usize] = arithmetic::div; - table[SDIV as usize] = arithmetic::sdiv; - table[MOD as usize] = arithmetic::rem; - table[SMOD as usize] = arithmetic::smod; - table[ADDMOD as usize] = arithmetic::addmod; - table[MULMOD as usize] = arithmetic::mulmod; - table[EXP as usize] = arithmetic::exp; - table[SIGNEXTEND as usize] = arithmetic::signextend; - - table[LT as usize] = bitwise::lt; - table[GT as usize] = bitwise::gt; - table[SLT as usize] = bitwise::slt; - table[SGT as usize] = bitwise::sgt; - table[EQ as usize] = bitwise::eq; - table[ISZERO as usize] = bitwise::iszero; - table[AND as usize] = bitwise::bitand; - table[OR as usize] = bitwise::bitor; - table[XOR as usize] = bitwise::bitxor; - table[NOT as usize] = bitwise::not; - table[BYTE as usize] = bitwise::byte; - table[SHL as usize] = bitwise::shl; - table[SHR as usize] = bitwise::shr; - table[SAR as usize] = bitwise::sar; - table[CLZ as usize] = bitwise::clz; - - table[KECCAK256 as usize] = system::keccak256; - - table[ADDRESS as usize] = system::address; - table[BALANCE as usize] = host::balance; - table[ORIGIN as usize] = tx_info::origin; - table[CALLER as usize] = system::caller; - table[CALLVALUE as usize] = system::callvalue; - table[CALLDATALOAD as usize] = system::calldataload; - table[CALLDATASIZE as usize] = system::calldatasize; - table[CALLDATACOPY as usize] = system::calldatacopy; - table[CODESIZE as usize] = system::codesize; - table[CODECOPY as usize] = system::codecopy; - - table[GASPRICE as usize] = tx_info::gasprice; - table[EXTCODESIZE as usize] = host::extcodesize; - table[EXTCODECOPY as usize] = host::extcodecopy; - table[RETURNDATASIZE as usize] = system::returndatasize; - table[RETURNDATACOPY as usize] = system::returndatacopy; - table[EXTCODEHASH as usize] = host::extcodehash; - table[BLOCKHASH as usize] = host::blockhash; - table[COINBASE as usize] = block_info::coinbase; - table[TIMESTAMP as usize] = block_info::timestamp; - table[NUMBER as usize] = block_info::block_number; - table[DIFFICULTY as usize] = block_info::difficulty; - table[GASLIMIT as usize] = block_info::gaslimit; - table[CHAINID as usize] = block_info::chainid; - table[SELFBALANCE as usize] = host::selfbalance; - table[BASEFEE as usize] = block_info::basefee; - table[BLOBHASH as usize] = tx_info::blob_hash; - table[BLOBBASEFEE as usize] = block_info::blob_basefee; - - table[POP as usize] = stack::pop; - table[MLOAD as usize] = memory::mload; - table[MSTORE as usize] = memory::mstore; - table[MSTORE8 as usize] = memory::mstore8; - table[SLOAD as usize] = host::sload; - table[SSTORE as usize] = host::sstore; - table[JUMP as usize] = control::jump; - table[JUMPI as usize] = control::jumpi; - table[PC as usize] = control::pc; - table[MSIZE as usize] = memory::msize; - table[GAS as usize] = system::gas; - table[JUMPDEST as usize] = control::jumpdest; - table[TLOAD as usize] = host::tload; - table[TSTORE as usize] = host::tstore; - table[MCOPY as usize] = memory::mcopy; - - table[PUSH0 as usize] = stack::push0; - table[PUSH1 as usize] = stack::push::<1, _, _>; - table[PUSH2 as usize] = stack::push::<2, _, _>; - table[PUSH3 as usize] = stack::push::<3, _, _>; - table[PUSH4 as usize] = stack::push::<4, _, _>; - table[PUSH5 as usize] = stack::push::<5, _, _>; - table[PUSH6 as usize] = stack::push::<6, _, _>; - table[PUSH7 as usize] = stack::push::<7, _, _>; - table[PUSH8 as usize] = stack::push::<8, _, _>; - table[PUSH9 as usize] = stack::push::<9, _, _>; - table[PUSH10 as usize] = stack::push::<10, _, _>; - table[PUSH11 as usize] = stack::push::<11, _, _>; - table[PUSH12 as usize] = stack::push::<12, _, _>; - table[PUSH13 as usize] = stack::push::<13, _, _>; - table[PUSH14 as usize] = stack::push::<14, _, _>; - table[PUSH15 as usize] = stack::push::<15, _, _>; - table[PUSH16 as usize] = stack::push::<16, _, _>; - table[PUSH17 as usize] = stack::push::<17, _, _>; - table[PUSH18 as usize] = stack::push::<18, _, _>; - table[PUSH19 as usize] = stack::push::<19, _, _>; - table[PUSH20 as usize] = stack::push::<20, _, _>; - table[PUSH21 as usize] = stack::push::<21, _, _>; - table[PUSH22 as usize] = stack::push::<22, _, _>; - table[PUSH23 as usize] = stack::push::<23, _, _>; - table[PUSH24 as usize] = stack::push::<24, _, _>; - table[PUSH25 as usize] = stack::push::<25, _, _>; - table[PUSH26 as usize] = stack::push::<26, _, _>; - table[PUSH27 as usize] = stack::push::<27, _, _>; - table[PUSH28 as usize] = stack::push::<28, _, _>; - table[PUSH29 as usize] = stack::push::<29, _, _>; - table[PUSH30 as usize] = stack::push::<30, _, _>; - table[PUSH31 as usize] = stack::push::<31, _, _>; - table[PUSH32 as usize] = stack::push::<32, _, _>; - - table[DUP1 as usize] = stack::dup::<1, _, _>; - table[DUP2 as usize] = stack::dup::<2, _, _>; - table[DUP3 as usize] = stack::dup::<3, _, _>; - table[DUP4 as usize] = stack::dup::<4, _, _>; - table[DUP5 as usize] = stack::dup::<5, _, _>; - table[DUP6 as usize] = stack::dup::<6, _, _>; - table[DUP7 as usize] = stack::dup::<7, _, _>; - table[DUP8 as usize] = stack::dup::<8, _, _>; - table[DUP9 as usize] = stack::dup::<9, _, _>; - table[DUP10 as usize] = stack::dup::<10, _, _>; - table[DUP11 as usize] = stack::dup::<11, _, _>; - table[DUP12 as usize] = stack::dup::<12, _, _>; - table[DUP13 as usize] = stack::dup::<13, _, _>; - table[DUP14 as usize] = stack::dup::<14, _, _>; - table[DUP15 as usize] = stack::dup::<15, _, _>; - table[DUP16 as usize] = stack::dup::<16, _, _>; - - table[SWAP1 as usize] = stack::swap::<1, _, _>; - table[SWAP2 as usize] = stack::swap::<2, _, _>; - table[SWAP3 as usize] = stack::swap::<3, _, _>; - table[SWAP4 as usize] = stack::swap::<4, _, _>; - table[SWAP5 as usize] = stack::swap::<5, _, _>; - table[SWAP6 as usize] = stack::swap::<6, _, _>; - table[SWAP7 as usize] = stack::swap::<7, _, _>; - table[SWAP8 as usize] = stack::swap::<8, _, _>; - table[SWAP9 as usize] = stack::swap::<9, _, _>; - table[SWAP10 as usize] = stack::swap::<10, _, _>; - table[SWAP11 as usize] = stack::swap::<11, _, _>; - table[SWAP12 as usize] = stack::swap::<12, _, _>; - table[SWAP13 as usize] = stack::swap::<13, _, _>; - table[SWAP14 as usize] = stack::swap::<14, _, _>; - table[SWAP15 as usize] = stack::swap::<15, _, _>; - table[SWAP16 as usize] = stack::swap::<16, _, _>; - - table[LOG0 as usize] = host::log::<0, _>; - table[LOG1 as usize] = host::log::<1, _>; - table[LOG2 as usize] = host::log::<2, _>; - table[LOG3 as usize] = host::log::<3, _>; - table[LOG4 as usize] = host::log::<4, _>; - - table[CREATE as usize] = contract::create::<_, false, _>; - table[CALL as usize] = contract::call; - table[CALLCODE as usize] = contract::call_code; - table[RETURN as usize] = control::ret; - table[DELEGATECALL as usize] = contract::delegate_call; - table[CREATE2 as usize] = contract::create::<_, true, _>; - - table[STATICCALL as usize] = contract::static_call; - table[REVERT as usize] = control::revert; - table[INVALID as usize] = control::invalid; - table[SELFDESTRUCT as usize] = host::selfdestruct; + let mut table = [Instruction::unknown(); 256]; + + table[STOP as usize] = Instruction::new(control::stop, 0); + table[ADD as usize] = Instruction::new(arithmetic::add, 3); + table[MUL as usize] = Instruction::new(arithmetic::mul, 5); + table[SUB as usize] = Instruction::new(arithmetic::sub, 3); + table[DIV as usize] = Instruction::new(arithmetic::div, 5); + table[SDIV as usize] = Instruction::new(arithmetic::sdiv, 5); + table[MOD as usize] = Instruction::new(arithmetic::rem, 5); + table[SMOD as usize] = Instruction::new(arithmetic::smod, 5); + table[ADDMOD as usize] = Instruction::new(arithmetic::addmod, 8); + table[MULMOD as usize] = Instruction::new(arithmetic::mulmod, 8); + table[EXP as usize] = Instruction::new(arithmetic::exp, 0); // dynamic + table[SIGNEXTEND as usize] = Instruction::new(arithmetic::signextend, 5); + + table[LT as usize] = Instruction::new(bitwise::lt, 3); + table[GT as usize] = Instruction::new(bitwise::gt, 3); + table[SLT as usize] = Instruction::new(bitwise::slt, 3); + table[SGT as usize] = Instruction::new(bitwise::sgt, 3); + table[EQ as usize] = Instruction::new(bitwise::eq, 3); + table[ISZERO as usize] = Instruction::new(bitwise::iszero, 3); + table[AND as usize] = Instruction::new(bitwise::bitand, 3); + table[OR as usize] = Instruction::new(bitwise::bitor, 3); + table[XOR as usize] = Instruction::new(bitwise::bitxor, 3); + table[NOT as usize] = Instruction::new(bitwise::not, 3); + table[BYTE as usize] = Instruction::new(bitwise::byte, 3); + table[SHL as usize] = Instruction::new(bitwise::shl, 3); + table[SHR as usize] = Instruction::new(bitwise::shr, 3); + table[SAR as usize] = Instruction::new(bitwise::sar, 3); + table[CLZ as usize] = Instruction::new(bitwise::clz, 5); + + table[KECCAK256 as usize] = Instruction::new(system::keccak256, 0); // dynamic + + table[ADDRESS as usize] = Instruction::new(system::address, 2); + table[BALANCE as usize] = Instruction::new(host::balance, 0); // dynamic + table[ORIGIN as usize] = Instruction::new(tx_info::origin, 2); + table[CALLER as usize] = Instruction::new(system::caller, 2); + table[CALLVALUE as usize] = Instruction::new(system::callvalue, 2); + table[CALLDATALOAD as usize] = Instruction::new(system::calldataload, 3); + table[CALLDATASIZE as usize] = Instruction::new(system::calldatasize, 2); + table[CALLDATACOPY as usize] = Instruction::new(system::calldatacopy, 0); // static 2, mostly dynamic + table[CODESIZE as usize] = Instruction::new(system::codesize, 2); + table[CODECOPY as usize] = Instruction::new(system::codecopy, 0); // static 2, mostly dynamic + + table[GASPRICE as usize] = Instruction::new(tx_info::gasprice, 2); + table[EXTCODESIZE as usize] = Instruction::new(host::extcodesize, 0); // dynamic + table[EXTCODECOPY as usize] = Instruction::new(host::extcodecopy, 0); // dynamic + table[RETURNDATASIZE as usize] = Instruction::new(system::returndatasize, 2); + table[RETURNDATACOPY as usize] = Instruction::new(system::returndatacopy, 0); // static 2, mostly dynamic + table[EXTCODEHASH as usize] = Instruction::new(host::extcodehash, 0); // dynamic + table[BLOCKHASH as usize] = Instruction::new(host::blockhash, 20); + table[COINBASE as usize] = Instruction::new(block_info::coinbase, 2); + table[TIMESTAMP as usize] = Instruction::new(block_info::timestamp, 2); + table[NUMBER as usize] = Instruction::new(block_info::block_number, 2); + table[DIFFICULTY as usize] = Instruction::new(block_info::difficulty, 2); + table[GASLIMIT as usize] = Instruction::new(block_info::gaslimit, 2); + table[CHAINID as usize] = Instruction::new(block_info::chainid, 2); + table[SELFBALANCE as usize] = Instruction::new(host::selfbalance, 5); + table[BASEFEE as usize] = Instruction::new(block_info::basefee, 2); + table[BLOBHASH as usize] = Instruction::new(tx_info::blob_hash, 3); + table[BLOBBASEFEE as usize] = Instruction::new(block_info::blob_basefee, 2); + + table[POP as usize] = Instruction::new(stack::pop, 2); + table[MLOAD as usize] = Instruction::new(memory::mload, 3); + table[MSTORE as usize] = Instruction::new(memory::mstore, 3); + table[MSTORE8 as usize] = Instruction::new(memory::mstore8, 3); + table[SLOAD as usize] = Instruction::new(host::sload, 0); // dynamic + table[SSTORE as usize] = Instruction::new(host::sstore, 0); // dynamic + table[JUMP as usize] = Instruction::new(control::jump, 8); + table[JUMPI as usize] = Instruction::new(control::jumpi, 10); + table[PC as usize] = Instruction::new(control::pc, 2); + table[MSIZE as usize] = Instruction::new(memory::msize, 2); + table[GAS as usize] = Instruction::new(system::gas, 2); + table[JUMPDEST as usize] = Instruction::new(control::jumpdest, 1); + table[TLOAD as usize] = Instruction::new(host::tload, 100); + table[TSTORE as usize] = Instruction::new(host::tstore, 100); + table[MCOPY as usize] = Instruction::new(memory::mcopy, 0); // static 2, mostly dynamic + + table[PUSH0 as usize] = Instruction::new(stack::push0, 2); + table[PUSH1 as usize] = Instruction::new(stack::push::<1, _, _>, 3); + table[PUSH2 as usize] = Instruction::new(stack::push::<2, _, _>, 3); + table[PUSH3 as usize] = Instruction::new(stack::push::<3, _, _>, 3); + table[PUSH4 as usize] = Instruction::new(stack::push::<4, _, _>, 3); + table[PUSH5 as usize] = Instruction::new(stack::push::<5, _, _>, 3); + table[PUSH6 as usize] = Instruction::new(stack::push::<6, _, _>, 3); + table[PUSH7 as usize] = Instruction::new(stack::push::<7, _, _>, 3); + table[PUSH8 as usize] = Instruction::new(stack::push::<8, _, _>, 3); + table[PUSH9 as usize] = Instruction::new(stack::push::<9, _, _>, 3); + table[PUSH10 as usize] = Instruction::new(stack::push::<10, _, _>, 3); + table[PUSH11 as usize] = Instruction::new(stack::push::<11, _, _>, 3); + table[PUSH12 as usize] = Instruction::new(stack::push::<12, _, _>, 3); + table[PUSH13 as usize] = Instruction::new(stack::push::<13, _, _>, 3); + table[PUSH14 as usize] = Instruction::new(stack::push::<14, _, _>, 3); + table[PUSH15 as usize] = Instruction::new(stack::push::<15, _, _>, 3); + table[PUSH16 as usize] = Instruction::new(stack::push::<16, _, _>, 3); + table[PUSH17 as usize] = Instruction::new(stack::push::<17, _, _>, 3); + table[PUSH18 as usize] = Instruction::new(stack::push::<18, _, _>, 3); + table[PUSH19 as usize] = Instruction::new(stack::push::<19, _, _>, 3); + table[PUSH20 as usize] = Instruction::new(stack::push::<20, _, _>, 3); + table[PUSH21 as usize] = Instruction::new(stack::push::<21, _, _>, 3); + table[PUSH22 as usize] = Instruction::new(stack::push::<22, _, _>, 3); + table[PUSH23 as usize] = Instruction::new(stack::push::<23, _, _>, 3); + table[PUSH24 as usize] = Instruction::new(stack::push::<24, _, _>, 3); + table[PUSH25 as usize] = Instruction::new(stack::push::<25, _, _>, 3); + table[PUSH26 as usize] = Instruction::new(stack::push::<26, _, _>, 3); + table[PUSH27 as usize] = Instruction::new(stack::push::<27, _, _>, 3); + table[PUSH28 as usize] = Instruction::new(stack::push::<28, _, _>, 3); + table[PUSH29 as usize] = Instruction::new(stack::push::<29, _, _>, 3); + table[PUSH30 as usize] = Instruction::new(stack::push::<30, _, _>, 3); + table[PUSH31 as usize] = Instruction::new(stack::push::<31, _, _>, 3); + table[PUSH32 as usize] = Instruction::new(stack::push::<32, _, _>, 3); + + table[DUP1 as usize] = Instruction::new(stack::dup::<1, _, _>, 3); + table[DUP2 as usize] = Instruction::new(stack::dup::<2, _, _>, 3); + table[DUP3 as usize] = Instruction::new(stack::dup::<3, _, _>, 3); + table[DUP4 as usize] = Instruction::new(stack::dup::<4, _, _>, 3); + table[DUP5 as usize] = Instruction::new(stack::dup::<5, _, _>, 3); + table[DUP6 as usize] = Instruction::new(stack::dup::<6, _, _>, 3); + table[DUP7 as usize] = Instruction::new(stack::dup::<7, _, _>, 3); + table[DUP8 as usize] = Instruction::new(stack::dup::<8, _, _>, 3); + table[DUP9 as usize] = Instruction::new(stack::dup::<9, _, _>, 3); + table[DUP10 as usize] = Instruction::new(stack::dup::<10, _, _>, 3); + table[DUP11 as usize] = Instruction::new(stack::dup::<11, _, _>, 3); + table[DUP12 as usize] = Instruction::new(stack::dup::<12, _, _>, 3); + table[DUP13 as usize] = Instruction::new(stack::dup::<13, _, _>, 3); + table[DUP14 as usize] = Instruction::new(stack::dup::<14, _, _>, 3); + table[DUP15 as usize] = Instruction::new(stack::dup::<15, _, _>, 3); + table[DUP16 as usize] = Instruction::new(stack::dup::<16, _, _>, 3); + + table[SWAP1 as usize] = Instruction::new(stack::swap::<1, _, _>, 3); + table[SWAP2 as usize] = Instruction::new(stack::swap::<2, _, _>, 3); + table[SWAP3 as usize] = Instruction::new(stack::swap::<3, _, _>, 3); + table[SWAP4 as usize] = Instruction::new(stack::swap::<4, _, _>, 3); + table[SWAP5 as usize] = Instruction::new(stack::swap::<5, _, _>, 3); + table[SWAP6 as usize] = Instruction::new(stack::swap::<6, _, _>, 3); + table[SWAP7 as usize] = Instruction::new(stack::swap::<7, _, _>, 3); + table[SWAP8 as usize] = Instruction::new(stack::swap::<8, _, _>, 3); + table[SWAP9 as usize] = Instruction::new(stack::swap::<9, _, _>, 3); + table[SWAP10 as usize] = Instruction::new(stack::swap::<10, _, _>, 3); + table[SWAP11 as usize] = Instruction::new(stack::swap::<11, _, _>, 3); + table[SWAP12 as usize] = Instruction::new(stack::swap::<12, _, _>, 3); + table[SWAP13 as usize] = Instruction::new(stack::swap::<13, _, _>, 3); + table[SWAP14 as usize] = Instruction::new(stack::swap::<14, _, _>, 3); + table[SWAP15 as usize] = Instruction::new(stack::swap::<15, _, _>, 3); + table[SWAP16 as usize] = Instruction::new(stack::swap::<16, _, _>, 3); + + table[LOG0 as usize] = Instruction::new(host::log::<0, _>, 0); // dynamic + table[LOG1 as usize] = Instruction::new(host::log::<1, _>, 0); // dynamic + table[LOG2 as usize] = Instruction::new(host::log::<2, _>, 0); // dynamic + table[LOG3 as usize] = Instruction::new(host::log::<3, _>, 0); // dynamic + table[LOG4 as usize] = Instruction::new(host::log::<4, _>, 0); // dynamic + + table[CREATE as usize] = Instruction::new(contract::create::<_, false, _>, 0); // dynamic + table[CALL as usize] = Instruction::new(contract::call, 0); // dynamic + table[CALLCODE as usize] = Instruction::new(contract::call_code, 0); // dynamic + table[RETURN as usize] = Instruction::new(control::ret, 0); + table[DELEGATECALL as usize] = Instruction::new(contract::delegate_call, 0); // dynamic + table[CREATE2 as usize] = Instruction::new(contract::create::<_, true, _>, 0); // dynamic + + table[STATICCALL as usize] = Instruction::new(contract::static_call, 0); // dynamic + table[REVERT as usize] = Instruction::new(control::revert, 0); + table[INVALID as usize] = Instruction::new(control::invalid, 0); + table[SELFDESTRUCT as usize] = Instruction::new(host::selfdestruct, 0); // dynamic table } @@ -227,7 +265,7 @@ mod tests { for (i, instr) in instr_table.iter().enumerate() { let is_opcode_unknown = OpCode::new(i as u8).is_none(); // - let is_instr_unknown = std::ptr::fn_addr_eq(*instr, unknown_istr); + let is_instr_unknown = std::ptr::fn_addr_eq(instr.fn_, unknown_istr.fn_); assert_eq!( is_instr_unknown, is_opcode_unknown, "Opcode 0x{i:X?} is not handled", diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index 7575cd167d..68333c9385 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -8,28 +8,28 @@ use primitives::U256; /// Implements the ADD instruction - adds two values from stack. pub fn add(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); *op2 = op1.wrapping_add(*op2); } /// Implements the MUL instruction - multiplies two values from stack. pub fn mul(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::LOW); + //gas!(context.interpreter, gas::LOW); popn_top!([op1], op2, context.interpreter); *op2 = op1.wrapping_mul(*op2); } /// Implements the SUB instruction - subtracts two values from stack. pub fn sub(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); *op2 = op1.wrapping_sub(*op2); } /// Implements the DIV instruction - divides two values from stack. pub fn div(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::LOW); + //gas!(context.interpreter, gas::LOW); popn_top!([op1], op2, context.interpreter); if !op2.is_zero() { *op2 = op1.wrapping_div(*op2); @@ -40,7 +40,7 @@ pub fn div(context: InstructionContext<'_, H, /// /// Performs signed division of two values from stack. pub fn sdiv(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::LOW); + //gas!(context.interpreter, gas::LOW); popn_top!([op1], op2, context.interpreter); *op2 = i256_div(op1, *op2); } @@ -49,7 +49,7 @@ pub fn sdiv(context: InstructionContext<'_, H /// /// Pops two values from stack and pushes the remainder of their division. pub fn rem(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::LOW); + //gas!(context.interpreter, gas::LOW); popn_top!([op1], op2, context.interpreter); if !op2.is_zero() { *op2 = op1.wrapping_rem(*op2); @@ -60,7 +60,7 @@ pub fn rem(context: InstructionContext<'_, H, /// /// Performs signed modulo of two values from stack. pub fn smod(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::LOW); + //gas!(context.interpreter, gas::LOW); popn_top!([op1], op2, context.interpreter); *op2 = i256_mod(op1, *op2) } @@ -69,7 +69,7 @@ pub fn smod(context: InstructionContext<'_, H /// /// Pops three values from stack and pushes (a + b) % n. pub fn addmod(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::MID); + //gas!(context.interpreter, gas::MID); popn_top!([op1, op2], op3, context.interpreter); *op3 = op1.add_mod(op2, *op3) } @@ -78,7 +78,7 @@ pub fn addmod(context: InstructionContext<'_, /// /// Pops three values from stack and pushes (a * b) % n. pub fn mulmod(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::MID); + //gas!(context.interpreter, gas::MID); popn_top!([op1, op2], op3, context.interpreter); *op3 = op1.mul_mod(op2, *op3) } @@ -121,7 +121,7 @@ pub fn exp(context: InstructionContext<'_, H, /// Similarly, if `b == 0` then the yellow paper says the output should start with all zeros, /// then end with bits from `b`; this is equal to `y & mask` where `&` is bitwise `AND`. pub fn signextend(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::LOW); + //gas!(context.interpreter, gas::LOW); popn_top!([ext], x, context.interpreter); // For 31 we also don't need to do anything. if ext < U256::from(31) { diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index 18696e7871..d07f7638e1 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -1,6 +1,5 @@ use super::i256::i256_cmp; use crate::{ - gas, interpreter_types::{InterpreterTypes, RuntimeFlag, StackTr}, InstructionContext, }; @@ -9,14 +8,14 @@ use primitives::U256; /// Implements the LT instruction - less than comparison. pub fn lt(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); *op2 = U256::from(op1 < *op2); } /// Implements the GT instruction - greater than comparison. pub fn gt(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); *op2 = U256::from(op1 > *op2); @@ -25,7 +24,7 @@ pub fn gt(context: InstructionContext<'_, H, /// Implements the CLZ instruction - count leading zeros. pub fn clz(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, OSAKA); - gas!(context.interpreter, gas::LOW); + //gas!(context.interpreter, gas::LOW); popn_top!([], op1, context.interpreter); let leading_zeros = op1.leading_zeros(); @@ -36,7 +35,7 @@ pub fn clz(context: InstructionContext<'_, H, /// /// Signed less than comparison of two values from stack. pub fn slt(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); *op2 = U256::from(i256_cmp(&op1, op2) == Ordering::Less); @@ -46,7 +45,7 @@ pub fn slt(context: InstructionContext<'_, H, /// /// Signed greater than comparison of two values from stack. pub fn sgt(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); *op2 = U256::from(i256_cmp(&op1, op2) == Ordering::Greater); @@ -56,7 +55,7 @@ pub fn sgt(context: InstructionContext<'_, H, /// /// Equality comparison of two values from stack. pub fn eq(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); *op2 = U256::from(op1 == *op2); @@ -66,7 +65,7 @@ pub fn eq(context: InstructionContext<'_, H, /// /// Checks if the top stack value is zero. pub fn iszero(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([], op1, context.interpreter); *op1 = U256::from(op1.is_zero()); } @@ -75,7 +74,7 @@ pub fn iszero(context: InstructionContext<'_, /// /// Bitwise AND of two values from stack. pub fn bitand(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); *op2 = op1 & *op2; } @@ -84,7 +83,7 @@ pub fn bitand(context: InstructionContext<'_, /// /// Bitwise OR of two values from stack. pub fn bitor(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); *op2 = op1 | *op2; @@ -94,7 +93,7 @@ pub fn bitor(context: InstructionContext<'_, /// /// Bitwise XOR of two values from stack. pub fn bitxor(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); *op2 = op1 ^ *op2; @@ -104,7 +103,7 @@ pub fn bitxor(context: InstructionContext<'_, /// /// Bitwise NOT (negation) of the top stack value. pub fn not(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([], op1, context.interpreter); *op1 = !*op1; @@ -114,7 +113,7 @@ pub fn not(context: InstructionContext<'_, H, /// /// Extracts a single byte from a word at a given index. pub fn byte(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); let o1 = as_usize_saturated!(op1); @@ -129,7 +128,7 @@ pub fn byte(context: InstructionContext<'_, H /// EIP-145: Bitwise shifting instructions in EVM pub fn shl(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, CONSTANTINOPLE); - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); let shift = as_usize_saturated!(op1); @@ -143,7 +142,7 @@ pub fn shl(context: InstructionContext<'_, H, /// EIP-145: Bitwise shifting instructions in EVM pub fn shr(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, CONSTANTINOPLE); - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); let shift = as_usize_saturated!(op1); @@ -157,7 +156,7 @@ pub fn shr(context: InstructionContext<'_, H, /// EIP-145: Bitwise shifting instructions in EVM pub fn sar(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, CONSTANTINOPLE); - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); let shift = as_usize_saturated!(op1); diff --git a/crates/interpreter/src/instructions/block_info.rs b/crates/interpreter/src/instructions/block_info.rs index 2e7b251c4b..0dff4a6ad1 100644 --- a/crates/interpreter/src/instructions/block_info.rs +++ b/crates/interpreter/src/instructions/block_info.rs @@ -1,5 +1,4 @@ use crate::{ - gas, interpreter_types::{InterpreterTypes, RuntimeFlag, StackTr}, Host, }; @@ -10,7 +9,7 @@ use crate::InstructionContext; /// EIP-1344: ChainID opcode pub fn chainid(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, ISTANBUL); - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!(context.interpreter, context.host.chain_id()); } @@ -20,7 +19,7 @@ pub fn chainid(context: InstructionCon pub fn coinbase( context: InstructionContext<'_, H, WIRE>, ) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!( context.interpreter, context.host.beneficiary().into_word().into() @@ -33,7 +32,7 @@ pub fn coinbase( pub fn timestamp( context: InstructionContext<'_, H, WIRE>, ) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!(context.interpreter, context.host.timestamp()); } @@ -43,7 +42,7 @@ pub fn timestamp( pub fn block_number( context: InstructionContext<'_, H, WIRE>, ) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!(context.interpreter, U256::from(context.host.block_number())); } @@ -53,7 +52,7 @@ pub fn block_number( pub fn difficulty( context: InstructionContext<'_, H, WIRE>, ) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); if context .interpreter .runtime_flag @@ -73,14 +72,14 @@ pub fn difficulty( pub fn gaslimit( context: InstructionContext<'_, H, WIRE>, ) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!(context.interpreter, context.host.gas_limit()); } /// EIP-3198: BASEFEE opcode pub fn basefee(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, LONDON); - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!(context.interpreter, context.host.basefee()); } @@ -89,6 +88,6 @@ pub fn blob_basefee( context: InstructionContext<'_, H, WIRE>, ) { check!(context.interpreter, CANCUN); - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!(context.interpreter, context.host.blob_gasprice()); } diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs index 58502560ca..66224360c4 100644 --- a/crates/interpreter/src/instructions/control.rs +++ b/crates/interpreter/src/instructions/control.rs @@ -1,5 +1,4 @@ use crate::{ - gas, interpreter::Interpreter, interpreter_types::{InterpreterTypes, Jumps, LoopControl, MemoryTr, RuntimeFlag, StackTr}, InstructionResult, InterpreterAction, @@ -12,7 +11,7 @@ use crate::InstructionContext; /// /// Unconditional jump to a valid destination. pub fn jump(context: InstructionContext<'_, H, ITy>) { - gas!(context.interpreter, gas::MID); + //gas!(context.interpreter, gas::MID); popn!([target], context.interpreter); jump_inner(context.interpreter, target); } @@ -21,7 +20,7 @@ pub fn jump(context: InstructionContext<'_, H, /// /// Conditional jump to a valid destination if condition is true. pub fn jumpi(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::HIGH); + //gas!(context.interpreter, gas::HIGH); popn!([target, cond], context.interpreter); if !cond.is_zero() { @@ -46,15 +45,15 @@ fn jump_inner(interpreter: &mut Interpreter, targe /// Implements the JUMPDEST instruction. /// /// Marks a valid destination for jump operations. -pub fn jumpdest(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::JUMPDEST); +pub fn jumpdest(_context: InstructionContext<'_, H, WIRE>) { + //gas!(context.interpreter, gas::JUMPDEST); } /// Implements the PC instruction. /// /// Pushes the current program counter onto the stack. pub fn pc(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); // - 1 because we have already advanced the instruction pointer in `Interpreter::step` push!( context.interpreter, @@ -71,7 +70,7 @@ fn return_inner( instruction_result: InstructionResult, ) { // Zero gas cost - // gas!(interpreter, gas::ZERO) + // //gas!(interpreter, gas::ZERO) popn!([offset, len], interpreter); let len = as_usize_or_fail!(interpreter, len); // Important: Offset must be ignored if len is zeros diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 70245220e8..4fb362e7a9 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -43,7 +43,7 @@ pub fn selfbalance( context: InstructionContext<'_, H, WIRE>, ) { check!(context.interpreter, ISTANBUL); - gas!(context.interpreter, gas::LOW); + //gas!(context.interpreter, gas::LOW); let Some(balance) = context .host @@ -154,7 +154,7 @@ pub fn extcodecopy( pub fn blockhash( context: InstructionContext<'_, H, WIRE>, ) { - gas!(context.interpreter, gas::BLOCKHASH); + //gas!(context.interpreter, gas::BLOCKHASH); popn_top!([], number, context.interpreter); let requested_number = *number; @@ -261,7 +261,7 @@ pub fn sstore(context: InstructionCont pub fn tstore(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, CANCUN); require_non_staticcall!(context.interpreter); - gas!(context.interpreter, gas::WARM_STORAGE_READ_COST); + //gas!(context.interpreter, gas::WARM_STORAGE_READ_COST); popn!([index, value], context.interpreter); @@ -274,7 +274,7 @@ pub fn tstore(context: InstructionCont /// Load value from transient storage pub fn tload(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, CANCUN); - gas!(context.interpreter, gas::WARM_STORAGE_READ_COST); + //gas!(context.interpreter, gas::WARM_STORAGE_READ_COST); popn_top!([], index, context.interpreter); diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index 300a7bfdac..cb62af05df 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -62,7 +62,7 @@ macro_rules! gas { }; ($interpreter:expr, $gas:expr, $ret:expr) => { if !$interpreter.gas.record_cost($gas) { - $interpreter.halt($crate::InstructionResult::OutOfGas); + $interpreter.halt_oog(); return $ret; } }; @@ -79,7 +79,7 @@ macro_rules! gas_or_fail { match $gas { Some(gas_used) => $crate::gas!($interpreter, gas_used, $ret), None => { - $interpreter.halt($crate::InstructionResult::OutOfGas); + $interpreter.halt_oog(); return $ret; } } diff --git a/crates/interpreter/src/instructions/memory.rs b/crates/interpreter/src/instructions/memory.rs index dff34d12c1..9a48d88f3d 100644 --- a/crates/interpreter/src/instructions/memory.rs +++ b/crates/interpreter/src/instructions/memory.rs @@ -11,7 +11,7 @@ use crate::InstructionContext; /// /// Loads a 32-byte word from memory. pub fn mload(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([], top, context.interpreter); let offset = as_usize_or_fail!(context.interpreter, top); resize_memory!(context.interpreter, offset, 32); @@ -23,7 +23,7 @@ pub fn mload(context: InstructionContext<'_, /// /// Stores a 32-byte word to memory. pub fn mstore(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn!([offset, value], context.interpreter); let offset = as_usize_or_fail!(context.interpreter, offset); resize_memory!(context.interpreter, offset, 32); @@ -37,7 +37,7 @@ pub fn mstore(context: InstructionContext<'_, /// /// Stores a single byte to memory. pub fn mstore8(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn!([offset, value], context.interpreter); let offset = as_usize_or_fail!(context.interpreter, offset); resize_memory!(context.interpreter, offset, 1); @@ -48,7 +48,7 @@ pub fn mstore8(context: InstructionContext<'_ /// /// Gets the size of active memory in bytes. pub fn msize(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!( context.interpreter, U256::from(context.interpreter.memory.size()) diff --git a/crates/interpreter/src/instructions/stack.rs b/crates/interpreter/src/instructions/stack.rs index cdfdceccd0..822e6240d7 100644 --- a/crates/interpreter/src/instructions/stack.rs +++ b/crates/interpreter/src/instructions/stack.rs @@ -1,5 +1,4 @@ use crate::{ - gas, interpreter_types::{Immediates, InterpreterTypes, Jumps, RuntimeFlag, StackTr}, InstructionResult, }; @@ -11,7 +10,7 @@ use crate::InstructionContext; /// /// Removes the top item from the stack. pub fn pop(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); // Can ignore return. as relative N jump is safe operation. popn!([_i], context.interpreter); } @@ -21,7 +20,7 @@ pub fn pop(context: InstructionContext<'_, H, /// Introduce a new instruction which pushes the constant value 0 onto the stack. pub fn push0(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, SHANGHAI); - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!(context.interpreter, U256::ZERO); } @@ -31,7 +30,7 @@ pub fn push0(context: InstructionContext<'_, pub fn push( context: InstructionContext<'_, H, WIRE>, ) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); let slice = context.interpreter.bytecode.read_slice(N); if !context.interpreter.stack.push_slice(slice) { @@ -49,7 +48,7 @@ pub fn push( pub fn dup( context: InstructionContext<'_, H, WIRE>, ) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); if !context.interpreter.stack.dup(N) { context.interpreter.halt(InstructionResult::StackOverflow); } @@ -61,7 +60,7 @@ pub fn dup( pub fn swap( context: InstructionContext<'_, H, WIRE>, ) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); assert!(N != 0); if !context.interpreter.stack.exchange(0, N) { context.interpreter.halt(InstructionResult::StackOverflow); diff --git a/crates/interpreter/src/instructions/system.rs b/crates/interpreter/src/instructions/system.rs index 6d3ba86570..0bea684e7f 100644 --- a/crates/interpreter/src/instructions/system.rs +++ b/crates/interpreter/src/instructions/system.rs @@ -32,7 +32,7 @@ pub fn keccak256(context: InstructionContext< /// /// Pushes the current contract's address onto the stack. pub fn address(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!( context.interpreter, context @@ -48,7 +48,7 @@ pub fn address(context: InstructionContext<'_ /// /// Pushes the caller's address onto the stack. pub fn caller(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!( context.interpreter, context @@ -64,7 +64,7 @@ pub fn caller(context: InstructionContext<'_, /// /// Pushes the size of running contract's bytecode onto the stack. pub fn codesize(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!( context.interpreter, U256::from(context.interpreter.bytecode.bytecode_len()) @@ -95,7 +95,7 @@ pub fn codecopy(context: InstructionContext<' /// /// Loads 32 bytes of input data from the specified offset. pub fn calldataload(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([], offset_ptr, context.interpreter); let mut word = B256::ZERO; let offset = as_usize_saturated!(offset_ptr); @@ -133,7 +133,7 @@ pub fn calldataload(context: InstructionConte /// /// Pushes the size of input data onto the stack. pub fn calldatasize(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!( context.interpreter, U256::from(context.interpreter.input.input().len()) @@ -144,7 +144,7 @@ pub fn calldatasize(context: InstructionConte /// /// Pushes the value sent with the current call onto the stack. pub fn callvalue(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!(context.interpreter, context.interpreter.input.call_value()); } @@ -180,7 +180,7 @@ pub fn calldatacopy(context: InstructionConte /// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY pub fn returndatasize(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, BYZANTIUM); - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!( context.interpreter, U256::from(context.interpreter.return_data.buffer().len()) @@ -219,7 +219,7 @@ pub fn returndatacopy(context: InstructionCon /// /// Pushes the amount of remaining gas onto the stack. pub fn gas(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!( context.interpreter, U256::from(context.interpreter.gas.remaining()) diff --git a/crates/interpreter/src/instructions/tx_info.rs b/crates/interpreter/src/instructions/tx_info.rs index 745c99a6be..9f0e9f38cb 100644 --- a/crates/interpreter/src/instructions/tx_info.rs +++ b/crates/interpreter/src/instructions/tx_info.rs @@ -1,5 +1,4 @@ use crate::{ - gas, interpreter_types::{InterpreterTypes, RuntimeFlag, StackTr}, Host, }; @@ -13,7 +12,7 @@ use crate::InstructionContext; pub fn gasprice( context: InstructionContext<'_, H, WIRE>, ) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!( context.interpreter, U256::from(context.host.effective_gas_price()) @@ -24,7 +23,7 @@ pub fn gasprice( /// /// Gets the execution origination address. pub fn origin(context: InstructionContext<'_, H, WIRE>) { - gas!(context.interpreter, gas::BASE); + //gas!(context.interpreter, gas::BASE); push!( context.interpreter, context.host.caller().into_word().into() @@ -38,7 +37,7 @@ pub fn blob_hash( context: InstructionContext<'_, H, WIRE>, ) { check!(context.interpreter, CANCUN); - gas!(context.interpreter, gas::VERYLOW); + //gas!(context.interpreter, gas::VERYLOW); popn_top!([], index, context.interpreter); let i = as_usize_saturated!(index); *index = context.host.blob_hash(i).unwrap_or_default(); diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index fffb852393..ba858d8a40 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -195,7 +195,8 @@ impl Interpreter { pub fn take_next_action(&mut self) -> InterpreterAction { self.bytecode.reset_action(); // Return next action if it is some. - core::mem::take(self.bytecode.action()).expect("Interpreter to set action") + let action = core::mem::take(self.bytecode.action()).expect("Interpreter to set action"); + action } /// Halt the interpreter with the given result. @@ -208,6 +209,14 @@ impl Interpreter { .set_action(InterpreterAction::new_halt(result, self.gas)); } + /// Halt the interpreter with an out-of-gas error. + #[cold] + #[inline(never)] + pub fn halt_oog(&mut self) { + self.gas.spend_all(); + self.halt(InstructionResult::OutOfGas); + } + /// Return with the given output. /// /// This will set the action to [`InterpreterAction::Return`] and set the gas to the current gas. @@ -223,12 +232,29 @@ impl Interpreter { /// /// Internally it will increment instruction pointer by one. #[inline] - pub fn step(&mut self, instruction_table: &InstructionTable, host: &mut H) { + pub fn step( + &mut self, + instruction_table: &InstructionTable, + host: &mut H, + ) { + // Get current opcode. + let opcode = self.bytecode.opcode(); + + // SAFETY: In analysis we are doing padding of bytecode so that we are sure that last + // byte instruction is STOP so we are safe to just increment program_counter bcs on last instruction + // it will do noop and just stop execution of this contract + self.bytecode.relative_jump(1); + + let instruction = unsafe { instruction_table.get_unchecked(opcode as usize) }; + + if self.gas.record_cost_unsafe(instruction.static_gas()) { + return self.halt_oog(); + } let context = InstructionContext { interpreter: self, host, }; - context.step(instruction_table); + instruction.execute(context); } /// Executes the instruction at the current instruction pointer. @@ -243,7 +269,7 @@ impl Interpreter { /// Executes the interpreter until it returns or stops. #[inline] - pub fn run_plain( + pub fn run_plain( &mut self, instruction_table: &InstructionTable, host: &mut H, @@ -255,6 +281,24 @@ impl Interpreter { } } +/* used for cargo asm +pub fn asm_step( + interpreter: &mut Interpreter, + instruction_table: &InstructionTable, + host: &mut DummyHost, +) { + interpreter.step(instruction_table, host); +} + +pub fn asm_run( + interpreter: &mut Interpreter, + instruction_table: &InstructionTable, + host: &mut DummyHost, +) { + interpreter.run_plain(instruction_table, host); +} +*/ + /// The result of an interpreter operation. #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] diff --git a/crates/interpreter/src/interpreter/ext_bytecode.rs b/crates/interpreter/src/interpreter/ext_bytecode.rs index 53852803cc..bbfc7030bf 100644 --- a/crates/interpreter/src/interpreter/ext_bytecode.rs +++ b/crates/interpreter/src/interpreter/ext_bytecode.rs @@ -10,6 +10,10 @@ mod serde; /// Extended bytecode structure that wraps base bytecode with additional execution metadata. #[derive(Debug)] pub struct ExtBytecode { + /// The current instruction pointer. + instruction_pointer: *const u8, + /// Whether the execution should continue. + continue_execution: bool, /// Bytecode Keccak-256 hash. /// This is `None` if it hasn't been calculated yet. /// Since it's not necessary for execution, it's not calculated by default. @@ -17,11 +21,8 @@ pub struct ExtBytecode { /// Actions that the EVM should do. It contains return value of the Interpreter or inputs for `CALL` or `CREATE` instructions. /// For `RETURN` or `REVERT` instructions it contains the result of the instruction. pub action: Option, - has_set_action: bool, /// The base bytecode. base: Bytecode, - /// The current instruction pointer. - instruction_pointer: *const u8, } impl Deref for ExtBytecode { @@ -63,7 +64,7 @@ impl ExtBytecode { instruction_pointer, bytecode_hash: hash, action: None, - has_set_action: false, + continue_execution: true, } } @@ -103,28 +104,28 @@ impl ExtBytecode { impl LoopControl for ExtBytecode { #[inline] - fn is_end(&self) -> bool { - self.has_set_action + fn is_not_end(&self) -> bool { + self.continue_execution } #[inline] fn reset_action(&mut self) { - self.has_set_action = false; + self.continue_execution = true; } #[inline] fn set_action(&mut self, action: InterpreterAction) { debug_assert_eq!( - self.has_set_action, + !self.continue_execution, self.action.is_some(), "has_set_action out of sync" ); debug_assert!( - !self.has_set_action, + self.continue_execution, "action already set;\nold: {:#?}\nnew: {:#?}", self.action, action, ); - self.has_set_action = true; + self.continue_execution = false; self.action = Some(action); } diff --git a/crates/interpreter/src/interpreter_action.rs b/crates/interpreter/src/interpreter_action.rs index 08743e8885..83cf29741f 100644 --- a/crates/interpreter/src/interpreter_action.rs +++ b/crates/interpreter/src/interpreter_action.rs @@ -54,23 +54,36 @@ pub enum InterpreterAction { impl InterpreterAction { /// Returns `true` if action is call. + #[inline] pub fn is_call(&self) -> bool { matches!(self, InterpreterAction::NewFrame(FrameInput::Call(..))) } /// Returns `true` if action is create. + #[inline] pub fn is_create(&self) -> bool { matches!(self, InterpreterAction::NewFrame(FrameInput::Create(..))) } /// Returns `true` if action is return. + #[inline] pub fn is_return(&self) -> bool { matches!(self, InterpreterAction::Return { .. }) } + /// Returns [`Gas`] if action is return. + #[inline] + pub fn gas_mut(&mut self) -> Option<&mut Gas> { + match self { + InterpreterAction::Return(result) => Some(&mut result.gas), + _ => None, + } + } + /// Returns [`InterpreterResult`] if action is return. /// /// Else it returns [None]. + #[inline] pub fn into_result_return(self) -> Option { match self { InterpreterAction::Return(result) => Some(result), @@ -81,6 +94,7 @@ impl InterpreterAction { /// Returns [`InstructionResult`] if action is return. /// /// Else it returns [None]. + #[inline] pub fn instruction_result(&self) -> Option { match self { InterpreterAction::Return(result) => Some(result.result), @@ -89,21 +103,25 @@ impl InterpreterAction { } /// Create new frame action with the given frame input. + #[inline] pub fn new_frame(frame_input: FrameInput) -> Self { Self::NewFrame(frame_input) } /// Create new halt action with the given result and gas. + #[inline] pub fn new_halt(result: InstructionResult, gas: Gas) -> Self { Self::Return(InterpreterResult::new(result, Bytes::new(), gas)) } /// Create new return action with the given result, output and gas. + #[inline] pub fn new_return(result: InstructionResult, output: Bytes, gas: Gas) -> Self { Self::Return(InterpreterResult::new(result, output, gas)) } /// Create new stop action. + #[inline] pub fn new_stop() -> Self { Self::Return(InterpreterResult::new( InstructionResult::Stop, diff --git a/crates/interpreter/src/interpreter_types.rs b/crates/interpreter/src/interpreter_types.rs index c369e20dbc..4c152336a3 100644 --- a/crates/interpreter/src/interpreter_types.rs +++ b/crates/interpreter/src/interpreter_types.rs @@ -248,12 +248,12 @@ pub trait ReturnData { /// Trait controls execution of the loop. pub trait LoopControl { /// Returns `true` if the loop should continue. + fn is_not_end(&self) -> bool; + /// Is end of the loop. #[inline] - fn is_not_end(&self) -> bool { - !self.is_end() + fn is_end(&self) -> bool { + !self.is_not_end() } - /// Is end of the loop. - fn is_end(&self) -> bool; /// Sets the `end` flag internally. Action should be taken after. fn reset_action(&mut self); /// Set return action. diff --git a/examples/custom_opcodes/src/main.rs b/examples/custom_opcodes/src/main.rs index c08305de5f..c81d99f238 100644 --- a/examples/custom_opcodes/src/main.rs +++ b/examples/custom_opcodes/src/main.rs @@ -10,7 +10,7 @@ use revm::{ interpreter::{ interpreter::EthInterpreter, interpreter_types::{Immediates, Jumps}, - InstructionContext, + Instruction, InstructionContext, }, primitives::TxKind, state::Bytecode, @@ -41,10 +41,13 @@ pub fn main() { // insert our custom opcode instructions.insert_instruction( MY_STATIC_JUMP, - |ctx: InstructionContext<'_, _, EthInterpreter>| { - let offset = ctx.interpreter.bytecode.read_i16(); - ctx.interpreter.bytecode.relative_jump(offset as isize); - }, + Instruction::new( + |ctx: InstructionContext<'_, _, EthInterpreter>| { + let offset = ctx.interpreter.bytecode.read_i16(); + ctx.interpreter.bytecode.relative_jump(offset as isize); + }, + 0, + ), ); // Create a new EVM instance.