diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index eb034bf4c7..2b476b909b 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -17,6 +17,10 @@ pub enum InstructionResult { // error codes OutOfGas = 0x50, + MemoryOOG = 0x51, + MemoryLimitOOG = 0x52, + PrecompileOOG = 0x53, + InvalidOperandOOG = 0x54, OpcodeNotFound, CallNotAllowedInsideStatic, StateChangeDuringStaticCall, @@ -58,7 +62,21 @@ impl From for SuccessOrHalt { InstructionResult::Revert => Self::Revert, InstructionResult::CallTooDeep => Self::Internal, // not gonna happen for first call InstructionResult::OutOfFund => Self::Internal, // Check for first call is done separately. - InstructionResult::OutOfGas => Self::Halt(Halt::OutOfGas), + InstructionResult::OutOfGas => Self::Halt(Halt::OutOfGas( + revm_primitives::OutOfGasError::BasicOutOfGas, + )), + InstructionResult::MemoryLimitOOG => { + Self::Halt(Halt::OutOfGas(revm_primitives::OutOfGasError::MemoryLimit)) + } + InstructionResult::MemoryOOG => { + Self::Halt(Halt::OutOfGas(revm_primitives::OutOfGasError::Memory)) + } + InstructionResult::PrecompileOOG => { + Self::Halt(Halt::OutOfGas(revm_primitives::OutOfGasError::Precompile)) + } + InstructionResult::InvalidOperandOOG => Self::Halt(Halt::OutOfGas( + revm_primitives::OutOfGasError::InvalidOperand, + )), InstructionResult::OpcodeNotFound => Self::Halt(Halt::OpcodeNotFound), InstructionResult::CallNotAllowedInsideStatic => Self::Internal, // first call is not static call InstructionResult::StateChangeDuringStaticCall => Self::Internal, diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs index 7de642f7bf..8d3213e661 100644 --- a/crates/interpreter/src/instructions/control.rs +++ b/crates/interpreter/src/instructions/control.rs @@ -51,11 +51,11 @@ pub fn pc(interpreter: &mut Interpreter, _host: &mut dyn Host) { pub fn ret(interpreter: &mut Interpreter, _host: &mut dyn Host) { // zero gas cost gas!(interp,gas::ZERO); pop!(interpreter, start, len); - let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::InvalidOperandOOG); if len == 0 { interpreter.return_range = usize::MAX..usize::MAX; } else { - let offset = as_usize_or_fail!(interpreter, start, InstructionResult::OutOfGas); + let offset = as_usize_or_fail!(interpreter, start, InstructionResult::InvalidOperandOOG); memory_resize!(interpreter, offset, len); interpreter.return_range = offset..(offset + len); } @@ -67,11 +67,11 @@ pub fn revert(interpreter: &mut Interpreter, _host: &mut dyn Host) { // EIP-140: REVERT instruction check!(interpreter, SPEC::enabled(BYZANTIUM)); pop!(interpreter, start, len); - let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::InvalidOperandOOG); if len == 0 { interpreter.return_range = usize::MAX..usize::MAX; } else { - let offset = as_usize_or_fail!(interpreter, start, InstructionResult::OutOfGas); + let offset = as_usize_or_fail!(interpreter, start, InstructionResult::InvalidOperandOOG); memory_resize!(interpreter, offset, len); interpreter.return_range = offset..(offset + len); } diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 04e046e352..7ed0e081f2 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -92,7 +92,7 @@ pub fn extcodecopy(interpreter: &mut Interpreter, host: &mut dyn Hos } let (code, is_cold) = ret.unwrap(); - let len = as_usize_or_fail!(interpreter, len_u256, InstructionResult::OutOfGas); + let len = as_usize_or_fail!(interpreter, len_u256, InstructionResult::InvalidOperandOOG); gas_or_fail!( interpreter, gas::extcodecopy_cost::(len as u64, is_cold) @@ -100,7 +100,11 @@ pub fn extcodecopy(interpreter: &mut Interpreter, host: &mut dyn Hos if len == 0 { return; } - let memory_offset = as_usize_or_fail!(interpreter, memory_offset, InstructionResult::OutOfGas); + let memory_offset = as_usize_or_fail!( + interpreter, + memory_offset, + InstructionResult::InvalidOperandOOG + ); let code_offset = min(as_usize_saturated!(code_offset), code.len()); memory_resize!(interpreter, memory_offset, len); @@ -167,12 +171,12 @@ pub fn log(interpreter: &mut Interpreter, host: &mut dy check_staticcall!(interpreter); pop!(interpreter, offset, len); - let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::InvalidOperandOOG); gas_or_fail!(interpreter, gas::log_cost(N, len as u64)); let data = if len == 0 { Bytes::new() } else { - let offset = as_usize_or_fail!(interpreter, offset, InstructionResult::OutOfGas); + let offset = as_usize_or_fail!(interpreter, offset, InstructionResult::InvalidOperandOOG); memory_resize!(interpreter, offset, len); Bytes::copy_from_slice(interpreter.memory.get_slice(offset, len)) }; @@ -226,12 +230,16 @@ pub fn create( interpreter.return_data_buffer = Bytes::new(); pop!(interpreter, value, code_offset, len); - let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::InvalidOperandOOG); let code = if len == 0 { Bytes::new() } else { - let code_offset = as_usize_or_fail!(interpreter, code_offset, InstructionResult::OutOfGas); + let code_offset = as_usize_or_fail!( + interpreter, + code_offset, + InstructionResult::InvalidOperandOOG + ); memory_resize!(interpreter, code_offset, len); Bytes::copy_from_slice(interpreter.memory.get_slice(code_offset, len)) }; @@ -347,18 +355,23 @@ pub fn call_inner( pop!(interpreter, in_offset, in_len, out_offset, out_len); - let in_len = as_usize_or_fail!(interpreter, in_len, InstructionResult::OutOfGas); + let in_len = as_usize_or_fail!(interpreter, in_len, InstructionResult::InvalidOperandOOG); let input = if in_len != 0 { - let in_offset = as_usize_or_fail!(interpreter, in_offset, InstructionResult::OutOfGas); + let in_offset = + as_usize_or_fail!(interpreter, in_offset, InstructionResult::InvalidOperandOOG); memory_resize!(interpreter, in_offset, in_len); Bytes::copy_from_slice(interpreter.memory.get_slice(in_offset, in_len)) } else { Bytes::new() }; - let out_len = as_usize_or_fail!(interpreter, out_len, InstructionResult::OutOfGas); + let out_len = as_usize_or_fail!(interpreter, out_len, InstructionResult::InvalidOperandOOG); let out_offset = if out_len != 0 { - let out_offset = as_usize_or_fail!(interpreter, out_offset, InstructionResult::OutOfGas); + let out_offset = as_usize_or_fail!( + interpreter, + out_offset, + InstructionResult::InvalidOperandOOG + ); memory_resize!(interpreter, out_offset, out_len); out_offset } else { diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index 762667426d..71b6d17f22 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -60,7 +60,7 @@ macro_rules! memory_resize { { #[cfg(feature = "memory_limit")] if new_size > ($interp.memory_limit as usize) { - $interp.instruction_result = InstructionResult::OutOfGas; + $interp.instruction_result = InstructionResult::MemoryLimitOOG; return; } @@ -68,14 +68,14 @@ macro_rules! memory_resize { if crate::USE_GAS { let num_bytes = new_size / 32; if !$interp.gas.record_memory(crate::gas::memory_gas(num_bytes)) { - $interp.instruction_result = InstructionResult::OutOfGas; + $interp.instruction_result = InstructionResult::MemoryLimitOOG; return; } } $interp.memory.resize(new_size); } } else { - $interp.instruction_result = InstructionResult::OutOfGas; + $interp.instruction_result = InstructionResult::MemoryOOG; return; } }}; @@ -222,7 +222,7 @@ macro_rules! as_usize_saturated { macro_rules! as_usize_or_fail { ( $interp:expr, $v:expr ) => {{ - as_usize_or_fail!($interp, $v, InstructionResult::OutOfGas) + as_usize_or_fail!($interp, $v, InstructionResult::InvalidOperandOOG) }}; ( $interp:expr, $v:expr, $reason:expr ) => {{ diff --git a/crates/interpreter/src/instructions/memory.rs b/crates/interpreter/src/instructions/memory.rs index 08d720e32b..f40f4561bb 100644 --- a/crates/interpreter/src/instructions/memory.rs +++ b/crates/interpreter/src/instructions/memory.rs @@ -3,7 +3,7 @@ use crate::{interpreter::Interpreter, primitives::U256, Host, InstructionResult} pub fn mload(interpreter: &mut Interpreter, _host: &mut dyn Host) { // gas!(interp, gas::VERYLOW); pop!(interpreter, index); - let index = as_usize_or_fail!(interpreter, index, InstructionResult::OutOfGas); + let index = as_usize_or_fail!(interpreter, index, InstructionResult::InvalidOperandOOG); memory_resize!(interpreter, index, 32); push!( interpreter, @@ -16,7 +16,7 @@ pub fn mload(interpreter: &mut Interpreter, _host: &mut dyn Host) { pub fn mstore(interpreter: &mut Interpreter, _host: &mut dyn Host) { // gas!(interp, gas::VERYLOW); pop!(interpreter, index, value); - let index = as_usize_or_fail!(interpreter, index, InstructionResult::OutOfGas); + let index = as_usize_or_fail!(interpreter, index, InstructionResult::InvalidOperandOOG); memory_resize!(interpreter, index, 32); interpreter.memory.set_u256(index, value); } @@ -24,7 +24,7 @@ pub fn mstore(interpreter: &mut Interpreter, _host: &mut dyn Host) { pub fn mstore8(interpreter: &mut Interpreter, _host: &mut dyn Host) { // gas!(interp, gas::VERYLOW); pop!(interpreter, index, value); - let index = as_usize_or_fail!(interpreter, index, InstructionResult::OutOfGas); + let index = as_usize_or_fail!(interpreter, index, InstructionResult::InvalidOperandOOG); memory_resize!(interpreter, index, 1); let value = value.as_le_bytes()[0]; // Safety: we resized our memory two lines above. diff --git a/crates/interpreter/src/instructions/system.rs b/crates/interpreter/src/instructions/system.rs index adc8d5f215..4659636236 100644 --- a/crates/interpreter/src/instructions/system.rs +++ b/crates/interpreter/src/instructions/system.rs @@ -8,12 +8,12 @@ use core::cmp::min; pub fn sha3(interpreter: &mut Interpreter, _host: &mut dyn Host) { pop!(interpreter, from, len); - let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::InvalidOperandOOG); gas_or_fail!(interpreter, gas::sha3_cost(len as u64)); let hash = if len == 0 { KECCAK_EMPTY } else { - let from = as_usize_or_fail!(interpreter, from, InstructionResult::OutOfGas); + let from = as_usize_or_fail!(interpreter, from, InstructionResult::InvalidOperandOOG); memory_resize!(interpreter, from, len); keccak256(interpreter.memory.get_slice(from, len)) }; @@ -38,12 +38,16 @@ pub fn codesize(interpreter: &mut Interpreter, _host: &mut dyn Host) { pub fn codecopy(interpreter: &mut Interpreter, _host: &mut dyn Host) { pop!(interpreter, memory_offset, code_offset, len); - let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::InvalidOperandOOG); gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64)); if len == 0 { return; } - let memory_offset = as_usize_or_fail!(interpreter, memory_offset, InstructionResult::OutOfGas); + let memory_offset = as_usize_or_fail!( + interpreter, + memory_offset, + InstructionResult::InvalidOperandOOG + ); let code_offset = as_usize_saturated!(code_offset); memory_resize!(interpreter, memory_offset, len); @@ -85,12 +89,16 @@ pub fn callvalue(interpreter: &mut Interpreter, _host: &mut dyn Host) { pub fn calldatacopy(interpreter: &mut Interpreter, _host: &mut dyn Host) { pop!(interpreter, memory_offset, data_offset, len); - let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::InvalidOperandOOG); gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64)); if len == 0 { return; } - let memory_offset = as_usize_or_fail!(interpreter, memory_offset, InstructionResult::OutOfGas); + let memory_offset = as_usize_or_fail!( + interpreter, + memory_offset, + InstructionResult::InvalidOperandOOG + ); let data_offset = as_usize_saturated!(data_offset); memory_resize!(interpreter, memory_offset, len); @@ -114,7 +122,7 @@ pub fn returndatacopy(interpreter: &mut Interpreter, _host: &mut dyn // EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY check!(interpreter, SPEC::enabled(BYZANTIUM)); pop!(interpreter, memory_offset, offset, len); - let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::InvalidOperandOOG); gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64)); let data_offset = as_usize_saturated!(offset); let (data_end, overflow) = data_offset.overflowing_add(len); @@ -123,8 +131,11 @@ pub fn returndatacopy(interpreter: &mut Interpreter, _host: &mut dyn return; } if len != 0 { - let memory_offset = - as_usize_or_fail!(interpreter, memory_offset, InstructionResult::OutOfGas); + let memory_offset = as_usize_or_fail!( + interpreter, + memory_offset, + InstructionResult::InvalidOperandOOG + ); memory_resize!(interpreter, memory_offset, len); interpreter.memory.set( memory_offset, diff --git a/crates/primitives/src/result.rs b/crates/primitives/src/result.rs index 95f69f9d1e..8364a9ac47 100644 --- a/crates/primitives/src/result.rs +++ b/crates/primitives/src/result.rs @@ -115,7 +115,7 @@ pub enum Eval { #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Halt { - OutOfGas, + OutOfGas(OutOfGasError), OpcodeNotFound, InvalidFEOpcode, InvalidJump, @@ -132,3 +132,19 @@ pub enum Halt { /// Error on created contract that begins with EF CreateContractStartingWithEF, } + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum OutOfGasError { + // Basic OOG error + BasicOutOfGas, + // Tried to expand past REVM limit + MemoryLimit, + // Basic OOG error from memory expansion + Memory, + // Precompile threw OOG error + Precompile, + // When performing something that takes a U256 and casts down to a u64, if its too large this would fire + // i.e. in `as_usize_or_fail` + InvalidOperand, +} diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs index a65349800a..b4b38a5ce8 100644 --- a/crates/revm/src/evm_impl.rs +++ b/crates/revm/src/evm_impl.rs @@ -576,9 +576,9 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, } } } - // if we have enought gas + // if we have enough gas self.data.journaled_state.checkpoint_commit(); - // Do analysis of bytecode streight away. + // Do analysis of bytecode straight away. let bytecode = match self.data.env.cfg.perf_analyse_created_bytecodes { AnalysisKind::Raw => Bytecode::new_raw(bytes.clone()), AnalysisKind::Check => Bytecode::new_raw(bytes.clone()).to_checked(), @@ -697,12 +697,12 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, (InstructionResult::Return, gas, Bytes::from(data)) } else { self.data.journaled_state.checkpoint_revert(checkpoint); - (InstructionResult::OutOfGas, gas, Bytes::new()) + (InstructionResult::PrecompileOOG, gas, Bytes::new()) } } Err(e) => { let ret = if let precompile::Error::OutOfGas = e { - InstructionResult::OutOfGas + InstructionResult::PrecompileOOG } else { InstructionResult::PrecompileError };