From 7893dae550642fc4f6608817fa8d64741a744c76 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 15 Feb 2022 17:31:12 +0300 Subject: [PATCH 01/23] Optimization: switch vm stack to little endian representation (#7) --- core/src/eval/macros.rs | 18 ++++--- core/src/eval/misc.rs | 19 +++---- core/src/eval/mod.rs | 2 +- core/src/memory.rs | 19 ++++++- core/src/stack.rs | 35 ++++++++++--- gasometer/src/lib.rs | 101 ++++++++++++++++++------------------- runtime/src/eval/macros.rs | 18 ++++--- runtime/src/eval/mod.rs | 23 +++++---- runtime/src/eval/system.rs | 52 ++++++++++--------- 9 files changed, 165 insertions(+), 122 deletions(-) diff --git a/core/src/eval/macros.rs b/core/src/eval/macros.rs index 3fd7a9f6e..9cd9c30ee 100644 --- a/core/src/eval/macros.rs +++ b/core/src/eval/macros.rs @@ -7,11 +7,15 @@ macro_rules! try_or_fail { }; } -macro_rules! pop { +macro_rules! pop_h256 { ( $machine:expr, $( $x:ident ),* ) => ( $( let $x = match $machine.stack.pop() { - Ok(value) => value, + Ok(value) => { + let mut res = H256([0; 32]); + value.to_big_endian(&mut res[..]); + res + }, Err(e) => return Control::Exit(e.into()), }; )* @@ -22,17 +26,17 @@ macro_rules! pop_u256 { ( $machine:expr, $( $x:ident ),* ) => ( $( let $x = match $machine.stack.pop() { - Ok(value) => U256::from_big_endian(&value[..]), + Ok(value) => value, Err(e) => return Control::Exit(e.into()), }; )* ); } -macro_rules! push { +macro_rules! push_h256 { ( $machine:expr, $( $x:expr ),* ) => ( $( - match $machine.stack.push($x) { + match $machine.stack.push(U256::from_big_endian(&$x[..])) { Ok(()) => (), Err(e) => return Control::Exit(e.into()), } @@ -43,9 +47,7 @@ macro_rules! push { macro_rules! push_u256 { ( $machine:expr, $( $x:expr ),* ) => ( $( - let mut value = H256::default(); - $x.to_big_endian(&mut value[..]); - match $machine.stack.push(value) { + match $machine.stack.push($x) { Ok(()) => (), Err(e) => return Control::Exit(e.into()), } diff --git a/core/src/eval/misc.rs b/core/src/eval/misc.rs index 8271d5f6c..a4c5f03a5 100644 --- a/core/src/eval/misc.rs +++ b/core/src/eval/misc.rs @@ -41,7 +41,7 @@ pub fn calldataload(state: &mut Machine) -> Control { } } - push!(state, H256::from(load)); + push_h256!(state, H256::from(load)); Control::Continue(1) } @@ -72,7 +72,7 @@ pub fn calldatacopy(state: &mut Machine) -> Control { #[inline] pub fn pop(state: &mut Machine) -> Control { - pop!(state, _val); + pop_u256!(state, _val); Control::Continue(1) } @@ -81,15 +81,15 @@ pub fn mload(state: &mut Machine) -> Control { pop_u256!(state, index); try_or_fail!(state.memory.resize_offset(index, U256::from(32))); let index = as_usize_or_fail!(index); - let value = H256::from_slice(&state.memory.get(index, 32)[..]); - push!(state, value); + let value = state.memory.get_h256(index); + push_h256!(state, value); Control::Continue(1) } #[inline] pub fn mstore(state: &mut Machine) -> Control { pop_u256!(state, index); - pop!(state, value); + pop_h256!(state, value); try_or_fail!(state.memory.resize_offset(index, U256::from(32))); let index = as_usize_or_fail!(index); match state.memory.set(index, &value[..], Some(32)) { @@ -125,9 +125,9 @@ pub fn jump(state: &mut Machine) -> Control { #[inline] pub fn jumpi(state: &mut Machine) -> Control { pop_u256!(state, dest); - pop!(state, value); + pop_u256!(state, value); - if value != H256::zero() { + if value != U256::zero() { let dest = as_usize_or_fail!(dest, ExitError::InvalidJump); if state.valids.is_valid(dest) { Control::Jump(dest) @@ -157,8 +157,9 @@ pub fn push(state: &mut Machine, n: usize, position: usize) -> Control { let slice = &state.code[(position + 1)..end]; let mut val = [0u8; 32]; val[(32 - n)..(32 - n + slice.len())].copy_from_slice(slice); + let val = U256::from_big_endian(&val); - push!(state, H256(val)); + push_u256!(state, val); Control::Continue(1 + n) } @@ -168,7 +169,7 @@ pub fn dup(state: &mut Machine, n: usize) -> Control { Ok(value) => value, Err(e) => return Control::Exit(e.into()), }; - push!(state, value); + push_u256!(state, value); Control::Continue(1) } diff --git a/core/src/eval/mod.rs b/core/src/eval/mod.rs index 519f5d925..c71721b42 100644 --- a/core/src/eval/mod.rs +++ b/core/src/eval/mod.rs @@ -6,7 +6,7 @@ mod misc; use crate::{ExitError, ExitReason, ExitSucceed, Machine, Opcode}; use core::ops::{BitAnd, BitOr, BitXor}; -use primitive_types::{H256, U256}; +use primitive_types::U256; #[derive(Clone, Eq, PartialEq, Debug)] pub enum Control { diff --git a/core/src/memory.rs b/core/src/memory.rs index 7bb09629b..2f1f96393 100644 --- a/core/src/memory.rs +++ b/core/src/memory.rs @@ -2,7 +2,7 @@ use crate::{ExitError, ExitFatal}; use alloc::vec::Vec; use core::cmp::min; use core::ops::{BitAnd, Not}; -use primitive_types::U256; +use primitive_types::{H256, U256}; /// A sequencial memory. It uses Rust's `Vec` for internal /// representation. @@ -96,6 +96,23 @@ impl Memory { ret } + /// Get `H256` from a specific offset in memory. + pub fn get_h256(&self, offset: usize) -> H256 { + let mut ret = [0; 32]; + + #[allow(clippy::needless_range_loop)] + for index in 0..32 { + let position = offset + index; + if position >= self.data.len() { + break; + } + + ret[index] = self.data[position]; + } + + H256(ret) + } + /// Set memory region at given offset. The offset and value is considered /// untrusted. pub fn set( diff --git a/core/src/stack.rs b/core/src/stack.rs index 893af517c..830987e11 100644 --- a/core/src/stack.rs +++ b/core/src/stack.rs @@ -1,11 +1,11 @@ use crate::ExitError; use alloc::vec::Vec; -use primitive_types::H256; +use primitive_types::{H256, U256}; /// EVM stack. #[derive(Clone, Debug)] pub struct Stack { - data: Vec, + data: Vec, limit: usize, } @@ -38,21 +38,30 @@ impl Stack { #[inline] /// Stack data. - pub fn data(&self) -> &Vec { + pub fn data(&self) -> &Vec { &self.data } #[inline] /// Pop a value from the stack. If the stack is already empty, returns the /// `StackUnderflow` error. - pub fn pop(&mut self) -> Result { + pub fn pop(&mut self) -> Result { self.data.pop().ok_or(ExitError::StackUnderflow) } + #[inline] + pub fn pop_h256(&mut self) -> Result { + self.pop().map(|it| { + let mut res = H256([0; 32]); + it.to_big_endian(&mut res.0); + res + }) + } + #[inline] /// Push a new value into the stack. If it will exceed the stack limit, /// returns `StackOverflow` error and leaves the stack unchanged. - pub fn push(&mut self, value: H256) -> Result<(), ExitError> { + pub fn push(&mut self, value: U256) -> Result<(), ExitError> { if self.data.len() + 1 > self.limit { return Err(ExitError::StackOverflow); } @@ -64,7 +73,7 @@ impl Stack { /// Peek a value at given index for the stack, where the top of /// the stack is at index `0`. If the index is too large, /// `StackError::Underflow` is returned. - pub fn peek(&self, no_from_top: usize) -> Result { + pub fn peek(&self, no_from_top: usize) -> Result { if self.data.len() > no_from_top { Ok(self.data[self.data.len() - no_from_top - 1]) } else { @@ -72,11 +81,23 @@ impl Stack { } } + #[inline] + /// Peek a value at given index for the stack, where the top of + /// the stack is at index `0`. If the index is too large, + /// `StackError::Underflow` is returned. + pub fn peek_h256(&self, no_from_top: usize) -> Result { + self.peek(no_from_top).map(|it| { + let mut res = H256([0; 32]); + it.to_big_endian(&mut res.0); + res + }) + } + #[inline] /// Set a value at given index for the stack, where the top of the /// stack is at index `0`. If the index is too large, /// `StackError::Underflow` is returned. - pub fn set(&mut self, no_from_top: usize, val: H256) -> Result<(), ExitError> { + pub fn set(&mut self, no_from_top: usize, val: U256) -> Result<(), ExitError> { if self.data.len() > no_from_top { let len = self.data.len(); self.data[len - no_from_top - 1] = val; diff --git a/gasometer/src/lib.rs b/gasometer/src/lib.rs index ce60a3907..da0fcf4ab 100644 --- a/gasometer/src/lib.rs +++ b/gasometer/src/lib.rs @@ -1,4 +1,4 @@ -//! EVM gasometer. +//! VM gasometer. #![deny(warnings)] #![forbid(unsafe_code, unused_variables)] @@ -479,14 +479,14 @@ pub fn dynamic_opcode_cost( Opcode::BASEFEE => GasCost::Invalid(opcode), Opcode::EXTCODESIZE => { - let target = stack.peek(0)?.into(); + let target = stack.peek_h256(0)?.into(); storage_target = StorageTarget::Address(target); GasCost::ExtCodeSize { target_is_cold: handler.is_cold(target, None)?, } } Opcode::BALANCE => { - let target = stack.peek(0)?.into(); + let target = stack.peek_h256(0)?.into(); storage_target = StorageTarget::Address(target); GasCost::Balance { target_is_cold: handler.is_cold(target, None)?, @@ -495,7 +495,7 @@ pub fn dynamic_opcode_cost( Opcode::BLOCKHASH => GasCost::BlockHash, Opcode::EXTCODEHASH if config.has_ext_code_hash => { - let target = stack.peek(0)?.into(); + let target = stack.peek_h256(0)?.into(); storage_target = StorageTarget::Address(target); GasCost::ExtCodeHash { target_is_cold: handler.is_cold(target, None)?, @@ -504,43 +504,43 @@ pub fn dynamic_opcode_cost( Opcode::EXTCODEHASH => GasCost::Invalid(opcode), Opcode::CALLCODE => { - let target = stack.peek(1)?.into(); + let target = stack.peek_h256(1)?.into(); storage_target = StorageTarget::Address(target); GasCost::CallCode { - value: U256::from_big_endian(&stack.peek(2)?[..]), - gas: U256::from_big_endian(&stack.peek(0)?[..]), + value: stack.peek(2)?, + gas: stack.peek(0)?, target_is_cold: handler.is_cold(target, None)?, target_exists: handler.exists(target), } } Opcode::STATICCALL => { - let target = stack.peek(1)?.into(); + let target = stack.peek_h256(1)?.into(); storage_target = StorageTarget::Address(target); GasCost::StaticCall { - gas: U256::from_big_endian(&stack.peek(0)?[..]), + gas: stack.peek(0)?, target_is_cold: handler.is_cold(target, None)?, target_exists: handler.exists(target), } } Opcode::SHA3 => GasCost::Sha3 { - len: U256::from_big_endian(&stack.peek(1)?[..]), + len: stack.peek(1)?, }, Opcode::EXTCODECOPY => { - let target = stack.peek(0)?.into(); + let target = stack.peek_h256(0)?.into(); storage_target = StorageTarget::Address(target); GasCost::ExtCodeCopy { target_is_cold: handler.is_cold(target, None)?, - len: U256::from_big_endian(&stack.peek(3)?[..]), + len: stack.peek(3)?, } } Opcode::CALLDATACOPY | Opcode::CODECOPY => GasCost::VeryLowCopy { - len: U256::from_big_endian(&stack.peek(2)?[..]), + len: stack.peek(2)?, }, Opcode::EXP => GasCost::Exp { - power: U256::from_big_endian(&stack.peek(1)?[..]), + power: stack.peek(1)?, }, Opcode::SLOAD => { - let index = stack.peek(0)?; + let index = stack.peek_h256(0)?; storage_target = StorageTarget::Slot(address, index); GasCost::SLoad { target_is_cold: handler.is_cold(address, Some(index))?, @@ -548,10 +548,10 @@ pub fn dynamic_opcode_cost( } Opcode::DELEGATECALL if config.has_delegate_call => { - let target = stack.peek(1)?.into(); + let target = stack.peek_h256(1)?.into(); storage_target = StorageTarget::Address(target); GasCost::DelegateCall { - gas: U256::from_big_endian(&stack.peek(0)?[..]), + gas: stack.peek(0)?, target_is_cold: handler.is_cold(target, None)?, target_exists: handler.exists(target), } @@ -560,13 +560,13 @@ pub fn dynamic_opcode_cost( Opcode::RETURNDATASIZE if config.has_return_data => GasCost::Base, Opcode::RETURNDATACOPY if config.has_return_data => GasCost::VeryLowCopy { - len: U256::from_big_endian(&stack.peek(2)?[..]), + len: stack.peek(2)?, }, Opcode::RETURNDATASIZE | Opcode::RETURNDATACOPY => GasCost::Invalid(opcode), Opcode::SSTORE if !is_static => { - let index = stack.peek(0)?; - let value = stack.peek(1)?; + let index = stack.peek_h256(0)?; + let value = stack.peek_h256(1)?; storage_target = StorageTarget::Slot(address, index); GasCost::SStore { @@ -578,30 +578,30 @@ pub fn dynamic_opcode_cost( } Opcode::LOG0 if !is_static => GasCost::Log { n: 0, - len: U256::from_big_endian(&stack.peek(1)?[..]), + len: stack.peek(1)?, }, Opcode::LOG1 if !is_static => GasCost::Log { n: 1, - len: U256::from_big_endian(&stack.peek(1)?[..]), + len: stack.peek(1)?, }, Opcode::LOG2 if !is_static => GasCost::Log { n: 2, - len: U256::from_big_endian(&stack.peek(1)?[..]), + len: stack.peek(1)?, }, Opcode::LOG3 if !is_static => GasCost::Log { n: 3, - len: U256::from_big_endian(&stack.peek(1)?[..]), + len: stack.peek(1)?, }, Opcode::LOG4 if !is_static => GasCost::Log { n: 4, - len: U256::from_big_endian(&stack.peek(1)?[..]), + len: stack.peek(1)?, }, Opcode::CREATE if !is_static => GasCost::Create, Opcode::CREATE2 if !is_static && config.has_create2 => GasCost::Create2 { - len: U256::from_big_endian(&stack.peek(2)?[..]), + len: stack.peek(2)?, }, Opcode::SUICIDE if !is_static => { - let target = stack.peek(0)?.into(); + let target = stack.peek_h256(0)?.into(); storage_target = StorageTarget::Address(target); GasCost::Suicide { value: handler.balance(address), @@ -610,15 +610,12 @@ pub fn dynamic_opcode_cost( already_removed: handler.deleted(address), } } - Opcode::CALL - if !is_static - || (is_static && U256::from_big_endian(&stack.peek(2)?[..]) == U256::zero()) => - { - let target = stack.peek(1)?.into(); + Opcode::CALL if !is_static || (is_static && stack.peek(2)? == U256::zero()) => { + let target = stack.peek_h256(1)?.into(); storage_target = StorageTarget::Address(target); GasCost::Call { - value: U256::from_big_endian(&stack.peek(2)?[..]), - gas: U256::from_big_endian(&stack.peek(0)?[..]), + value: stack.peek(2)?, + gas: stack.peek(0)?, target_is_cold: handler.is_cold(target, None)?, target_exists: handler.exists(target), } @@ -638,54 +635,54 @@ pub fn dynamic_opcode_cost( | Opcode::LOG2 | Opcode::LOG3 | Opcode::LOG4 => Some(MemoryCost { - offset: U256::from_big_endian(&stack.peek(0)?[..]), - len: U256::from_big_endian(&stack.peek(1)?[..]), + offset: stack.peek(0)?, + len: stack.peek(1)?, }), Opcode::CODECOPY | Opcode::CALLDATACOPY | Opcode::RETURNDATACOPY => Some(MemoryCost { - offset: U256::from_big_endian(&stack.peek(0)?[..]), - len: U256::from_big_endian(&stack.peek(2)?[..]), + offset: stack.peek(0)?, + len: stack.peek(2)?, }), Opcode::EXTCODECOPY => Some(MemoryCost { - offset: U256::from_big_endian(&stack.peek(1)?[..]), - len: U256::from_big_endian(&stack.peek(3)?[..]), + offset: stack.peek(1)?, + len: stack.peek(3)?, }), Opcode::MLOAD | Opcode::MSTORE => Some(MemoryCost { - offset: U256::from_big_endian(&stack.peek(0)?[..]), + offset: stack.peek(0)?, len: U256::from(32), }), Opcode::MSTORE8 => Some(MemoryCost { - offset: U256::from_big_endian(&stack.peek(0)?[..]), + offset: stack.peek(0)?, len: U256::from(1), }), Opcode::CREATE | Opcode::CREATE2 => Some(MemoryCost { - offset: U256::from_big_endian(&stack.peek(1)?[..]), - len: U256::from_big_endian(&stack.peek(2)?[..]), + offset: stack.peek(1)?, + len: stack.peek(2)?, }), Opcode::CALL | Opcode::CALLCODE => Some( MemoryCost { - offset: U256::from_big_endian(&stack.peek(3)?[..]), - len: U256::from_big_endian(&stack.peek(4)?[..]), + offset: stack.peek(3)?, + len: stack.peek(4)?, } .join(MemoryCost { - offset: U256::from_big_endian(&stack.peek(5)?[..]), - len: U256::from_big_endian(&stack.peek(6)?[..]), + offset: stack.peek(5)?, + len: stack.peek(6)?, }), ), Opcode::DELEGATECALL | Opcode::STATICCALL => Some( MemoryCost { - offset: U256::from_big_endian(&stack.peek(2)?[..]), - len: U256::from_big_endian(&stack.peek(3)?[..]), + offset: stack.peek(2)?, + len: stack.peek(3)?, } .join(MemoryCost { - offset: U256::from_big_endian(&stack.peek(4)?[..]), - len: U256::from_big_endian(&stack.peek(5)?[..]), + offset: stack.peek(4)?, + len: stack.peek(5)?, }), ), diff --git a/runtime/src/eval/macros.rs b/runtime/src/eval/macros.rs index 076866fba..bace4c90e 100644 --- a/runtime/src/eval/macros.rs +++ b/runtime/src/eval/macros.rs @@ -7,11 +7,15 @@ macro_rules! try_or_fail { }; } -macro_rules! pop { +macro_rules! pop_h256 { ( $machine:expr, $( $x:ident ),* ) => ( $( let $x = match $machine.machine.stack_mut().pop() { - Ok(value) => value, + Ok(value) => { + let mut res = H256([0; 32]); + value.to_big_endian(&mut res[..]); + res + }, Err(e) => return Control::Exit(e.into()), }; )* @@ -22,17 +26,17 @@ macro_rules! pop_u256 { ( $machine:expr, $( $x:ident ),* ) => ( $( let $x = match $machine.machine.stack_mut().pop() { - Ok(value) => U256::from_big_endian(&value[..]), + Ok(value) => value, Err(e) => return Control::Exit(e.into()), }; )* ); } -macro_rules! push { +macro_rules! push_h256 { ( $machine:expr, $( $x:expr ),* ) => ( $( - match $machine.machine.stack_mut().push($x) { + match $machine.machine.stack_mut().push(U256::from_big_endian(&$x[..])) { Ok(()) => (), Err(e) => return Control::Exit(e.into()), } @@ -43,9 +47,7 @@ macro_rules! push { macro_rules! push_u256 { ( $machine:expr, $( $x:expr ),* ) => ( $( - let mut value = H256::default(); - $x.to_big_endian(&mut value[..]); - match $machine.machine.stack_mut().push(value) { + match $machine.machine.stack_mut().push($x) { Ok(()) => (), Err(e) => return Control::Exit(e.into()), } diff --git a/runtime/src/eval/mod.rs b/runtime/src/eval/mod.rs index b2965335f..f317d0b61 100644 --- a/runtime/src/eval/mod.rs +++ b/runtime/src/eval/mod.rs @@ -74,19 +74,22 @@ pub fn finish_create( match reason { ExitReason::Succeed(_) => { - runtime.machine.stack_mut().push(create_address)?; + runtime + .machine + .stack_mut() + .push(U256::from_big_endian(&create_address[..]))?; Ok(()) } ExitReason::Revert(_) => { - runtime.machine.stack_mut().push(H256::default())?; + runtime.machine.stack_mut().push(U256::zero())?; Ok(()) } ExitReason::Error(_) => { - runtime.machine.stack_mut().push(H256::default())?; + runtime.machine.stack_mut().push(U256::zero())?; Ok(()) } ExitReason::Fatal(e) => { - runtime.machine.stack_mut().push(H256::default())?; + runtime.machine.stack_mut().push(U256::zero())?; Err(e.into()) } } @@ -111,19 +114,17 @@ pub fn finish_call( &runtime.return_data_buffer[..], ) { Ok(()) => { - let mut value = H256::default(); - U256::one().to_big_endian(&mut value[..]); - runtime.machine.stack_mut().push(value)?; + runtime.machine.stack_mut().push(U256::one())?; Ok(()) } Err(_) => { - runtime.machine.stack_mut().push(H256::default())?; + runtime.machine.stack_mut().push(U256::zero())?; Ok(()) } } } ExitReason::Revert(_) => { - runtime.machine.stack_mut().push(H256::default())?; + runtime.machine.stack_mut().push(U256::zero())?; let _ = runtime.machine.memory_mut().copy_large( out_offset, @@ -135,12 +136,12 @@ pub fn finish_call( Ok(()) } ExitReason::Error(_) => { - runtime.machine.stack_mut().push(H256::default())?; + runtime.machine.stack_mut().push(U256::zero())?; Ok(()) } ExitReason::Fatal(e) => { - runtime.machine.stack_mut().push(H256::default())?; + runtime.machine.stack_mut().push(U256::zero())?; Err(e.into()) } diff --git a/runtime/src/eval/system.rs b/runtime/src/eval/system.rs index e7ef50365..26f9c8a4d 100644 --- a/runtime/src/eval/system.rs +++ b/runtime/src/eval/system.rs @@ -21,7 +21,7 @@ pub fn sha3(runtime: &mut Runtime) -> Control { }; let ret = Keccak256::digest(data.as_slice()); - push!(runtime, H256::from_slice(ret.as_slice())); + push_h256!(runtime, H256::from_slice(ret.as_slice())); Control::Continue } @@ -34,13 +34,13 @@ pub fn chainid(runtime: &mut Runtime, handler: &H) -> Control { pub fn address(runtime: &mut Runtime) -> Control { let ret = H256::from(runtime.context.address); - push!(runtime, ret); + push_h256!(runtime, ret); Control::Continue } pub fn balance(runtime: &mut Runtime, handler: &H) -> Control { - pop!(runtime, address); + pop_h256!(runtime, address); push_u256!(runtime, handler.balance(address.into())); Control::Continue @@ -54,14 +54,14 @@ pub fn selfbalance(runtime: &mut Runtime, handler: &H) -> Control pub fn origin(runtime: &mut Runtime, handler: &H) -> Control { let ret = H256::from(handler.origin()); - push!(runtime, ret); + push_h256!(runtime, ret); Control::Continue } pub fn caller(runtime: &mut Runtime) -> Control { let ret = H256::from(runtime.context.caller); - push!(runtime, ret); + push_h256!(runtime, ret); Control::Continue } @@ -69,7 +69,7 @@ pub fn caller(runtime: &mut Runtime) -> Control { pub fn callvalue(runtime: &mut Runtime) -> Control { let mut ret = H256::default(); runtime.context.apparent_value.to_big_endian(&mut ret[..]); - push!(runtime, ret); + push_h256!(runtime, ret); Control::Continue } @@ -77,7 +77,7 @@ pub fn callvalue(runtime: &mut Runtime) -> Control { pub fn gasprice(runtime: &mut Runtime, handler: &H) -> Control { let mut ret = H256::default(); handler.gas_price().to_big_endian(&mut ret[..]); - push!(runtime, ret); + push_h256!(runtime, ret); Control::Continue } @@ -85,27 +85,27 @@ pub fn gasprice(runtime: &mut Runtime, handler: &H) -> Control { pub fn base_fee(runtime: &mut Runtime, handler: &H) -> Control { let mut ret = H256::default(); handler.block_base_fee_per_gas().to_big_endian(&mut ret[..]); - push!(runtime, ret); + push_h256!(runtime, ret); Control::Continue } pub fn extcodesize(runtime: &mut Runtime, handler: &H) -> Control { - pop!(runtime, address); + pop_h256!(runtime, address); push_u256!(runtime, handler.code_size(address.into())); Control::Continue } pub fn extcodehash(runtime: &mut Runtime, handler: &H) -> Control { - pop!(runtime, address); - push!(runtime, handler.code_hash(address.into())); + pop_h256!(runtime, address); + push_h256!(runtime, handler.code_hash(address.into())); Control::Continue } pub fn extcodecopy(runtime: &mut Runtime, handler: &H) -> Control { - pop!(runtime, address); + pop_h256!(runtime, address); pop_u256!(runtime, memory_offset, code_offset, len); try_or_fail!(runtime @@ -160,13 +160,13 @@ pub fn returndatacopy(runtime: &mut Runtime) -> Control { pub fn blockhash(runtime: &mut Runtime, handler: &H) -> Control { pop_u256!(runtime, number); - push!(runtime, handler.block_hash(number)); + push_h256!(runtime, handler.block_hash(number)); Control::Continue } pub fn coinbase(runtime: &mut Runtime, handler: &H) -> Control { - push!(runtime, handler.block_coinbase().into()); + push_h256!(runtime, handler.block_coinbase()); Control::Continue } @@ -187,7 +187,7 @@ pub fn difficulty(runtime: &mut Runtime, handler: &H) -> Control pub fn prevrandao(runtime: &mut Runtime, handler: &H) -> Control { if let Some(rand) = handler.block_randomness() { - push!(runtime, rand); + push_h256!(runtime, rand); Control::Continue } else { difficulty(runtime, handler) @@ -200,9 +200,9 @@ pub fn gaslimit(runtime: &mut Runtime, handler: &H) -> Control { } pub fn sload(runtime: &mut Runtime, handler: &H) -> Control { - pop!(runtime, index); + pop_h256!(runtime, index); let value = handler.storage(runtime.context.address, index); - push!(runtime, value); + push_h256!(runtime, value); event!(SLoad { address: runtime.context.address, @@ -214,7 +214,7 @@ pub fn sload(runtime: &mut Runtime, handler: &H) -> Control { } pub fn sstore(runtime: &mut Runtime, handler: &mut H) -> Control { - pop!(runtime, index, value); + pop_h256!(runtime, index, value); event!(SStore { address: runtime.context.address, @@ -249,7 +249,7 @@ pub fn log(runtime: &mut Runtime, n: u8, handler: &mut H) -> Control let mut topics = Vec::new(); for _ in 0..(n as usize) { - match runtime.machine.stack_mut().pop() { + match runtime.machine.stack_mut().pop_h256() { Ok(value) => { topics.push(value); } @@ -264,7 +264,7 @@ pub fn log(runtime: &mut Runtime, n: u8, handler: &mut H) -> Control } pub fn suicide(runtime: &mut Runtime, handler: &mut H) -> Control { - pop!(runtime, target); + pop_h256!(runtime, target); match handler.mark_delete(runtime.context.address, target.into()) { Ok(()) => (), @@ -290,7 +290,7 @@ pub fn create(runtime: &mut Runtime, is_create2: bool, handler: &mut }; let scheme = if is_create2 { - pop!(runtime, salt); + pop_h256!(runtime, salt); let code_hash = H256::from_slice(Keccak256::digest(&code).as_slice()); CreateScheme::Create2 { caller: runtime.context.address, @@ -310,7 +310,10 @@ pub fn create(runtime: &mut Runtime, is_create2: bool, handler: &mut Err(e) => Control::Exit(e), } } - Capture::Trap(interrupt) => Control::CreateInterrupt(interrupt), + Capture::Trap(interrupt) => { + push_h256!(runtime, H256::default()); + Control::CreateInterrupt(interrupt) + } } } @@ -318,7 +321,7 @@ pub fn call(runtime: &mut Runtime, scheme: CallScheme, handler: &mut runtime.return_data_buffer = Vec::new(); pop_u256!(runtime, gas); - pop!(runtime, to); + pop_h256!(runtime, to); let gas = if gas > U256::from(u64::MAX) { None } else { @@ -402,8 +405,7 @@ pub fn call(runtime: &mut Runtime, scheme: CallScheme, handler: &mut } } Capture::Trap(interrupt) => { - runtime.return_data_len = out_len; - runtime.return_data_offset = out_offset; + push_h256!(runtime, H256::default()); Control::CallInterrupt(interrupt) } } From 1c200fdd02591230ebed053dbc856472cb0a340e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 18 Feb 2022 23:34:02 +0300 Subject: [PATCH 02/23] Optimization: Specialize small pushes (#10) --- core/src/eval/misc.rs | 19 +++++++++++++++++++ core/src/eval/mod.rs | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/core/src/eval/misc.rs b/core/src/eval/misc.rs index a4c5f03a5..b77ff499c 100644 --- a/core/src/eval/misc.rs +++ b/core/src/eval/misc.rs @@ -163,6 +163,25 @@ pub fn push(state: &mut Machine, n: usize, position: usize) -> Control { Control::Continue(1 + n) } +#[inline] +pub fn push1(state: &mut Machine, position: usize) -> Control { + let b0 = *state.code.get(position + 1).unwrap_or(&0) as u64; + let val = U256::from(b0); + + push_u256!(state, val); + Control::Continue(2) +} + +#[inline] +pub fn push2(state: &mut Machine, position: usize) -> Control { + let b0 = *state.code.get(position + 1).unwrap_or(&0) as u64; + let b1 = *state.code.get(position + 2).unwrap_or(&0) as u64; + let val = U256::from((b0 << 8) | b1); + + push_u256!(state, val); + Control::Continue(3) +} + #[inline] pub fn dup(state: &mut Machine, n: usize) -> Control { let value = match state.stack.peek(n - 1) { diff --git a/core/src/eval/mod.rs b/core/src/eval/mod.rs index c71721b42..3b1ea71ae 100644 --- a/core/src/eval/mod.rs +++ b/core/src/eval/mod.rs @@ -181,11 +181,11 @@ fn eval_push0(state: &mut Machine, _opcode: Opcode, position: usize) -> Control } fn eval_push1(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 1, position) + self::misc::push1(state, position) } fn eval_push2(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 2, position) + self::misc::push2(state, position) } fn eval_push3(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { From e9595059a62077fba49a5112cff9022e48c2bd33 Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Mon, 28 Feb 2022 17:24:55 +0300 Subject: [PATCH 03/23] Optimize execution (#5) --- Cargo.toml | 1 + core/Cargo.toml | 1 + core/src/eval/misc.rs | 8 + core/src/eval/mod.rs | 854 ++++++++++++--------------------- core/src/lib.rs | 112 +++-- gasometer/src/consts.rs | 42 +- gasometer/src/costs.rs | 12 +- gasometer/src/lib.rs | 24 +- gasometer/src/memory.rs | 2 +- runtime/src/handler.rs | 10 +- runtime/src/lib.rs | 38 +- runtime/src/tracing.rs | 8 +- src/executor/stack/executor.rs | 129 +++-- 13 files changed, 534 insertions(+), 707 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 94ae9d9db..3d43f3849 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,6 +66,7 @@ with-serde = [ ] tracing = [ "environmental", + "evm-core/tracing", "evm-gasometer/tracing", "evm-runtime/tracing", ] diff --git a/core/Cargo.toml b/core/Cargo.toml index 3b5618cd5..150e5eb9e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -34,3 +34,4 @@ with-serde = [ "serde", "primitive-types/impl-serde", ] +tracing = [] diff --git a/core/src/eval/misc.rs b/core/src/eval/misc.rs index b77ff499c..8c28af696 100644 --- a/core/src/eval/misc.rs +++ b/core/src/eval/misc.rs @@ -163,6 +163,14 @@ pub fn push(state: &mut Machine, n: usize, position: usize) -> Control { Control::Continue(1 + n) } +#[inline] +pub fn push0(state: &mut Machine) -> Control { + let val = U256::zero(); + + push_u256!(state, val); + Control::Continue(2) +} + #[inline] pub fn push1(state: &mut Machine, position: usize) -> Control { let b0 = *state.code.get(position + 1).unwrap_or(&0) as u64; diff --git a/core/src/eval/mod.rs b/core/src/eval/mod.rs index 3b1ea71ae..2c0fcb4b3 100644 --- a/core/src/eval/mod.rs +++ b/core/src/eval/mod.rs @@ -4,9 +4,9 @@ mod arithmetic; mod bitwise; mod misc; -use crate::{ExitError, ExitReason, ExitSucceed, Machine, Opcode}; +use crate::{ExitError, ExitReason, ExitSucceed, InterpreterHandler, Machine, Opcode}; use core::ops::{BitAnd, BitOr, BitXor}; -use primitive_types::U256; +use primitive_types::{H160, U256}; #[derive(Clone, Eq, PartialEq, Debug)] pub enum Control { @@ -16,562 +16,310 @@ pub enum Control { Trap(Opcode), } -fn eval_stop(_state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - Control::Exit(ExitSucceed::Stopped.into()) -} - -fn eval_add(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_tuple!(state, overflowing_add) -} - -fn eval_mul(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_tuple!(state, overflowing_mul) -} - -fn eval_sub(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_tuple!(state, overflowing_sub) -} - -fn eval_div(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_fn!(state, self::arithmetic::div) -} - -fn eval_sdiv(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_fn!(state, self::arithmetic::sdiv) -} - -fn eval_mod(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_fn!(state, self::arithmetic::rem) -} - -fn eval_smod(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_fn!(state, self::arithmetic::srem) -} - -fn eval_addmod(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op3_u256_fn!(state, self::arithmetic::addmod) -} - -fn eval_mulmod(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op3_u256_fn!(state, self::arithmetic::mulmod) -} - -fn eval_exp(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_fn!(state, self::arithmetic::exp) -} - -fn eval_signextend(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_fn!(state, self::arithmetic::signextend) -} - -fn eval_lt(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_bool_ref!(state, lt) -} - -fn eval_gt(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_bool_ref!(state, gt) -} - -fn eval_slt(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_fn!(state, self::bitwise::slt) -} - -fn eval_sgt(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_fn!(state, self::bitwise::sgt) -} - -fn eval_eq(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_bool_ref!(state, eq) -} - -fn eval_iszero(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op1_u256_fn!(state, self::bitwise::iszero) -} - -fn eval_and(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256!(state, bitand) -} - -fn eval_or(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256!(state, bitor) -} - -fn eval_xor(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256!(state, bitxor) -} - -fn eval_not(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op1_u256_fn!(state, self::bitwise::not) -} - -fn eval_byte(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_fn!(state, self::bitwise::byte) -} - -fn eval_shl(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_fn!(state, self::bitwise::shl) -} - -fn eval_shr(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_fn!(state, self::bitwise::shr) -} - -fn eval_sar(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - op2_u256_fn!(state, self::bitwise::sar) -} - -fn eval_codesize(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::codesize(state) -} - -fn eval_codecopy(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::codecopy(state) -} - -fn eval_calldataload(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::calldataload(state) -} - -fn eval_calldatasize(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::calldatasize(state) -} - -fn eval_calldatacopy(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::calldatacopy(state) -} - -fn eval_pop(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::pop(state) -} - -fn eval_mload(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::mload(state) -} - -fn eval_mstore(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::mstore(state) -} - -fn eval_mstore8(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::mstore8(state) -} - -fn eval_jump(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::jump(state) -} - -fn eval_jumpi(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::jumpi(state) -} - -fn eval_pc(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::pc(state, position) -} - -fn eval_msize(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::msize(state) -} - -fn eval_jumpdest(_state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - Control::Continue(1) -} - -fn eval_push0(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 0, position) -} - -fn eval_push1(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push1(state, position) -} - -fn eval_push2(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push2(state, position) -} - -fn eval_push3(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 3, position) -} - -fn eval_push4(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 4, position) -} - -fn eval_push5(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 5, position) -} - -fn eval_push6(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 6, position) -} - -fn eval_push7(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 7, position) -} - -fn eval_push8(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 8, position) -} - -fn eval_push9(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 9, position) -} - -fn eval_push10(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 10, position) -} - -fn eval_push11(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 11, position) -} - -fn eval_push12(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 12, position) -} - -fn eval_push13(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 13, position) -} - -fn eval_push14(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 14, position) -} - -fn eval_push15(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 15, position) -} - -fn eval_push16(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 16, position) -} - -fn eval_push17(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 17, position) -} - -fn eval_push18(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 18, position) -} - -fn eval_push19(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 19, position) -} - -fn eval_push20(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 20, position) -} - -fn eval_push21(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 21, position) -} - -fn eval_push22(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 22, position) -} - -fn eval_push23(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 23, position) -} - -fn eval_push24(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 24, position) -} - -fn eval_push25(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 25, position) -} - -fn eval_push26(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 26, position) -} - -fn eval_push27(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 27, position) -} - -fn eval_push28(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 28, position) -} - -fn eval_push29(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 29, position) -} - -fn eval_push30(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 30, position) -} - -fn eval_push31(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 31, position) -} - -fn eval_push32(state: &mut Machine, _opcode: Opcode, position: usize) -> Control { - self::misc::push(state, 32, position) -} - -fn eval_dup1(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 1) -} - -fn eval_dup2(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 2) -} - -fn eval_dup3(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 3) -} - -fn eval_dup4(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 4) -} - -fn eval_dup5(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 5) -} - -fn eval_dup6(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 6) -} - -fn eval_dup7(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 7) -} - -fn eval_dup8(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 8) -} - -fn eval_dup9(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 9) -} - -fn eval_dup10(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 10) -} - -fn eval_dup11(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 11) -} - -fn eval_dup12(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 12) -} - -fn eval_dup13(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 13) -} - -fn eval_dup14(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 14) -} - -fn eval_dup15(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 15) -} - -fn eval_dup16(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::dup(state, 16) -} - -fn eval_swap1(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 1) -} - -fn eval_swap2(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 2) -} - -fn eval_swap3(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 3) -} - -fn eval_swap4(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 4) -} - -fn eval_swap5(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 5) -} - -fn eval_swap6(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 6) -} - -fn eval_swap7(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 7) -} - -fn eval_swap8(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 8) -} - -fn eval_swap9(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 9) -} - -fn eval_swap10(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 10) -} - -fn eval_swap11(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 11) -} - -fn eval_swap12(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 12) -} - -fn eval_swap13(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 13) -} - -fn eval_swap14(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 14) -} - -fn eval_swap15(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 15) -} - -fn eval_swap16(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::swap(state, 16) -} - -fn eval_return(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::ret(state) -} - -fn eval_revert(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - self::misc::revert(state) -} - -fn eval_invalid(_state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { - Control::Exit(ExitError::DesignatedInvalid.into()) -} - -fn eval_external(_state: &mut Machine, opcode: Opcode, _position: usize) -> Control { - Control::Trap(opcode) +#[inline] +pub fn eval( + machine: &mut Machine, + position: usize, + handler: &mut H, + address: &H160, +) -> Control { + eval_table(machine, position, handler, address) } +// Table-based interpreter, shows the smallest gas cost. #[inline] -pub fn eval(state: &mut Machine, opcode: Opcode, position: usize) -> Control { +fn eval_table( + state: &mut Machine, + position: usize, + handler: &mut H, + address: &H160, +) -> Control { static TABLE: [fn(state: &mut Machine, opcode: Opcode, position: usize) -> Control; 256] = { + fn eval_external(state: &mut Machine, opcode: Opcode, position: usize) -> Control { + state.position = Ok(position + 1); + Control::Trap(opcode) + } let mut table = [eval_external as _; 256]; - - table[Opcode::STOP.as_usize()] = eval_stop as _; - table[Opcode::ADD.as_usize()] = eval_add as _; - table[Opcode::MUL.as_usize()] = eval_mul as _; - table[Opcode::SUB.as_usize()] = eval_sub as _; - table[Opcode::DIV.as_usize()] = eval_div as _; - table[Opcode::SDIV.as_usize()] = eval_sdiv as _; - table[Opcode::MOD.as_usize()] = eval_mod as _; - table[Opcode::SMOD.as_usize()] = eval_smod as _; - table[Opcode::ADDMOD.as_usize()] = eval_addmod as _; - table[Opcode::MULMOD.as_usize()] = eval_mulmod as _; - table[Opcode::EXP.as_usize()] = eval_exp as _; - table[Opcode::SIGNEXTEND.as_usize()] = eval_signextend as _; - table[Opcode::LT.as_usize()] = eval_lt as _; - table[Opcode::GT.as_usize()] = eval_gt as _; - table[Opcode::SLT.as_usize()] = eval_slt as _; - table[Opcode::SGT.as_usize()] = eval_sgt as _; - table[Opcode::EQ.as_usize()] = eval_eq as _; - table[Opcode::ISZERO.as_usize()] = eval_iszero as _; - table[Opcode::AND.as_usize()] = eval_and as _; - table[Opcode::OR.as_usize()] = eval_or as _; - table[Opcode::XOR.as_usize()] = eval_xor as _; - table[Opcode::NOT.as_usize()] = eval_not as _; - table[Opcode::BYTE.as_usize()] = eval_byte as _; - table[Opcode::SHL.as_usize()] = eval_shl as _; - table[Opcode::SHR.as_usize()] = eval_shr as _; - table[Opcode::SAR.as_usize()] = eval_sar as _; - table[Opcode::CODESIZE.as_usize()] = eval_codesize as _; - table[Opcode::CODECOPY.as_usize()] = eval_codecopy as _; - table[Opcode::CALLDATALOAD.as_usize()] = eval_calldataload as _; - table[Opcode::CALLDATASIZE.as_usize()] = eval_calldatasize as _; - table[Opcode::CALLDATACOPY.as_usize()] = eval_calldatacopy as _; - table[Opcode::POP.as_usize()] = eval_pop as _; - table[Opcode::MLOAD.as_usize()] = eval_mload as _; - table[Opcode::MSTORE.as_usize()] = eval_mstore as _; - table[Opcode::MSTORE8.as_usize()] = eval_mstore8 as _; - table[Opcode::JUMP.as_usize()] = eval_jump as _; - table[Opcode::JUMPI.as_usize()] = eval_jumpi as _; - table[Opcode::PC.as_usize()] = eval_pc as _; - table[Opcode::MSIZE.as_usize()] = eval_msize as _; - table[Opcode::JUMPDEST.as_usize()] = eval_jumpdest as _; - - table[Opcode::PUSH0.as_usize()] = eval_push0 as _; - table[Opcode::PUSH1.as_usize()] = eval_push1 as _; - table[Opcode::PUSH2.as_usize()] = eval_push2 as _; - table[Opcode::PUSH3.as_usize()] = eval_push3 as _; - table[Opcode::PUSH4.as_usize()] = eval_push4 as _; - table[Opcode::PUSH5.as_usize()] = eval_push5 as _; - table[Opcode::PUSH6.as_usize()] = eval_push6 as _; - table[Opcode::PUSH7.as_usize()] = eval_push7 as _; - table[Opcode::PUSH8.as_usize()] = eval_push8 as _; - table[Opcode::PUSH9.as_usize()] = eval_push9 as _; - table[Opcode::PUSH10.as_usize()] = eval_push10 as _; - table[Opcode::PUSH11.as_usize()] = eval_push11 as _; - table[Opcode::PUSH12.as_usize()] = eval_push12 as _; - table[Opcode::PUSH13.as_usize()] = eval_push13 as _; - table[Opcode::PUSH14.as_usize()] = eval_push14 as _; - table[Opcode::PUSH15.as_usize()] = eval_push15 as _; - table[Opcode::PUSH16.as_usize()] = eval_push16 as _; - table[Opcode::PUSH17.as_usize()] = eval_push17 as _; - table[Opcode::PUSH18.as_usize()] = eval_push18 as _; - table[Opcode::PUSH19.as_usize()] = eval_push19 as _; - table[Opcode::PUSH20.as_usize()] = eval_push20 as _; - table[Opcode::PUSH21.as_usize()] = eval_push21 as _; - table[Opcode::PUSH22.as_usize()] = eval_push22 as _; - table[Opcode::PUSH23.as_usize()] = eval_push23 as _; - table[Opcode::PUSH24.as_usize()] = eval_push24 as _; - table[Opcode::PUSH25.as_usize()] = eval_push25 as _; - table[Opcode::PUSH26.as_usize()] = eval_push26 as _; - table[Opcode::PUSH27.as_usize()] = eval_push27 as _; - table[Opcode::PUSH28.as_usize()] = eval_push28 as _; - table[Opcode::PUSH29.as_usize()] = eval_push29 as _; - table[Opcode::PUSH30.as_usize()] = eval_push30 as _; - table[Opcode::PUSH31.as_usize()] = eval_push31 as _; - table[Opcode::PUSH32.as_usize()] = eval_push32 as _; - - table[Opcode::DUP1.as_usize()] = eval_dup1 as _; - table[Opcode::DUP2.as_usize()] = eval_dup2 as _; - table[Opcode::DUP3.as_usize()] = eval_dup3 as _; - table[Opcode::DUP4.as_usize()] = eval_dup4 as _; - table[Opcode::DUP5.as_usize()] = eval_dup5 as _; - table[Opcode::DUP6.as_usize()] = eval_dup6 as _; - table[Opcode::DUP7.as_usize()] = eval_dup7 as _; - table[Opcode::DUP8.as_usize()] = eval_dup8 as _; - table[Opcode::DUP9.as_usize()] = eval_dup9 as _; - table[Opcode::DUP10.as_usize()] = eval_dup10 as _; - table[Opcode::DUP11.as_usize()] = eval_dup11 as _; - table[Opcode::DUP12.as_usize()] = eval_dup12 as _; - table[Opcode::DUP13.as_usize()] = eval_dup13 as _; - table[Opcode::DUP14.as_usize()] = eval_dup14 as _; - table[Opcode::DUP15.as_usize()] = eval_dup15 as _; - table[Opcode::DUP16.as_usize()] = eval_dup16 as _; - - table[Opcode::SWAP1.as_usize()] = eval_swap1 as _; - table[Opcode::SWAP2.as_usize()] = eval_swap2 as _; - table[Opcode::SWAP3.as_usize()] = eval_swap3 as _; - table[Opcode::SWAP4.as_usize()] = eval_swap4 as _; - table[Opcode::SWAP5.as_usize()] = eval_swap5 as _; - table[Opcode::SWAP6.as_usize()] = eval_swap6 as _; - table[Opcode::SWAP7.as_usize()] = eval_swap7 as _; - table[Opcode::SWAP8.as_usize()] = eval_swap8 as _; - table[Opcode::SWAP9.as_usize()] = eval_swap9 as _; - table[Opcode::SWAP10.as_usize()] = eval_swap10 as _; - table[Opcode::SWAP11.as_usize()] = eval_swap11 as _; - table[Opcode::SWAP12.as_usize()] = eval_swap12 as _; - table[Opcode::SWAP13.as_usize()] = eval_swap13 as _; - table[Opcode::SWAP14.as_usize()] = eval_swap14 as _; - table[Opcode::SWAP15.as_usize()] = eval_swap15 as _; - table[Opcode::SWAP16.as_usize()] = eval_swap16 as _; - - table[Opcode::RETURN.as_usize()] = eval_return as _; - table[Opcode::REVERT.as_usize()] = eval_revert as _; - table[Opcode::INVALID.as_usize()] = eval_invalid as _; - + macro_rules! table_elem { + ($operation:ident, $definition:expr) => { + table_elem!($operation, _state, $definition) + }; + ($operation:ident, $state:ident, $definition:expr) => { + table_elem!($operation, $state, _pc, $definition) + }; + ($operation:ident, $state:ident, $pc:ident, $definition:expr) => { + #[allow(non_snake_case)] + fn $operation($state: &mut Machine, _opcode: Opcode, $pc: usize) -> Control { + $definition + } + table[Opcode::$operation.as_usize()] = $operation as _; + }; + } + table_elem!(ADD, state, op2_u256_tuple!(state, overflowing_add)); + table_elem!(MUL, state, op2_u256_tuple!(state, overflowing_mul)); + table_elem!(SUB, state, op2_u256_tuple!(state, overflowing_sub)); + table_elem!(DIV, state, op2_u256_fn!(state, self::arithmetic::div)); + table_elem!(SDIV, state, op2_u256_fn!(state, self::arithmetic::sdiv)); + table_elem!(EXP, state, op2_u256_fn!(state, self::arithmetic::exp)); + table_elem!( + SIGNEXTEND, + state, + op2_u256_fn!(state, self::arithmetic::signextend) + ); + table_elem!(LT, state, op2_u256_bool_ref!(state, lt)); + table_elem!(GT, state, op2_u256_bool_ref!(state, gt)); + table_elem!(SLT, state, op2_u256_fn!(state, self::bitwise::slt)); + table_elem!(SGT, state, op2_u256_fn!(state, self::bitwise::sgt)); + table_elem!(EQ, state, op2_u256_bool_ref!(state, eq)); + table_elem!(ISZERO, state, op1_u256_fn!(state, self::bitwise::iszero)); + table_elem!(AND, state, op2_u256!(state, bitand)); + table_elem!(OR, state, op2_u256!(state, bitor)); + table_elem!(XOR, state, op2_u256!(state, bitxor)); + table_elem!(NOT, state, op1_u256_fn!(state, self::bitwise::not)); + table_elem!(BYTE, state, op2_u256_fn!(state, self::bitwise::byte)); + table_elem!(SHL, state, op2_u256_fn!(state, self::bitwise::shl)); + table_elem!(SHR, state, op2_u256_fn!(state, self::bitwise::shr)); + table_elem!(SAR, state, op2_u256_fn!(state, self::bitwise::sar)); + table_elem!(POP, state, self::misc::pop(state)); + table_elem!(PC, state, position, self::misc::pc(state, position)); + table_elem!(MSIZE, state, self::misc::msize(state)); + table_elem!(PUSH0, state, self::misc::push0(state)); + table_elem!(PUSH1, state, position, self::misc::push1(state, position)); + table_elem!(PUSH2, state, position, self::misc::push2(state, position)); + table_elem!(PUSH3, state, position, self::misc::push(state, 3, position)); + table_elem!(PUSH4, state, position, self::misc::push(state, 4, position)); + table_elem!(PUSH5, state, position, self::misc::push(state, 5, position)); + table_elem!(PUSH6, state, position, self::misc::push(state, 6, position)); + table_elem!(PUSH7, state, position, self::misc::push(state, 7, position)); + table_elem!(PUSH8, state, position, self::misc::push(state, 8, position)); + table_elem!(PUSH9, state, position, self::misc::push(state, 9, position)); + table_elem!( + PUSH10, + state, + position, + self::misc::push(state, 10, position) + ); + table_elem!( + PUSH11, + state, + position, + self::misc::push(state, 11, position) + ); + table_elem!( + PUSH12, + state, + position, + self::misc::push(state, 12, position) + ); + table_elem!( + PUSH13, + state, + position, + self::misc::push(state, 13, position) + ); + table_elem!( + PUSH14, + state, + position, + self::misc::push(state, 14, position) + ); + table_elem!( + PUSH15, + state, + position, + self::misc::push(state, 15, position) + ); + table_elem!( + PUSH16, + state, + position, + self::misc::push(state, 16, position) + ); + table_elem!( + PUSH17, + state, + position, + self::misc::push(state, 17, position) + ); + table_elem!( + PUSH18, + state, + position, + self::misc::push(state, 18, position) + ); + table_elem!( + PUSH19, + state, + position, + self::misc::push(state, 19, position) + ); + table_elem!( + PUSH20, + state, + position, + self::misc::push(state, 20, position) + ); + table_elem!( + PUSH21, + state, + position, + self::misc::push(state, 21, position) + ); + table_elem!( + PUSH22, + state, + position, + self::misc::push(state, 22, position) + ); + table_elem!( + PUSH23, + state, + position, + self::misc::push(state, 23, position) + ); + table_elem!( + PUSH24, + state, + position, + self::misc::push(state, 24, position) + ); + table_elem!( + PUSH25, + state, + position, + self::misc::push(state, 25, position) + ); + table_elem!( + PUSH26, + state, + position, + self::misc::push(state, 26, position) + ); + table_elem!( + PUSH27, + state, + position, + self::misc::push(state, 27, position) + ); + table_elem!( + PUSH28, + state, + position, + self::misc::push(state, 28, position) + ); + table_elem!( + PUSH29, + state, + position, + self::misc::push(state, 29, position) + ); + table_elem!( + PUSH30, + state, + position, + self::misc::push(state, 30, position) + ); + table_elem!( + PUSH31, + state, + position, + self::misc::push(state, 31, position) + ); + table_elem!( + PUSH32, + state, + position, + self::misc::push(state, 32, position) + ); + table_elem!(MOD, state, op2_u256_fn!(state, self::arithmetic::rem)); + table_elem!(SMOD, state, op2_u256_fn!(state, self::arithmetic::srem)); + table_elem!(CODESIZE, state, self::misc::codesize(state)); + table_elem!(CALLDATALOAD, state, self::misc::calldataload(state)); + table_elem!(CALLDATASIZE, state, self::misc::calldatasize(state)); + table_elem!(ADDMOD, state, op3_u256_fn!(state, self::arithmetic::addmod)); + table_elem!(MULMOD, state, op3_u256_fn!(state, self::arithmetic::mulmod)); + table_elem!(MLOAD, state, self::misc::mload(state)); + table_elem!(MSTORE, state, self::misc::mstore(state)); + table_elem!(MSTORE8, state, self::misc::mstore8(state)); + table_elem!(CODECOPY, state, self::misc::codecopy(state)); + table_elem!(CALLDATACOPY, state, self::misc::calldatacopy(state)); + table_elem!(DUP1, state, self::misc::dup(state, 1)); + table_elem!(DUP2, state, self::misc::dup(state, 2)); + table_elem!(DUP3, state, self::misc::dup(state, 3)); + table_elem!(DUP4, state, self::misc::dup(state, 4)); + table_elem!(DUP5, state, self::misc::dup(state, 5)); + table_elem!(DUP6, state, self::misc::dup(state, 6)); + table_elem!(DUP7, state, self::misc::dup(state, 7)); + table_elem!(DUP8, state, self::misc::dup(state, 8)); + table_elem!(DUP9, state, self::misc::dup(state, 9)); + table_elem!(DUP10, state, self::misc::dup(state, 10)); + table_elem!(DUP11, state, self::misc::dup(state, 11)); + table_elem!(DUP12, state, self::misc::dup(state, 12)); + table_elem!(DUP13, state, self::misc::dup(state, 13)); + table_elem!(DUP14, state, self::misc::dup(state, 14)); + table_elem!(DUP15, state, self::misc::dup(state, 15)); + table_elem!(DUP16, state, self::misc::dup(state, 16)); + table_elem!(SWAP1, state, self::misc::swap(state, 1)); + table_elem!(SWAP2, state, self::misc::swap(state, 2)); + table_elem!(SWAP3, state, self::misc::swap(state, 3)); + table_elem!(SWAP4, state, self::misc::swap(state, 4)); + table_elem!(SWAP5, state, self::misc::swap(state, 5)); + table_elem!(SWAP6, state, self::misc::swap(state, 6)); + table_elem!(SWAP7, state, self::misc::swap(state, 7)); + table_elem!(SWAP8, state, self::misc::swap(state, 8)); + table_elem!(SWAP9, state, self::misc::swap(state, 9)); + table_elem!(SWAP10, state, self::misc::swap(state, 10)); + table_elem!(SWAP11, state, self::misc::swap(state, 11)); + table_elem!(SWAP12, state, self::misc::swap(state, 12)); + table_elem!(SWAP13, state, self::misc::swap(state, 13)); + table_elem!(SWAP14, state, self::misc::swap(state, 14)); + table_elem!(SWAP15, state, self::misc::swap(state, 15)); + table_elem!(SWAP16, state, self::misc::swap(state, 16)); + table_elem!(RETURN, state, self::misc::ret(state)); + table_elem!(REVERT, state, self::misc::revert(state)); + table_elem!(INVALID, Control::Exit(ExitError::DesignatedInvalid.into())); + table_elem!(STOP, Control::Exit(ExitSucceed::Stopped.into())); + table_elem!(JUMPDEST, Control::Continue(1)); + table_elem!(JUMP, state, self::misc::jump(state)); + table_elem!(JUMPI, state, self::misc::jumpi(state)); table }; - - TABLE[opcode.as_usize()](state, opcode, position) + let mut pc = position; + handler.before_eval(); + loop { + let op = match state.code.get(pc) { + Some(v) => Opcode(*v), + None => { + state.position = Err(ExitSucceed::Stopped.into()); + return Control::Exit(ExitSucceed::Stopped.into()); + } + }; + match handler.before_bytecode(op, pc, state, address) { + Ok(()) => (), + Err(e) => { + state.exit(e.clone().into()); + return Control::Exit(ExitReason::Error(e)); + } + }; + let control = TABLE[op.as_usize()](state, op, pc); + + #[cfg(feature = "tracing")] + { + use crate::Capture; + let result = match &control { + Control::Continue(_) | Control::Jump(_) => Ok(()), + Control::Trap(t) => Err(Capture::Trap(*t)), + Control::Exit(e) => Err(Capture::Exit(e.clone())), + }; + handler.after_bytecode(&result, state); + } + pc = match control { + Control::Continue(bytes) => pc + bytes, + Control::Jump(pos) => pos, + _ => { + handler.after_eval(); + return control; + } + } + } } diff --git a/core/src/lib.rs b/core/src/lib.rs index 0dd187c54..09dc4fdc7 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -24,7 +24,7 @@ use crate::eval::{eval, Control}; use alloc::rc::Rc; use alloc::vec::Vec; use core::ops::Range; -use primitive_types::U256; +use primitive_types::{H160, U256}; /// Core execution layer for EVM. pub struct Machine { @@ -44,6 +44,24 @@ pub struct Machine { stack: Stack, } +/// EVM interpreter handler. +pub trait InterpreterHandler { + fn before_eval(&mut self); + + fn after_eval(&mut self); + + fn before_bytecode( + &mut self, + opcode: Opcode, + pc: usize, + machine: &Machine, + address: &H160, + ) -> Result<(), ExitError>; + + // Only invoked if #[cfg(feature = "tracing")] + fn after_bytecode(&mut self, result: &Result<(), Capture>, machine: &Machine); +} + impl Machine { /// Reference of machine stack. pub fn stack(&self) -> &Stack { @@ -128,8 +146,10 @@ impl Machine { /// Loop stepping the machine, until it stops. pub fn run(&mut self) -> Capture { + let mut handler = SimpleInterpreterHandler::default(); + let address = H160::default(); loop { - match self.step() { + match self.step(&mut handler, &address) { Ok(()) => (), Err(res) => return res, } @@ -137,36 +157,74 @@ impl Machine { } #[inline] - /// Step the machine, executing one opcode. It then returns. - pub fn step(&mut self) -> Result<(), Capture> { + /// Step the machine, executing until exit or trap. + pub fn step( + &mut self, + handler: &mut H, + address: &H160, + ) -> Result<(), Capture> { let position = *self .position .as_ref() .map_err(|reason| Capture::Exit(reason.clone()))?; - - match self.code.get(position).map(|v| Opcode(*v)) { - Some(opcode) => match eval(self, opcode, position) { - Control::Continue(p) => { - self.position = Ok(position + p); - Ok(()) - } - Control::Exit(e) => { - self.position = Err(e.clone()); - Err(Capture::Exit(e)) - } - Control::Jump(p) => { - self.position = Ok(p); - Ok(()) - } - Control::Trap(opcode) => { - self.position = Ok(position + 1); - Err(Capture::Trap(opcode)) - } - }, - None => { - self.position = Err(ExitSucceed::Stopped.into()); - Err(Capture::Exit(ExitSucceed::Stopped.into())) + match eval(self, position, handler, address) { + Control::Exit(e) => { + self.position = Err(e.clone()); + Err(Capture::Exit(e)) } + Control::Trap(opcode) => Err(Capture::Trap(opcode)), + Control::Continue(_) | Control::Jump(_) => Ok(()), + } + } +} + +pub struct SimpleInterpreterHandler { + pub executed: u64, + pub profile: [u64; 256], + pub address: H160, +} + +impl SimpleInterpreterHandler { + pub fn new(address: H160) -> Self { + Self { + executed: 0, + profile: [0; 256], + address, } } + + pub fn default() -> Self { + Self { + executed: 0, + profile: [0; 256], + address: H160::default(), + } + } +} + +impl InterpreterHandler for SimpleInterpreterHandler { + fn before_eval(&mut self) {} + + fn after_eval(&mut self) {} + + #[inline] + fn before_bytecode( + &mut self, + opcode: Opcode, + _pc: usize, + _machine: &Machine, + _address: &H160, + ) -> Result<(), ExitError> { + self.executed += 1; + self.profile[opcode.as_usize()] += 1; + Ok(()) + } + + #[inline] + fn after_bytecode( + &mut self, + _result: &Result<(), Capture>, + _machine: &Machine, + ) { + } } diff --git a/gasometer/src/consts.rs b/gasometer/src/consts.rs index 285a4c1ac..6fae1f090 100644 --- a/gasometer/src/consts.rs +++ b/gasometer/src/consts.rs @@ -1,21 +1,21 @@ -pub const G_ZERO: u64 = 0; -pub const G_BASE: u64 = 2; -pub const G_VERYLOW: u64 = 3; -pub const G_LOW: u64 = 5; -pub const G_MID: u64 = 8; -pub const G_HIGH: u64 = 10; -pub const G_JUMPDEST: u64 = 1; -pub const R_SUICIDE: i64 = 24000; -pub const G_CREATE: u64 = 32000; -pub const G_CALLVALUE: u64 = 9000; -pub const G_NEWACCOUNT: u64 = 25000; -pub const G_EXP: u64 = 10; -pub const G_MEMORY: u64 = 3; -pub const G_LOG: u64 = 375; -pub const G_LOGDATA: u64 = 8; -pub const G_LOGTOPIC: u64 = 375; -pub const G_SHA3: u64 = 30; -pub const G_SHA3WORD: u64 = 6; -pub const G_COPY: u64 = 3; -pub const G_BLOCKHASH: u64 = 20; -pub const G_CODEDEPOSIT: u64 = 200; +pub const G_ZERO: u32 = 0; +pub const G_BASE: u32 = 2; +pub const G_VERYLOW: u32 = 3; +pub const G_LOW: u32 = 5; +pub const G_MID: u32 = 8; +pub const G_HIGH: u32 = 10; +pub const G_JUMPDEST: u32 = 1; +pub const R_SUICIDE: i32 = 24000; +pub const G_CREATE: u32 = 32000; +pub const G_CALLVALUE: u32 = 9000; +pub const G_NEWACCOUNT: u32 = 25000; +pub const G_EXP: u32 = 10; +pub const G_MEMORY: u32 = 3; +pub const G_LOG: u32 = 375; +pub const G_LOGDATA: u32 = 8; +pub const G_LOGTOPIC: u32 = 375; +pub const G_SHA3: u32 = 30; +pub const G_SHA3WORD: u32 = 6; +pub const G_COPY: u32 = 3; +pub const G_BLOCKHASH: u32 = 20; +pub const G_CODEDEPOSIT: u32 = 200; diff --git a/gasometer/src/costs.rs b/gasometer/src/costs.rs index c25f5db9c..9fc1f40d7 100644 --- a/gasometer/src/costs.rs +++ b/gasometer/src/costs.rs @@ -15,7 +15,7 @@ pub fn suicide_refund(already_removed: bool) -> i64 { if already_removed { 0 } else { - R_SUICIDE + R_SUICIDE as i64 } } @@ -81,7 +81,7 @@ pub fn create2_cost(len: U256) -> Result { pub fn exp_cost(power: U256, config: &Config) -> Result { if power == U256::zero() { - Ok(G_EXP) + Ok(G_EXP as u64) } else { let gas = U256::from(G_EXP) .checked_add( @@ -152,7 +152,7 @@ pub fn log_cost(n: u8, len: U256) -> Result { .ok_or(ExitError::OutOfGas)?, ) .ok_or(ExitError::OutOfGas)? - .checked_add(U256::from(G_LOGTOPIC * n as u64)) + .checked_add(U256::from(G_LOGTOPIC * n as u32)) .ok_or(ExitError::OutOfGas)?; if gas > U256::from(u64::MAX) { @@ -294,7 +294,7 @@ pub fn address_access_cost(is_cold: bool, regular_value: u64, config: &Config) - fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 { if is_call_or_callcode && transfers_value { - G_CALLVALUE + G_CALLVALUE as u64 } else { 0 } @@ -310,12 +310,12 @@ fn new_cost( if is_call_or_staticcall { if eip161 { if transfers_value && new_account { - G_NEWACCOUNT + G_NEWACCOUNT as u64 } else { 0 } } else if new_account { - G_NEWACCOUNT + G_NEWACCOUNT as u64 } else { 0 } diff --git a/gasometer/src/lib.rs b/gasometer/src/lib.rs index da0fcf4ab..0656e07fc 100644 --- a/gasometer/src/lib.rs +++ b/gasometer/src/lib.rs @@ -97,6 +97,12 @@ impl<'config> Gasometer<'config> { self.config } + #[inline] + /// Gas limit. + pub fn gas_limit(&self) -> u64 { + self.gas_limit + } + #[inline] /// Remaining gas. pub fn gas(&self) -> u64 { @@ -163,7 +169,7 @@ impl<'config> Gasometer<'config> { #[inline] /// Record `CREATE` code deposit. pub fn record_deposit(&mut self, len: usize) -> Result<(), ExitError> { - let cost = len as u64 * consts::G_CODEDEPOSIT; + let cost = len as u64 * (consts::G_CODEDEPOSIT as u64); self.record_cost(cost) } @@ -325,8 +331,8 @@ fn count_access_list(access_list: &[(H160, Vec)]) -> (usize, usize) { } #[inline] -pub fn static_opcode_cost(opcode: Opcode) -> Option { - static TABLE: [Option; 256] = { +pub fn static_opcode_cost(opcode: Opcode) -> Option { + static TABLE: [Option; 256] = { let mut table = [None; 256]; table[Opcode::STOP.as_usize()] = Some(consts::G_ZERO); @@ -806,14 +812,14 @@ impl<'config> Inner<'config> { GasCost::Log { n, len } => costs::log_cost(n, len)?, GasCost::VeryLowCopy { len } => costs::verylowcopy_cost(len)?, GasCost::Exp { power } => costs::exp_cost(power, self.config)?, - GasCost::Create => consts::G_CREATE, + GasCost::Create => consts::G_CREATE as u64, GasCost::Create2 { len } => costs::create2_cost(len)?, GasCost::SLoad { target_is_cold } => costs::sload_cost(target_is_cold, self.config), - GasCost::Zero => consts::G_ZERO, - GasCost::Base => consts::G_BASE, - GasCost::VeryLow => consts::G_VERYLOW, - GasCost::Low => consts::G_LOW, + GasCost::Zero => consts::G_ZERO as u64, + GasCost::Base => consts::G_BASE as u64, + GasCost::VeryLow => consts::G_VERYLOW as u64, + GasCost::Low => consts::G_LOW as u64, GasCost::Invalid(opcode) => return Err(ExitError::InvalidCode(opcode)), GasCost::ExtCodeSize { target_is_cold } => { @@ -826,7 +832,7 @@ impl<'config> Inner<'config> { GasCost::Balance { target_is_cold } => { costs::address_access_cost(target_is_cold, self.config.gas_balance, self.config) } - GasCost::BlockHash => consts::G_BLOCKHASH, + GasCost::BlockHash => consts::G_BLOCKHASH as u64, GasCost::ExtCodeHash { target_is_cold } => costs::address_access_cost( target_is_cold, self.config.gas_ext_code_hash, diff --git a/gasometer/src/memory.rs b/gasometer/src/memory.rs index a875b250b..c0afed914 100644 --- a/gasometer/src/memory.rs +++ b/gasometer/src/memory.rs @@ -3,7 +3,7 @@ use evm_core::ExitError; pub fn memory_gas(a: usize) -> Result { let a = a as u64; - G_MEMORY + (G_MEMORY as u64) .checked_mul(a) .ok_or(ExitError::OutOfGas)? .checked_add(a.checked_mul(a).ok_or(ExitError::OutOfGas)? / 512) diff --git a/runtime/src/handler.rs b/runtime/src/handler.rs index 31e894fbe..4e632a54d 100644 --- a/runtime/src/handler.rs +++ b/runtime/src/handler.rs @@ -1,4 +1,4 @@ -use crate::{Capture, Context, CreateScheme, ExitError, ExitReason, Machine, Opcode, Stack}; +use crate::{Capture, Context, CreateScheme, ExitError, ExitReason, Machine, Opcode}; use alloc::vec::Vec; use primitive_types::{H160, H256, U256}; @@ -108,14 +108,6 @@ pub trait Handler { fn call_feedback(&mut self, _feedback: Self::CallFeedback) -> Result<(), ExitError> { Ok(()) } - - /// Pre-validation step for the runtime. - fn pre_validate( - &mut self, - context: &Context, - opcode: Opcode, - stack: &Stack, - ) -> Result<(), ExitError>; /// Handle other unknown external opcodes. fn other(&mut self, opcode: Opcode, _stack: &mut Machine) -> Result<(), ExitError> { Err(ExitError::InvalidCode(opcode)) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 8809e58f3..06a72d822 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -39,39 +39,7 @@ use primitive_types::{H160, U256}; macro_rules! step { ( $self:expr, $handler:expr, $return:tt $($err:path)?; $($ok:path)? ) => ({ - if let Some((opcode, stack)) = $self.machine.inspect() { - event!(Step { - context: &$self.context, - opcode, - position: $self.machine.position(), - stack, - memory: $self.machine.memory() - }); - - match $handler.pre_validate(&$self.context, opcode, stack) { - Ok(()) => (), - Err(e) => { - $self.machine.exit(e.clone().into()); - $self.status = Err(e.into()); - }, - } - } - - match &$self.status { - Ok(()) => (), - Err(e) => { - #[allow(unused_parens)] - $return $($err)*(Capture::Exit(e.clone())) - }, - } - - let result = $self.machine.step(); - - event!(StepResult { - result: &result, - return_value: &$self.machine.return_value(), - }); - + let result = $self.machine.step($handler, &$self.context.address); match result { Ok(()) => $($ok)?(()), Err(Capture::Exit(e)) => { @@ -146,7 +114,7 @@ impl Runtime { } /// Step the runtime. - pub fn step<'a, H: Handler>( + pub fn step<'a, H: Handler + InterpreterHandler>( &'a mut self, handler: &mut H, ) -> Result<(), Capture>> { @@ -154,7 +122,7 @@ impl Runtime { } /// Loop stepping the runtime until it stops. - pub fn run<'a, H: Handler>( + pub fn run<'a, H: Handler + InterpreterHandler>( &'a mut self, handler: &mut H, ) -> Capture> { diff --git a/runtime/src/tracing.rs b/runtime/src/tracing.rs index f9fed1961..908d63f2b 100644 --- a/runtime/src/tracing.rs +++ b/runtime/src/tracing.rs @@ -1,6 +1,6 @@ //! Allows to listen to runtime events. -use crate::{Capture, Context, ExitReason, Memory, Opcode, Stack, Trap}; +use crate::{Capture, ExitReason, Memory, Opcode, Stack, Trap}; use primitive_types::{H160, H256}; environmental::environmental!(listener: dyn EventListener + 'static); @@ -12,7 +12,7 @@ pub trait EventListener { #[derive(Debug, Copy, Clone)] pub enum Event<'a> { Step { - context: &'a Context, + address: H160, opcode: Opcode, position: &'a Result, stack: &'a Stack, @@ -34,8 +34,8 @@ pub enum Event<'a> { }, } -// Expose `listener::with` to the crate only. -pub(crate) fn with(f: F) { +// Expose `listener::with` to allow flexible tracing. +pub fn with(f: F) { listener::with(f); } diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 4d30981d5..92ed1b643 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -6,12 +6,12 @@ use crate::executor::stack::tagged_runtime::{RuntimeKind, TaggedRuntime}; use crate::gasometer::{self, Gasometer, StorageTarget}; use crate::maybe_borrowed::MaybeBorrowed; use crate::{ - Capture, Config, Context, CreateScheme, ExitError, ExitReason, Handler, Opcode, Runtime, Stack, + Capture, Config, Context, CreateScheme, ExitError, ExitReason, Handler, Opcode, Runtime, Transfer, }; use alloc::{collections::BTreeSet, rc::Rc, vec::Vec}; use core::{cmp::min, convert::Infallible}; -use evm_core::ExitFatal; +use evm_core::{ExitFatal, InterpreterHandler, Machine, Trap}; use evm_runtime::Resolve; use primitive_types::{H160, H256, U256}; use sha3::{Digest, Keccak256}; @@ -1014,6 +1014,89 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> } } +impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> InterpreterHandler + for StackExecutor<'config, 'precompiles, S, P> +{ + #[inline] + fn before_eval(&mut self) {} + + #[inline] + fn after_eval(&mut self) {} + + #[inline] + fn before_bytecode( + &mut self, + opcode: Opcode, + _pc: usize, + machine: &Machine, + address: &H160, + ) -> Result<(), ExitError> { + #[cfg(feature = "tracing")] + { + use evm_runtime::tracing::Event::Step; + evm_runtime::tracing::with(|listener| { + listener.event(Step { + address: *address, + opcode, + position: &Ok(_pc), + stack: machine.stack(), + memory: machine.memory(), + }) + }); + } + + if let Some(cost) = gasometer::static_opcode_cost(opcode) { + self.state + .metadata_mut() + .gasometer + .record_cost(cost as u64)?; + } else { + let is_static = self.state.metadata().is_static; + let (gas_cost, target, memory_cost) = gasometer::dynamic_opcode_cost( + *address, + opcode, + machine.stack(), + is_static, + self.config, + self, + )?; + + self.state + .metadata_mut() + .gasometer + .record_dynamic_cost(gas_cost, memory_cost)?; + match target { + StorageTarget::Address(address) => { + self.state.metadata_mut().access_address(address) + } + StorageTarget::Slot(address, key) => { + self.state.metadata_mut().access_storage(address, key) + } + StorageTarget::None => (), + } + } + Ok(()) + } + + #[inline] + fn after_bytecode( + &mut self, + _result: &Result<(), Capture>, + _machine: &Machine, + ) { + #[cfg(feature = "tracing")] + { + use evm_runtime::tracing::Event::StepResult; + evm_runtime::tracing::with(|listener| { + listener.event(StepResult { + result: _result, + return_value: _machine.return_value().as_slice(), + }) + }); + } + } +} + pub struct StackExecutorCallInterrupt<'borrow>(TaggedRuntime<'borrow>); pub struct StackExecutorCreateInterrupt<'borrow>(TaggedRuntime<'borrow>); @@ -1096,9 +1179,11 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler fn gas_price(&self) -> U256 { self.state.gas_price() } + fn origin(&self) -> H160 { self.state.origin() } + fn block_hash(&self, number: U256) -> H256 { self.state.block_hash(number) } @@ -1126,7 +1211,6 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler fn chain_id(&self) -> U256 { self.state.chain_id() } - fn deleted(&self, address: H160) -> bool { self.state.deleted(address) } @@ -1252,45 +1336,6 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler capture } - - #[inline] - fn pre_validate( - &mut self, - context: &Context, - opcode: Opcode, - stack: &Stack, - ) -> Result<(), ExitError> { - // log::trace!(target: "evm", "Running opcode: {:?}, Pre gas-left: {:?}", opcode, gasometer.gas()); - - if let Some(cost) = gasometer::static_opcode_cost(opcode) { - self.state.metadata_mut().gasometer.record_cost(cost)?; - } else { - let is_static = self.state.metadata().is_static; - let (gas_cost, target, memory_cost) = gasometer::dynamic_opcode_cost( - context.address, - opcode, - stack, - is_static, - self.config, - self, - )?; - - let gasometer = &mut self.state.metadata_mut().gasometer; - - gasometer.record_dynamic_cost(gas_cost, memory_cost)?; - match target { - StorageTarget::Address(address) => { - self.state.metadata_mut().access_address(address) - } - StorageTarget::Slot(address, key) => { - self.state.metadata_mut().access_storage(address, key) - } - StorageTarget::None => (), - } - } - - Ok(()) - } } struct StackExecutorHandle<'inner, 'config, 'precompiles, S, P> { From f50ce0455b702caba75d26c5ec782586f6717cb0 Mon Sep 17 00:00:00 2001 From: Michael Birch Date: Thu, 3 Nov 2022 11:35:26 -0500 Subject: [PATCH 04/23] Fix: explicit EVM call stack (#16) --- runtime/src/eval/system.rs | 8 +++----- src/executor/stack/executor.rs | 3 +-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/runtime/src/eval/system.rs b/runtime/src/eval/system.rs index 26f9c8a4d..ca53ad834 100644 --- a/runtime/src/eval/system.rs +++ b/runtime/src/eval/system.rs @@ -310,10 +310,7 @@ pub fn create(runtime: &mut Runtime, is_create2: bool, handler: &mut Err(e) => Control::Exit(e), } } - Capture::Trap(interrupt) => { - push_h256!(runtime, H256::default()); - Control::CreateInterrupt(interrupt) - } + Capture::Trap(interrupt) => Control::CreateInterrupt(interrupt), } } @@ -405,7 +402,8 @@ pub fn call(runtime: &mut Runtime, scheme: CallScheme, handler: &mut } } Capture::Trap(interrupt) => { - push_h256!(runtime, H256::default()); + runtime.return_data_len = out_len; + runtime.return_data_offset = out_offset; Control::CallInterrupt(interrupt) } } diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 92ed1b643..d1fd36a9c 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -920,8 +920,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> return_data: Vec, ) -> (ExitReason, Option, Vec) { fn check_first_byte(config: &Config, code: &[u8]) -> Result<(), ExitError> { - if config.disallow_executable_format && Some(&Opcode::EOFMAGIC.as_u8()) == code.first() - { + if config.disallow_executable_format && Some(&Opcode::EOFMAGIC.as_u8()) == code.get(0) { return Err(ExitError::InvalidCode(Opcode::EOFMAGIC)); } Ok(()) From 82e954e123b51382b10d3fe7e3544a539504173a Mon Sep 17 00:00:00 2001 From: Michael Birch Date: Tue, 15 Nov 2022 14:02:02 +0100 Subject: [PATCH 05/23] Fix: use checked add to avoid integer overflow in gas cost calculation --- gasometer/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gasometer/src/lib.rs b/gasometer/src/lib.rs index 0656e07fc..536383604 100644 --- a/gasometer/src/lib.rs +++ b/gasometer/src/lib.rs @@ -196,7 +196,9 @@ impl<'config> Gasometer<'config> { snapshot: self.snapshot(), }); - let all_gas_cost = memory_gas + used_gas + gas_cost; + let all_gas_cost = memory_gas + .checked_add(used_gas.saturating_add(gas_cost)) + .ok_or(ExitError::OutOfGas)?; if self.gas_limit < all_gas_cost { self.inner = Err(ExitError::OutOfGas); return Err(ExitError::OutOfGas); From 7b51938a865559c4293b4418ef2461813112a46e Mon Sep 17 00:00:00 2001 From: Moritz Date: Thu, 7 Jul 2022 08:37:53 +0200 Subject: [PATCH 06/23] perf: reduce Result checks in Gasometer --- gasometer/src/lib.rs | 50 ++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/gasometer/src/lib.rs b/gasometer/src/lib.rs index 536383604..d673a799b 100644 --- a/gasometer/src/lib.rs +++ b/gasometer/src/lib.rs @@ -54,6 +54,18 @@ pub struct Snapshot { pub refunded_gas: i64, } +#[cfg(feature = "tracing")] +impl Snapshot { + fn new<'config>(gas_limit: u64, inner: &'config Inner<'config>) -> Snapshot { + Snapshot { + gas_limit, + memory_gas: inner.memory_gas, + used_gas: inner.used_gas, + refunded_gas: inner.refunded_gas, + } + } +} + /// EVM gasometer. #[derive(Clone, Debug)] pub struct Gasometer<'config> { @@ -180,20 +192,28 @@ impl<'config> Gasometer<'config> { memory: Option, ) -> Result<(), ExitError> { let gas = self.gas(); + // Extract a mutable reference to `Inner` to avoid checking `Result` + // repeatedly. Tuning performance as this function is on the hot path. + let mut inner_mut = match &mut self.inner { + Ok(inner) => inner, + Err(err) => return Err(err.clone()), + }; let memory_gas = match memory { - Some(memory) => try_or_fail!(self.inner, self.inner_mut()?.memory_gas(memory)), - None => self.inner_mut()?.memory_gas, + Some(memory) => try_or_fail!(self.inner, inner_mut.memory_gas(memory)), + None => inner_mut.memory_gas, }; - let gas_cost = try_or_fail!(self.inner, self.inner_mut()?.gas_cost(cost, gas)); - let gas_refund = self.inner_mut()?.gas_refund(cost); - let used_gas = self.inner_mut()?.used_gas; + let gas_cost = try_or_fail!(self.inner, inner_mut.gas_cost(cost, gas)); + let gas_refund = inner_mut.gas_refund(cost); + let used_gas = inner_mut.used_gas; + #[cfg(feature = "tracing")] + let gas_limit = self.gas_limit; event!(RecordDynamicCost { gas_cost, memory_gas, gas_refund, - snapshot: self.snapshot(), + snapshot: Some(Snapshot::new(gas_limit, inner_mut)), }); let all_gas_cost = memory_gas @@ -205,11 +225,11 @@ impl<'config> Gasometer<'config> { } let after_gas = self.gas_limit - all_gas_cost; - try_or_fail!(self.inner, self.inner_mut()?.extra_check(cost, after_gas)); + try_or_fail!(self.inner, inner_mut.extra_check(cost, after_gas)); - self.inner_mut()?.used_gas += gas_cost; - self.inner_mut()?.memory_gas = memory_gas; - self.inner_mut()?.refunded_gas += gas_refund; + inner_mut.used_gas += gas_cost; + inner_mut.memory_gas = memory_gas; + inner_mut.refunded_gas += gas_refund; Ok(()) } @@ -276,12 +296,10 @@ impl<'config> Gasometer<'config> { #[cfg(feature = "tracing")] pub fn snapshot(&self) -> Option { - self.inner.as_ref().ok().map(|inner| Snapshot { - gas_limit: self.gas_limit, - memory_gas: inner.memory_gas, - used_gas: inner.used_gas, - refunded_gas: inner.refunded_gas, - }) + self.inner + .as_ref() + .ok() + .map(|inner| Snapshot::new(self.gas_limit, inner)) } } From ca0caf9cf5ffa7ef3510bc36f29a1dc7f16e3345 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 18 Feb 2022 15:30:54 +0300 Subject: [PATCH 07/23] Use usize rather than u256 for memory offsets This change improves performance (as measured by the number of WASM instructions executed) by about 5%. The story here is that memory-related operations, notably mload and mstore, are pretty high in profiles. Part of the reason for that is that offsets are represented as U256, and carrying out bounds checking in wide integers is naturally slow! Luckily, we actually know that all actual addresses will fit in usize -- bigger addresses *must* run out of gas. So, in this PR we do two things: * when calculating memory-related gas usage, convert u256 to usize as early as possible, flagging overflows as out of gas errors. * when executing memory-related instructions, *assume* that operands fit usize and just cast them (we use checked cast, so we'll panic if that's not the case. Replacing that with unchecked cast leads to just a tiny further perf improvement). I am not entirely thrilled by this non-local invariant. It'd be much better if gas counting and execution were defined by the same function, so as to make the invariant local. Still, I think the current impl is fine: Even in the original impl, we had a similar implicit invariant (as_usize_or_fail was casting u256 to usize). The official geth impl seems to do roughly the same thing. As far as I understand, they even use unchecked casts! --- core/src/eval/macros.rs | 15 ++++++ core/src/eval/misc.rs | 61 +++++++++++++++-------- core/src/memory.rs | 53 ++++++++------------ core/src/stack.rs | 12 +++++ gasometer/src/lib.rs | 89 +++++++++++++++------------------- runtime/src/eval/macros.rs | 29 +++++------ runtime/src/eval/mod.rs | 6 +-- runtime/src/eval/system.rs | 83 +++++++++++++++++++++---------- runtime/src/lib.rs | 10 ++-- src/executor/stack/executor.rs | 2 +- 10 files changed, 205 insertions(+), 155 deletions(-) diff --git a/core/src/eval/macros.rs b/core/src/eval/macros.rs index 9cd9c30ee..b946d178b 100644 --- a/core/src/eval/macros.rs +++ b/core/src/eval/macros.rs @@ -55,6 +55,21 @@ macro_rules! push_u256 { ) } +/// Pops top element of the stack and converts it to `usize`. +/// +/// The top element **must** be not greater than `usize::MAX`. +/// This non-local invariant is enforced by gas metering infrastructure. +macro_rules! pop_usize { + ( $machine:expr, $( $x:ident ),* ) => ( + $( + let $x = match $machine.stack.pop() { + Ok(value) => value.as_usize(), + Err(e) => return Control::Exit(e.into()), + }; + )* + ); +} + macro_rules! op1_u256_fn { ( $machine:expr, $op:path ) => {{ pop_u256!($machine, op1); diff --git a/core/src/eval/misc.rs b/core/src/eval/misc.rs index 8c28af696..f433f3472 100644 --- a/core/src/eval/misc.rs +++ b/core/src/eval/misc.rs @@ -1,5 +1,5 @@ use super::Control; -use crate::{ExitError, ExitFatal, ExitRevert, ExitSucceed, Machine}; +use crate::{ExitError, ExitRevert, ExitSucceed, Machine}; use core::cmp::min; use primitive_types::{H256, U256}; @@ -12,7 +12,18 @@ pub fn codesize(state: &mut Machine) -> Control { #[inline] pub fn codecopy(state: &mut Machine) -> Control { - pop_u256!(state, memory_offset, code_offset, len); + pop_u256!(state, memory_offset); + pop_u256!(state, code_offset); + pop_usize!(state, len); + + // If `len` is zero then nothing happens, regardless of the + // value of the other parameters. In particular, `memory_offset` + // might be larger than `usize::MAX`, hence why we check this first. + if len == 0 { + return Control::Continue(1); + } + + let memory_offset = memory_offset.as_usize(); try_or_fail!(state.memory.resize_offset(memory_offset, len)); match state @@ -54,12 +65,16 @@ pub fn calldatasize(state: &mut Machine) -> Control { #[inline] pub fn calldatacopy(state: &mut Machine) -> Control { - pop_u256!(state, memory_offset, data_offset, len); + pop_u256!(state, memory_offset); + pop_u256!(state, data_offset); + pop_usize!(state, len); - try_or_fail!(state.memory.resize_offset(memory_offset, len)); - if len == U256::zero() { + // See comment on `codecopy` about the `len == 0` case. + if len == 0 { return Control::Continue(1); } + let memory_offset = memory_offset.as_usize(); + try_or_fail!(state.memory.resize_offset(memory_offset, len)); match state .memory @@ -78,9 +93,8 @@ pub fn pop(state: &mut Machine) -> Control { #[inline] pub fn mload(state: &mut Machine) -> Control { - pop_u256!(state, index); - try_or_fail!(state.memory.resize_offset(index, U256::from(32))); - let index = as_usize_or_fail!(index); + pop_usize!(state, index); + try_or_fail!(state.memory.resize_offset(index, 32)); let value = state.memory.get_h256(index); push_h256!(state, value); Control::Continue(1) @@ -88,10 +102,9 @@ pub fn mload(state: &mut Machine) -> Control { #[inline] pub fn mstore(state: &mut Machine) -> Control { - pop_u256!(state, index); + pop_usize!(state, index); pop_h256!(state, value); - try_or_fail!(state.memory.resize_offset(index, U256::from(32))); - let index = as_usize_or_fail!(index); + try_or_fail!(state.memory.resize_offset(index, 32)); match state.memory.set(index, &value[..], Some(32)) { Ok(()) => Control::Continue(1), Err(e) => Control::Exit(e.into()), @@ -100,9 +113,9 @@ pub fn mstore(state: &mut Machine) -> Control { #[inline] pub fn mstore8(state: &mut Machine) -> Control { - pop_u256!(state, index, value); - try_or_fail!(state.memory.resize_offset(index, U256::one())); - let index = as_usize_or_fail!(index); + pop_usize!(state, index); + pop_u256!(state, value); + try_or_fail!(state.memory.resize_offset(index, 1)); let value = (value.low_u32() & 0xff) as u8; match state.memory.set(index, &[value], Some(1)) { Ok(()) => Control::Continue(1), @@ -147,7 +160,7 @@ pub fn pc(state: &mut Machine, position: usize) -> Control { #[inline] pub fn msize(state: &mut Machine) -> Control { - push_u256!(state, state.memory.effective_len()); + push_u256!(state, state.memory.effective_len().into()); Control::Continue(1) } @@ -223,16 +236,22 @@ pub fn swap(state: &mut Machine, n: usize) -> Control { #[inline] pub fn ret(state: &mut Machine) -> Control { - pop_u256!(state, start, len); - try_or_fail!(state.memory.resize_offset(start, len)); - state.return_range = start..(start + len); + pop_u256!(state, start); + pop_usize!(state, len); + if len > 0 { + try_or_fail!(state.memory.resize_offset(start.as_usize(), len)); + } + state.return_range = start..(start + U256::from(len)); Control::Exit(ExitSucceed::Returned.into()) } #[inline] pub fn revert(state: &mut Machine) -> Control { - pop_u256!(state, start, len); - try_or_fail!(state.memory.resize_offset(start, len)); - state.return_range = start..(start + len); + pop_u256!(state, start); + pop_usize!(state, len); + if len > 0 { + try_or_fail!(state.memory.resize_offset(start.as_usize(), len)); + } + state.return_range = start..(start + U256::from(len)); Control::Exit(ExitRevert::Reverted.into()) } diff --git a/core/src/memory.rs b/core/src/memory.rs index 2f1f96393..ce4484102 100644 --- a/core/src/memory.rs +++ b/core/src/memory.rs @@ -9,7 +9,7 @@ use primitive_types::{H256, U256}; #[derive(Clone, Debug)] pub struct Memory { data: Vec, - effective_len: U256, + effective_len: usize, limit: usize, } @@ -18,7 +18,7 @@ impl Memory { pub fn new(limit: usize) -> Self { Self { data: Vec::new(), - effective_len: U256::zero(), + effective_len: 0, limit, } } @@ -34,7 +34,7 @@ impl Memory { } /// Get the effective length. - pub fn effective_len(&self) -> U256 { + pub fn effective_len(&self) -> usize { self.effective_len } @@ -51,8 +51,8 @@ impl Memory { /// Resize the memory, making it cover the memory region of `offset..(offset /// + len)`, with 32 bytes as the step. If the length is zero, this function /// does nothing. - pub fn resize_offset(&mut self, offset: U256, len: U256) -> Result<(), ExitError> { - if len == U256::zero() { + pub fn resize_offset(&mut self, offset: usize, len: usize) -> Result<(), ExitError> { + if len == 0 { return Ok(()); } @@ -64,7 +64,7 @@ impl Memory { } /// Resize the memory, making it cover to `end`, with 32 bytes as the step. - pub fn resize_end(&mut self, end: U256) -> Result<(), ExitError> { + pub fn resize_end(&mut self, end: usize) -> Result<(), ExitError> { if end > self.effective_len { let new_end = next_multiple_of_32(end).ok_or(ExitError::InvalidRange)?; self.effective_len = new_end; @@ -153,9 +153,9 @@ impl Memory { /// Copy `data` into the memory, of given `len`. pub fn copy_large( &mut self, - memory_offset: U256, + memory_offset: usize, data_offset: U256, - len: U256, + len: usize, data: &[u8], ) -> Result<(), ExitFatal> { // Needed to pass ethereum test defined in @@ -163,23 +163,11 @@ impl Memory { // (regardless of other inputs, a zero-length copy is defined to be a no-op). // TODO: refactor `set` and `copy_large` (see // https://github.com/rust-blockchain/evm/pull/40#discussion_r677180794) - if len.is_zero() { + if len == 0 { return Ok(()); } - let memory_offset = if memory_offset > U256::from(usize::MAX) { - return Err(ExitFatal::NotSupported); - } else { - memory_offset.as_usize() - }; - - let ulen = if len > U256::from(usize::MAX) { - return Err(ExitFatal::NotSupported); - } else { - len.as_usize() - }; - - let data = if let Some(end) = data_offset.checked_add(len) { + let data = if let Some(end) = data_offset.checked_add(len.into()) { if end > U256::from(usize::MAX) { &[] } else { @@ -196,26 +184,26 @@ impl Memory { &[] }; - self.set(memory_offset, data, Some(ulen)) + self.set(memory_offset, data, Some(len)) } } /// Rounds up `x` to the closest multiple of 32. If `x % 32 == 0` then `x` is returned. #[inline] -fn next_multiple_of_32(x: U256) -> Option { - let r = x.low_u32().bitand(31).not().wrapping_add(1).bitand(31); - x.checked_add(r.into()) +fn next_multiple_of_32(x: usize) -> Option { + let r = x.bitand(31).not().wrapping_add(1).bitand(31); + x.checked_add(r) } #[cfg(test)] mod tests { - use super::{next_multiple_of_32, U256}; + use super::next_multiple_of_32; #[test] fn test_next_multiple_of_32() { // next_multiple_of_32 returns x when it is a multiple of 32 for i in 0..32 { - let x = U256::from(i * 32); + let x = i * 32; assert_eq!(Some(x), next_multiple_of_32(x)); } @@ -225,16 +213,13 @@ mod tests { continue; } let next_multiple = x + 32 - (x % 32); - assert_eq!( - Some(U256::from(next_multiple)), - next_multiple_of_32(x.into()) - ); + assert_eq!(Some(next_multiple), next_multiple_of_32(x.into())); } // next_multiple_of_32 returns None when the next multiple of 32 is too big - let last_multiple_of_32 = U256::MAX & !U256::from(31); + let last_multiple_of_32 = usize::MAX & !31; for i in 0..63 { - let x = U256::MAX - U256::from(i); + let x = usize::MAX - i; if x > last_multiple_of_32 { assert_eq!(None, next_multiple_of_32(x)); } else { diff --git a/core/src/stack.rs b/core/src/stack.rs index 830987e11..13e8f4d58 100644 --- a/core/src/stack.rs +++ b/core/src/stack.rs @@ -93,6 +93,18 @@ impl Stack { }) } + #[inline] + /// Peek a value at given index for the stack as usize. + /// + /// If the value is larger than `usize::MAX`, `OutOfGas` error is returned. + pub fn peek_usize(&self, no_from_top: usize) -> Result { + let u = self.peek(no_from_top)?; + if u > usize::MAX.into() { + return Err(ExitError::OutOfGas); + } + Ok(u.as_usize()) + } + #[inline] /// Set a value at given index for the stack, where the top of the /// stack is at index `0`. If the index is too large, diff --git a/gasometer/src/lib.rs b/gasometer/src/lib.rs index d673a799b..4b070d86d 100644 --- a/gasometer/src/lib.rs +++ b/gasometer/src/lib.rs @@ -660,57 +660,33 @@ pub fn dynamic_opcode_cost( | Opcode::LOG1 | Opcode::LOG2 | Opcode::LOG3 - | Opcode::LOG4 => Some(MemoryCost { - offset: stack.peek(0)?, - len: stack.peek(1)?, - }), + | Opcode::LOG4 => Some(peek_memory_cost(stack, 0, 1)?), - Opcode::CODECOPY | Opcode::CALLDATACOPY | Opcode::RETURNDATACOPY => Some(MemoryCost { - offset: stack.peek(0)?, - len: stack.peek(2)?, - }), + Opcode::CODECOPY | Opcode::CALLDATACOPY | Opcode::RETURNDATACOPY => { + Some(peek_memory_cost(stack, 0, 2)?) + } - Opcode::EXTCODECOPY => Some(MemoryCost { - offset: stack.peek(1)?, - len: stack.peek(3)?, - }), + Opcode::EXTCODECOPY => Some(peek_memory_cost(stack, 1, 3)?), Opcode::MLOAD | Opcode::MSTORE => Some(MemoryCost { - offset: stack.peek(0)?, - len: U256::from(32), + offset: stack.peek_usize(0)?, + len: 32, }), Opcode::MSTORE8 => Some(MemoryCost { - offset: stack.peek(0)?, - len: U256::from(1), + offset: stack.peek_usize(0)?, + len: 1, }), - Opcode::CREATE | Opcode::CREATE2 => Some(MemoryCost { - offset: stack.peek(1)?, - len: stack.peek(2)?, - }), + Opcode::CREATE | Opcode::CREATE2 => Some(peek_memory_cost(stack, 1, 2)?), - Opcode::CALL | Opcode::CALLCODE => Some( - MemoryCost { - offset: stack.peek(3)?, - len: stack.peek(4)?, - } - .join(MemoryCost { - offset: stack.peek(5)?, - len: stack.peek(6)?, - }), - ), + Opcode::CALL | Opcode::CALLCODE => { + Some(peek_memory_cost(stack, 3, 4)?.join(peek_memory_cost(stack, 5, 6)?)) + } - Opcode::DELEGATECALL | Opcode::STATICCALL => Some( - MemoryCost { - offset: stack.peek(2)?, - len: stack.peek(3)?, - } - .join(MemoryCost { - offset: stack.peek(4)?, - len: stack.peek(5)?, - }), - ), + Opcode::DELEGATECALL | Opcode::STATICCALL => { + Some(peek_memory_cost(stack, 2, 3)?.join(peek_memory_cost(stack, 4, 5)?)) + } _ => None, }; @@ -718,6 +694,24 @@ pub fn dynamic_opcode_cost( Ok((gas_cost, storage_target, memory_cost)) } +fn peek_memory_cost( + stack: &Stack, + offset_index: usize, + len_index: usize, +) -> Result { + let len = stack.peek_usize(len_index)?; + + if len == 0 { + return Ok(MemoryCost { + offset: usize::MAX, + len, + }); + } + + let offset = stack.peek_usize(offset_index)?; + Ok(MemoryCost { offset, len }) +} + /// Holds the gas consumption for a Gasometer instance. #[derive(Clone, Debug)] struct Inner<'config> { @@ -732,17 +726,12 @@ impl<'config> Inner<'config> { let from = memory.offset; let len = memory.len; - if len == U256::zero() { + if len == 0 { return Ok(self.memory_gas); } let end = from.checked_add(len).ok_or(ExitError::OutOfGas)?; - if end > U256::from(usize::MAX) { - return Err(ExitError::OutOfGas); - } - let end = end.as_usize(); - let rem = end % 32; let new = if rem == 0 { end / 32 } else { end / 32 + 1 }; @@ -1031,9 +1020,9 @@ pub enum StorageTarget { #[derive(Debug, Clone, Copy)] pub struct MemoryCost { /// Affected memory offset. - pub offset: U256, + pub offset: usize, /// Affected length. - pub len: U256, + pub len: usize, } /// Transaction cost. @@ -1068,11 +1057,11 @@ pub enum TransactionCost { impl MemoryCost { /// Join two memory cost together. pub fn join(self, other: MemoryCost) -> MemoryCost { - if self.len == U256::zero() { + if self.len == 0 { return other; } - if other.len == U256::zero() { + if other.len == 0 { return self; } diff --git a/runtime/src/eval/macros.rs b/runtime/src/eval/macros.rs index bace4c90e..cb40f12bf 100644 --- a/runtime/src/eval/macros.rs +++ b/runtime/src/eval/macros.rs @@ -55,20 +55,17 @@ macro_rules! push_u256 { ) } -macro_rules! as_usize_or_fail { - ( $v:expr ) => {{ - if $v > U256::from(usize::MAX) { - return Control::Exit(ExitFatal::NotSupported.into()); - } - - $v.as_usize() - }}; - - ( $v:expr, $reason:expr ) => {{ - if $v > U256::from(usize::MAX) { - return Control::Exit($reason.into()); - } - - $v.as_usize() - }}; +/// Pops top element of the stack and converts it to `usize`. +/// +/// The top element **must** be not greater than `usize::MAX`. +/// This non-local invariant is enforced by gas metering infrastructure. +macro_rules! pop_usize { + ( $machine:expr, $( $x:ident ),* ) => ( + $( + let $x = match $machine.machine.stack_mut().pop() { + Ok(value) => value.as_usize(), + Err(e) => return Control::Exit(e.into()), + }; + )* + ); } diff --git a/runtime/src/eval/mod.rs b/runtime/src/eval/mod.rs index f317d0b61..344596c12 100644 --- a/runtime/src/eval/mod.rs +++ b/runtime/src/eval/mod.rs @@ -97,13 +97,13 @@ pub fn finish_create( pub fn finish_call( runtime: &mut Runtime, - out_len: U256, - out_offset: U256, + out_len: usize, + out_offset: usize, reason: ExitReason, return_data: Vec, ) -> Result<(), ExitReason> { runtime.return_data_buffer = return_data; - let target_len = min(out_len, U256::from(runtime.return_data_buffer.len())); + let target_len = min(out_len, runtime.return_data_buffer.len()); match reason { ExitReason::Succeed(_) => { diff --git a/runtime/src/eval/system.rs b/runtime/src/eval/system.rs index ca53ad834..dffeb79fd 100644 --- a/runtime/src/eval/system.rs +++ b/runtime/src/eval/system.rs @@ -1,22 +1,25 @@ use super::Control; use crate::{ - CallScheme, Capture, Context, CreateScheme, ExitError, ExitFatal, ExitSucceed, Handler, - Runtime, Transfer, + CallScheme, Capture, Context, CreateScheme, ExitError, ExitSucceed, Handler, Runtime, Transfer, }; use alloc::vec::Vec; use primitive_types::{H256, U256}; use sha3::{Digest, Keccak256}; pub fn sha3(runtime: &mut Runtime) -> Control { - pop_u256!(runtime, from, len); + pop_u256!(runtime, from); + pop_usize!(runtime, len); + + let from = if len == 0 { + usize::MAX + } else { + from.as_usize() + }; try_or_fail!(runtime.machine.memory_mut().resize_offset(from, len)); - let data = if len == U256::zero() { + let data = if len == 0 { Vec::new() } else { - let from = as_usize_or_fail!(from); - let len = as_usize_or_fail!(len); - runtime.machine.memory_mut().get(from, len) }; @@ -106,7 +109,15 @@ pub fn extcodehash(runtime: &mut Runtime, handler: &H) -> Control pub fn extcodecopy(runtime: &mut Runtime, handler: &H) -> Control { pop_h256!(runtime, address); - pop_u256!(runtime, memory_offset, code_offset, len); + pop_u256!(runtime, memory_offset); + pop_u256!(runtime, code_offset); + pop_usize!(runtime, len); + + if len == 0 { + return Control::Continue; + } + + let memory_offset = memory_offset.as_usize(); try_or_fail!(runtime .machine @@ -133,14 +144,16 @@ pub fn returndatasize(runtime: &mut Runtime) -> Control { } pub fn returndatacopy(runtime: &mut Runtime) -> Control { - pop_u256!(runtime, memory_offset, data_offset, len); + pop_usize!(runtime, memory_offset); + pop_u256!(runtime, data_offset); + pop_usize!(runtime, len); try_or_fail!(runtime .machine .memory_mut() .resize_offset(memory_offset, len)); if data_offset - .checked_add(len) + .checked_add(len.into()) .map(|l| l > U256::from(runtime.return_data_buffer.len())) .unwrap_or(true) { @@ -235,15 +248,19 @@ pub fn gas(runtime: &mut Runtime, handler: &H) -> Control { } pub fn log(runtime: &mut Runtime, n: u8, handler: &mut H) -> Control { - pop_u256!(runtime, offset, len); + pop_u256!(runtime, offset); + pop_usize!(runtime, len); + + let offset = if len == 0 { + usize::MAX + } else { + offset.as_usize() + }; try_or_fail!(runtime.machine.memory_mut().resize_offset(offset, len)); - let data = if len == U256::zero() { + let data = if len == 0 { Vec::new() } else { - let offset = as_usize_or_fail!(offset); - let len = as_usize_or_fail!(len); - runtime.machine.memory().get(offset, len) }; @@ -277,15 +294,20 @@ pub fn suicide(runtime: &mut Runtime, handler: &mut H) -> Control pub fn create(runtime: &mut Runtime, is_create2: bool, handler: &mut H) -> Control { runtime.return_data_buffer = Vec::new(); - pop_u256!(runtime, value, code_offset, len); + pop_u256!(runtime, value); + pop_u256!(runtime, code_offset); + pop_usize!(runtime, len); + + let code_offset = if len == 0 { + usize::MAX + } else { + code_offset.as_usize() + }; try_or_fail!(runtime.machine.memory_mut().resize_offset(code_offset, len)); - let code = if len == U256::zero() { + let code = if len == 0 { Vec::new() } else { - let code_offset = as_usize_or_fail!(code_offset); - let len = as_usize_or_fail!(len); - runtime.machine.memory().get(code_offset, len) }; @@ -333,7 +355,21 @@ pub fn call(runtime: &mut Runtime, scheme: CallScheme, handler: &mut CallScheme::DelegateCall | CallScheme::StaticCall => U256::zero(), }; - pop_u256!(runtime, in_offset, in_len, out_offset, out_len); + pop_u256!(runtime, in_offset); + pop_usize!(runtime, in_len); + pop_u256!(runtime, out_offset); + pop_usize!(runtime, out_len); + + let in_offset = if in_len == 0 { + usize::MAX + } else { + in_offset.as_usize() + }; + let out_offset = if out_len == 0 { + usize::MAX + } else { + out_offset.as_usize() + }; try_or_fail!(runtime .machine @@ -344,12 +380,9 @@ pub fn call(runtime: &mut Runtime, scheme: CallScheme, handler: &mut .memory_mut() .resize_offset(out_offset, out_len)); - let input = if in_len == U256::zero() { + let input = if in_len == 0 { Vec::new() } else { - let in_offset = as_usize_or_fail!(in_offset); - let in_len = as_usize_or_fail!(in_len); - runtime.machine.memory().get(in_offset, in_len) }; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 06a72d822..5e9292ebb 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -35,7 +35,7 @@ pub use crate::interrupt::{Resolve, ResolveCall, ResolveCreate}; use alloc::rc::Rc; use alloc::vec::Vec; -use primitive_types::{H160, U256}; +use primitive_types::H160; macro_rules! step { ( $self:expr, $handler:expr, $return:tt $($err:path)?; $($ok:path)? ) => ({ @@ -79,8 +79,8 @@ pub struct Runtime { machine: Machine, status: Result<(), ExitReason>, return_data_buffer: Vec, - return_data_len: U256, - return_data_offset: U256, + return_data_len: usize, + return_data_offset: usize, context: Context, } @@ -97,8 +97,8 @@ impl Runtime { machine: Machine::new(code, data, stack_limit, memory_limit), status: Ok(()), return_data_buffer: Vec::new(), - return_data_len: U256::zero(), - return_data_offset: U256::zero(), + return_data_len: 0, + return_data_offset: 0, context, } } diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index d1fd36a9c..11be39e00 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -1378,7 +1378,7 @@ impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Pr // We record the length of the input. let memory_cost = Some(crate::gasometer::MemoryCost { - offset: U256::zero(), + offset: 0, len: input.len().into(), }); From 52ba6a399157043c7dddef4b32076b29c191b76a Mon Sep 17 00:00:00 2001 From: Michael Birch Date: Mon, 5 Dec 2022 11:12:08 -0500 Subject: [PATCH 08/23] Fix(tracing): include missing Exit events (#19) --- src/executor/stack/executor.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 11be39e00..a9df92c08 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -920,7 +920,8 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> return_data: Vec, ) -> (ExitReason, Option, Vec) { fn check_first_byte(config: &Config, code: &[u8]) -> Result<(), ExitError> { - if config.disallow_executable_format && Some(&Opcode::EOFMAGIC.as_u8()) == code.get(0) { + if config.disallow_executable_format && Some(&Opcode::EOFMAGIC.as_u8()) == code.first() + { return Err(ExitError::InvalidCode(Opcode::EOFMAGIC)); } Ok(()) @@ -1379,7 +1380,7 @@ impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Pr // We record the length of the input. let memory_cost = Some(crate::gasometer::MemoryCost { offset: 0, - len: input.len().into(), + len: input.len(), }); if let Err(error) = self From 50a420ae28ec38fb7c02374d025fb2ce368b0996 Mon Sep 17 00:00:00 2001 From: Michael Birch Date: Thu, 15 Dec 2022 02:21:04 -0500 Subject: [PATCH 09/23] Feat(tracing): trace code stored as a result of create (#20) --- src/executor/stack/executor.rs | 4 ++++ src/tracing.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index a9df92c08..cb530c983 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -956,6 +956,10 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> { Ok(()) => { let exit_result = self.exit_substate(StackExitKind::Succeeded); + event!(CreateOutput { + address, + code: &out, + }); self.state.set_code(address, out); if let Err(e) = exit_result { return (e.into(), None, Vec::new()); diff --git a/src/tracing.rs b/src/tracing.rs index d866097b8..b1b818c1d 100644 --- a/src/tracing.rs +++ b/src/tracing.rs @@ -33,6 +33,10 @@ pub enum Event<'a> { target: H160, balance: U256, }, + CreateOutput { + address: H160, + code: &'a [u8], + }, Exit { reason: &'a ExitReason, return_value: &'a [u8], From fcc538cc1f7f91156ef66564a8ac032a82bd4b9e Mon Sep 17 00:00:00 2001 From: mandreyel Date: Wed, 31 May 2023 11:17:00 +0200 Subject: [PATCH 10/23] fix: prevent exiting substate on top level create fns --- src/executor/stack/executor.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index cb530c983..0dc00c2a3 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -435,7 +435,6 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> if let Some(limit) = self.config.max_initcode_size { if init_code.len() > limit { self.state.metadata_mut().gasometer.fail(); - let _ = self.exit_substate(StackExitKind::Failed); return emit_exit!(ExitError::CreateContractLimit.into(), Vec::new()); } } @@ -476,7 +475,6 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> if let Some(limit) = self.config.max_initcode_size { if init_code.len() > limit { self.state.metadata_mut().gasometer.fail(); - let _ = self.exit_substate(StackExitKind::Failed); return emit_exit!(ExitError::CreateContractLimit.into(), Vec::new()); } } From cdff7674c5b14230bd7145e89a23dfeba612e2b8 Mon Sep 17 00:00:00 2001 From: mandreyel Date: Wed, 21 Jun 2023 15:25:09 +0200 Subject: [PATCH 11/23] fix: verify nonce max val as first thing in call/create --- src/executor/stack/executor.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 0dc00c2a3..2b6fb73f9 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -424,6 +424,10 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> gas_limit: u64, access_list: Vec<(H160, Vec)>, // See EIP-2930 ) -> (ExitReason, Vec) { + if self.nonce(caller) >= U256::from(u64::MAX) { + return (ExitError::MaxNonce.into(), Vec::new()); + } + event!(TransactCreate { caller, value, @@ -543,6 +547,10 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> gas_limit, }); + if self.nonce(caller) >= U256::from(u64::MAX) { + return (ExitError::MaxNonce.into(), Vec::new()); + } + let transaction_cost = gasometer::call_transaction_cost(&data, &access_list); let gasometer = &mut self.state.metadata_mut().gasometer; match gasometer.record_transaction(transaction_cost) { @@ -665,6 +673,10 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> target_gas: Option, take_l64: bool, ) -> Capture<(ExitReason, Option, Vec), StackExecutorCreateInterrupt<'static>> { + if self.nonce(caller) >= U256::from(u64::MAX) { + return Capture::Exit((ExitError::MaxNonce.into(), None, Vec::new())); + } + macro_rules! try_or_fail { ( $e:expr ) => { match $e { @@ -702,10 +714,6 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> return Capture::Exit((ExitError::OutOfFund.into(), None, Vec::new())); } - if let Err(e) = self.state.inc_nonce(caller) { - return Capture::Exit((e.into(), None, Vec::new())); - } - let after_gas = if take_l64 && self.config.call_l64_after_gas { if self.config.estimate { let initial_after_gas = self.state.metadata().gasometer.gas(); @@ -724,6 +732,10 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> let gas_limit = min(after_gas, target_gas); try_or_fail!(self.state.metadata_mut().gasometer.record_cost(gas_limit)); + if let Err(e) = self.state.inc_nonce(caller) { + return Capture::Exit((e.into(), None, Vec::new())); + } + self.enter_substate(gas_limit, false); { From 07d4583383c9088d78cda8aba8e8910cba1b8251 Mon Sep 17 00:00:00 2001 From: mandreyel Date: Tue, 25 Jul 2023 17:39:45 +0200 Subject: [PATCH 12/23] fix: push0 should move program counter by 1 instead of 2 --- core/src/eval/misc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/eval/misc.rs b/core/src/eval/misc.rs index f433f3472..fbda19796 100644 --- a/core/src/eval/misc.rs +++ b/core/src/eval/misc.rs @@ -181,7 +181,7 @@ pub fn push0(state: &mut Machine) -> Control { let val = U256::zero(); push_u256!(state, val); - Control::Continue(2) + Control::Continue(1) } #[inline] From f981a072ab7b0690dbb4575251cc4bd8c5f4998b Mon Sep 17 00:00:00 2001 From: Michael Birch Date: Mon, 21 Aug 2023 08:55:59 -0400 Subject: [PATCH 13/23] Fix: if len==0 then memory_offset allowed to be larger than usize::MAX in returndatacopy (#26) --- runtime/src/eval/system.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/runtime/src/eval/system.rs b/runtime/src/eval/system.rs index dffeb79fd..ae5bd3abc 100644 --- a/runtime/src/eval/system.rs +++ b/runtime/src/eval/system.rs @@ -144,10 +144,23 @@ pub fn returndatasize(runtime: &mut Runtime) -> Control { } pub fn returndatacopy(runtime: &mut Runtime) -> Control { - pop_usize!(runtime, memory_offset); + pop_u256!(runtime, memory_offset); pop_u256!(runtime, data_offset); pop_usize!(runtime, len); + // If `len` is zero then nothing happens, regardless of the + // value of the other parameters. In particular, `memory_offset` + // might be larger than `usize::MAX`, hence why we check this first. + if len == 0 { + return Control::Continue; + } + + // SAFETY: this cast is safe because if `len > 0` then gas cost of memory + // would have already been taken into account at this point. It is impossible + // to have a memory offset greater than `usize::MAX` for any gas limit less + // than `u64::MAX` (and gas limits higher than this are disallowed in general). + let memory_offset = memory_offset.as_usize(); + try_or_fail!(runtime .machine .memory_mut() From 4e2d27d8ff8e5a50a43ed49d86282decce9d39f9 Mon Sep 17 00:00:00 2001 From: Guido Vranken Date: Mon, 21 Aug 2023 17:21:49 +0200 Subject: [PATCH 14/23] Improve performance of Memory get() (#27) --- core/src/memory.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/core/src/memory.rs b/core/src/memory.rs index ce4484102..bb9495285 100644 --- a/core/src/memory.rs +++ b/core/src/memory.rs @@ -79,20 +79,18 @@ impl Memory { /// /// Value of `size` is considered trusted. If they're too large, /// the program can run out of memory, or it can overflow. - pub fn get(&self, offset: usize, size: usize) -> Vec { - let mut ret = Vec::new(); - ret.resize(size, 0); - - #[allow(clippy::needless_range_loop)] - for index in 0..size { - let position = offset + index; - if position >= self.data.len() { - break; - } + pub fn get(&self, mut offset: usize, size: usize) -> Vec { + if offset > self.data.len() { + offset = self.data.len(); + } - ret[index] = self.data[position]; + let mut end = offset + size; + if end > self.data.len() { + end = self.data.len(); } + let mut ret = self.data[offset..end].to_vec(); + ret.resize(size, 0); ret } From 5f74467fdf236428deff48db42ed76a51608d9aa Mon Sep 17 00:00:00 2001 From: Oleksandr Anyshchenko Date: Fri, 8 Sep 2023 10:11:16 +0100 Subject: [PATCH 15/23] feat: add transact_create_fixed function (#29) --- Cargo.toml | 3 ++- src/executor/stack/executor.rs | 42 ++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3d43f3849..6cec2db03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ evm-gasometer = { version = "0.39", path = "gasometer", default-features = false evm-runtime = { version = "0.39", path = "runtime", default-features = false } [dev-dependencies] -criterion = "0.4" +criterion = "0.5" hex = "0.4" [[bench]] @@ -70,6 +70,7 @@ tracing = [ "evm-gasometer/tracing", "evm-runtime/tracing", ] +create-fixed = [] [workspace] members = [ diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 2b6fb73f9..1babbfbab 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -466,6 +466,48 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> } } + /// Same as `CREATE` but uses a specified address for created smart contract. + #[cfg(feature = "create-fixed")] + pub fn transact_create_fixed( + &mut self, + caller: H160, + address: H160, + value: U256, + init_code: Vec, + gas_limit: u64, + access_list: Vec<(H160, Vec)>, // See EIP-2930 + ) -> (ExitReason, Vec) { + event!(TransactCreate { + caller, + value, + init_code: &init_code, + gas_limit, + address: self.create_address(CreateScheme::Fixed(address)), + }); + + if let Err(e) = self.record_create_transaction_cost(&init_code, &access_list) { + return emit_exit!(e.into(), Vec::new()); + } + self.initialize_with_access_list(access_list); + + match self.create_inner( + caller, + CreateScheme::Fixed(address), + value, + init_code, + Some(gas_limit), + false, + ) { + Capture::Exit((s, _, v)) => emit_exit!(s, v), + Capture::Trap(rt) => { + let mut cs = Vec::with_capacity(DEFAULT_CALL_STACK_CAPACITY); + cs.push(rt.0); + let (s, _, v) = self.execute_with_call_stack(&mut cs); + emit_exit!(s, v) + } + } + } + /// Execute a `CREATE2` transaction. pub fn transact_create2( &mut self, From df6f49423f889b8d9891dab5b9e85220eb245983 Mon Sep 17 00:00:00 2001 From: Oleksandr Anyshchenko Date: Tue, 12 Sep 2023 09:15:12 +0100 Subject: [PATCH 16/23] chore: apply suggestions by clippy (#28) --- .github/workflows/rust.yml | 3 +- core/src/error.rs | 12 ++++--- core/src/eval/misc.rs | 12 +++---- core/src/eval/mod.rs | 11 +++--- core/src/lib.rs | 30 ++++++++-------- core/src/memory.rs | 60 +++++++++++++++++--------------- core/src/opcode.rs | 5 +++ core/src/stack.rs | 21 ++++++----- core/src/utils.rs | 56 ++++++++++++++--------------- core/src/valids.rs | 6 +++- gasometer/src/costs.rs | 10 +++--- gasometer/src/lib.rs | 26 ++++++-------- runtime/src/lib.rs | 36 +++++++++---------- src/backend/memory.rs | 4 +-- src/executor/stack/executor.rs | 26 +++++++------- src/executor/stack/memory.rs | 21 +++++------ src/executor/stack/precompile.rs | 4 +-- 17 files changed, 179 insertions(+), 164 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a3afec529..9f339779a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -4,7 +4,6 @@ on: push: branches: [ master ] pull_request: - branches: [ master ] env: CARGO_TERM_COLOR: always @@ -17,7 +16,7 @@ jobs: - name: Rustfmt run: cargo fmt --all -- --check - name: Clippy - run: cargo clippy --all + run: cargo clippy --workspace --all-targets -- -D clippy::all -D clippy::nursery build: runs-on: ubuntu-latest steps: diff --git a/core/src/error.rs b/core/src/error.rs index 9c6dd34e2..4cace11a9 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -35,22 +35,26 @@ pub enum ExitReason { impl ExitReason { /// Whether the exit is succeeded. - pub fn is_succeed(&self) -> bool { + #[must_use] + pub const fn is_succeed(&self) -> bool { matches!(self, Self::Succeed(_)) } /// Whether the exit is error. - pub fn is_error(&self) -> bool { + #[must_use] + pub const fn is_error(&self) -> bool { matches!(self, Self::Error(_)) } /// Whether the exit is revert. - pub fn is_revert(&self) -> bool { + #[must_use] + pub const fn is_revert(&self) -> bool { matches!(self, Self::Revert(_)) } /// Whether the exit is fatal. - pub fn is_fatal(&self) -> bool { + #[must_use] + pub const fn is_fatal(&self) -> bool { matches!(self, Self::Fatal(_)) } } diff --git a/core/src/eval/misc.rs b/core/src/eval/misc.rs index fbda19796..f796a0c35 100644 --- a/core/src/eval/misc.rs +++ b/core/src/eval/misc.rs @@ -140,15 +140,15 @@ pub fn jumpi(state: &mut Machine) -> Control { pop_u256!(state, dest); pop_u256!(state, value); - if value != U256::zero() { + if value == U256::zero() { + Control::Continue(1) + } else { let dest = as_usize_or_fail!(dest, ExitError::InvalidJump); if state.valids.is_valid(dest) { Control::Jump(dest) } else { Control::Exit(ExitError::InvalidJump.into()) } - } else { - Control::Continue(1) } } @@ -186,7 +186,7 @@ pub fn push0(state: &mut Machine) -> Control { #[inline] pub fn push1(state: &mut Machine, position: usize) -> Control { - let b0 = *state.code.get(position + 1).unwrap_or(&0) as u64; + let b0 = u64::from(*state.code.get(position + 1).unwrap_or(&0)); let val = U256::from(b0); push_u256!(state, val); @@ -195,8 +195,8 @@ pub fn push1(state: &mut Machine, position: usize) -> Control { #[inline] pub fn push2(state: &mut Machine, position: usize) -> Control { - let b0 = *state.code.get(position + 1).unwrap_or(&0) as u64; - let b1 = *state.code.get(position + 2).unwrap_or(&0) as u64; + let b0 = u64::from(*state.code.get(position + 1).unwrap_or(&0)); + let b1 = u64::from(*state.code.get(position + 2).unwrap_or(&0)); let val = U256::from((b0 << 8) | b1); push_u256!(state, val); diff --git a/core/src/eval/mod.rs b/core/src/eval/mod.rs index 2c0fcb4b3..f530f3dec 100644 --- a/core/src/eval/mod.rs +++ b/core/src/eval/mod.rs @@ -287,12 +287,11 @@ fn eval_table( let mut pc = position; handler.before_eval(); loop { - let op = match state.code.get(pc) { - Some(v) => Opcode(*v), - None => { - state.position = Err(ExitSucceed::Stopped.into()); - return Control::Exit(ExitSucceed::Stopped.into()); - } + let op = if let Some(v) = state.code.get(pc) { + Opcode(*v) + } else { + state.position = Err(ExitSucceed::Stopped.into()); + return Control::Exit(ExitSucceed::Stopped.into()); }; match handler.before_bytecode(op, pc, state, address) { Ok(()) => (), diff --git a/core/src/lib.rs b/core/src/lib.rs index 09dc4fdc7..49c798ec3 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -64,7 +64,8 @@ pub trait InterpreterHandler { impl Machine { /// Reference of machine stack. - pub fn stack(&self) -> &Stack { + #[must_use] + pub const fn stack(&self) -> &Stack { &self.stack } /// Mutable reference of machine stack. @@ -72,7 +73,8 @@ impl Machine { &mut self.stack } /// Reference of machine memory. - pub fn memory(&self) -> &Memory { + #[must_use] + pub const fn memory(&self) -> &Memory { &self.memory } /// Mutable reference of machine memory. @@ -80,11 +82,12 @@ impl Machine { &mut self.memory } /// Return a reference of the program counter. - pub fn position(&self) -> &Result { + pub const fn position(&self) -> &Result { &self.position } /// Create a new machine with given code and data. + #[must_use] pub fn new( code: Rc>, data: Rc>, @@ -110,23 +113,17 @@ impl Machine { } /// Inspect the machine's next opcode and current stack. + #[must_use] pub fn inspect(&self) -> Option<(Opcode, &Stack)> { - let position = match self.position { - Ok(position) => position, - Err(_) => return None, - }; + let Ok(position) = self.position else { return None }; self.code.get(position).map(|v| (Opcode(*v), &self.stack)) } /// Copy and get the return value of the machine, if any. + #[must_use] pub fn return_value(&self) -> Vec { if self.return_range.start > U256::from(usize::MAX) { - let mut ret = Vec::new(); - ret.resize( - (self.return_range.end - self.return_range.start).as_usize(), - 0, - ); - ret + vec![0; (self.return_range.end - self.return_range.start).as_usize()] } else if self.return_range.end > U256::from(usize::MAX) { let mut ret = self.memory.get( self.return_range.start.as_usize(), @@ -185,15 +182,18 @@ pub struct SimpleInterpreterHandler { } impl SimpleInterpreterHandler { - pub fn new(address: H160) -> Self { + #[must_use] + pub const fn new(address: H160) -> Self { Self { executed: 0, profile: [0; 256], address, } } +} - pub fn default() -> Self { +impl Default for SimpleInterpreterHandler { + fn default() -> Self { Self { executed: 0, profile: [0; 256], diff --git a/core/src/memory.rs b/core/src/memory.rs index bb9495285..a2f24e8d2 100644 --- a/core/src/memory.rs +++ b/core/src/memory.rs @@ -15,7 +15,8 @@ pub struct Memory { impl Memory { /// Create a new memory with the given limit. - pub fn new(limit: usize) -> Self { + #[must_use] + pub const fn new(limit: usize) -> Self { Self { data: Vec::new(), effective_len: 0, @@ -24,43 +25,45 @@ impl Memory { } /// Memory limit. - pub fn limit(&self) -> usize { + #[must_use] + pub const fn limit(&self) -> usize { self.limit } /// Get the length of the current memory range. + #[must_use] pub fn len(&self) -> usize { self.data.len() } /// Get the effective length. - pub fn effective_len(&self) -> usize { + #[must_use] + pub const fn effective_len(&self) -> usize { self.effective_len } /// Return true if current effective memory range is zero. + #[must_use] pub fn is_empty(&self) -> bool { self.len() == 0 } /// Return the full memory. - pub fn data(&self) -> &Vec { + #[must_use] + pub const fn data(&self) -> &Vec { &self.data } - /// Resize the memory, making it cover the memory region of `offset..(offset - /// + len)`, with 32 bytes as the step. If the length is zero, this function - /// does nothing. + /// Resize the memory, making it cover the memory region of `offset..offset + len`, + /// with 32 bytes as the step. If the length is zero, this function does nothing. pub fn resize_offset(&mut self, offset: usize, len: usize) -> Result<(), ExitError> { if len == 0 { return Ok(()); } - if let Some(end) = offset.checked_add(len) { - self.resize_end(end) - } else { - Err(ExitError::InvalidRange) - } + offset + .checked_add(len) + .map_or(Err(ExitError::InvalidRange), |end| self.resize_end(end)) } /// Resize the memory, making it cover to `end`, with 32 bytes as the step. @@ -79,6 +82,7 @@ impl Memory { /// /// Value of `size` is considered trusted. If they're too large, /// the program can run out of memory, or it can overflow. + #[must_use] pub fn get(&self, mut offset: usize, size: usize) -> Vec { if offset > self.data.len() { offset = self.data.len(); @@ -95,6 +99,7 @@ impl Memory { } /// Get `H256` from a specific offset in memory. + #[must_use] pub fn get_h256(&self, offset: usize) -> H256 { let mut ret = [0; 32]; @@ -126,8 +131,7 @@ impl Memory { if offset .checked_add(target_size) - .map(|pos| pos > self.limit) - .unwrap_or(true) + .map_or(true, |pos| pos > self.limit) { return Err(ExitFatal::NotSupported); } @@ -165,22 +169,22 @@ impl Memory { return Ok(()); } - let data = if let Some(end) = data_offset.checked_add(len.into()) { - if end > U256::from(usize::MAX) { - &[] - } else { - let data_offset = data_offset.as_usize(); - let end = end.as_usize(); - - if data_offset > data.len() { + let data = data_offset + .checked_add(len.into()) + .map_or(&[] as &[u8], |end| { + if end > U256::from(usize::MAX) { &[] } else { - &data[data_offset..min(end, data.len())] + let data_offset = data_offset.as_usize(); + let end = end.as_usize(); + + if data_offset > data.len() { + &[] + } else { + &data[data_offset..min(end, data.len())] + } } - } - } else { - &[] - }; + }); self.set(memory_offset, data, Some(len)) } @@ -211,7 +215,7 @@ mod tests { continue; } let next_multiple = x + 32 - (x % 32); - assert_eq!(Some(next_multiple), next_multiple_of_32(x.into())); + assert_eq!(Some(next_multiple), next_multiple_of_32(x)); } // next_multiple_of_32 returns None when the next multiple of 32 is too big diff --git a/core/src/opcode.rs b/core/src/opcode.rs index 15814b0ac..57f9001ab 100644 --- a/core/src/opcode.rs +++ b/core/src/opcode.rs @@ -8,6 +8,7 @@ pub struct Opcode(pub u8); // Core opcodes. +#[allow(clippy::use_self)] impl Opcode { /// `STOP` pub const STOP: Opcode = Opcode(0x00); @@ -178,6 +179,7 @@ impl Opcode { } // External opcodes +#[allow(clippy::use_self)] impl Opcode { /// `SHA3` pub const SHA3: Opcode = Opcode(0x20); @@ -251,6 +253,7 @@ impl Opcode { impl Opcode { /// Whether the opcode is a push opcode. + #[must_use] pub fn is_push(&self) -> Option { let value = self.0; if (0x60..=0x7f).contains(&value) { @@ -261,11 +264,13 @@ impl Opcode { } #[inline] + #[must_use] pub const fn as_u8(&self) -> u8 { self.0 } #[inline] + #[must_use] pub const fn as_usize(&self) -> usize { self.0 as usize } diff --git a/core/src/stack.rs b/core/src/stack.rs index 13e8f4d58..13cd4724d 100644 --- a/core/src/stack.rs +++ b/core/src/stack.rs @@ -11,40 +11,45 @@ pub struct Stack { impl Stack { /// Create a new stack with given limit. - pub fn new(limit: usize) -> Self { + #[must_use] + pub const fn new(limit: usize) -> Self { Self { data: Vec::new(), limit, } } - #[inline] /// Stack limit. - pub fn limit(&self) -> usize { + #[inline] + #[must_use] + pub const fn limit(&self) -> usize { self.limit } - #[inline] /// Stack length. + #[inline] + #[must_use] pub fn len(&self) -> usize { self.data.len() } - #[inline] /// Whether the stack is empty. + #[inline] + #[must_use] pub fn is_empty(&self) -> bool { self.data.is_empty() } - #[inline] /// Stack data. - pub fn data(&self) -> &Vec { + #[inline] + #[must_use] + pub const fn data(&self) -> &Vec { &self.data } - #[inline] /// Pop a value from the stack. If the stack is already empty, returns the /// `StackUnderflow` error. + #[inline] pub fn pop(&mut self) -> Result { self.data.pop().ok_or(ExitError::StackUnderflow) } diff --git a/core/src/utils.rs b/core/src/utils.rs index 7dc2bd5ce..44aaa4a11 100644 --- a/core/src/utils.rs +++ b/core/src/utils.rs @@ -21,17 +21,17 @@ pub struct I256(pub Sign, pub U256); impl I256 { /// Zero value of I256. - pub fn zero() -> I256 { - I256(Sign::Zero, U256::zero()) + pub const fn zero() -> Self { + Self(Sign::Zero, U256::zero()) } /// Minimum value of I256. - pub fn min_value() -> I256 { - I256(Sign::Minus, (U256::MAX & SIGN_BIT_MASK) + U256::from(1u64)) + pub fn min_value() -> Self { + Self(Sign::Minus, (U256::MAX & SIGN_BIT_MASK) + U256::from(1u64)) } } impl Ord for I256 { - fn cmp(&self, other: &I256) -> Ordering { + fn cmp(&self, other: &Self) -> Ordering { match (self.0, other.0) { (Sign::Zero, Sign::Zero) => Ordering::Equal, (Sign::Zero, Sign::Plus) => Ordering::Less, @@ -47,58 +47,58 @@ impl Ord for I256 { } impl PartialOrd for I256 { - fn partial_cmp(&self, other: &I256) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Default for I256 { - fn default() -> I256 { - I256::zero() + fn default() -> Self { + Self::zero() } } impl From for I256 { - fn from(val: U256) -> I256 { + fn from(val: U256) -> Self { if val == U256::zero() { - I256::zero() + Self::zero() } else if val & SIGN_BIT_MASK == val { - I256(Sign::Plus, val) + Self(Sign::Plus, val) } else { - I256(Sign::Minus, !val + U256::from(1u64)) + Self(Sign::Minus, !val + U256::from(1u64)) } } } impl From for U256 { - fn from(value: I256) -> U256 { + fn from(value: I256) -> Self { let sign = value.0; if sign == Sign::Zero { - U256::zero() + Self::zero() } else if sign == Sign::Plus { value.1 } else { - !value.1 + U256::from(1u64) + !value.1 + Self::from(1u64) } } } impl Div for I256 { - type Output = I256; + type Output = Self; - fn div(self, other: I256) -> I256 { - if other == I256::zero() { - return I256::zero(); + fn div(self, other: Self) -> Self { + if other == Self::zero() { + return Self::zero(); } - if self == I256::min_value() && other.1 == U256::from(1u64) { - return I256::min_value(); + if self == Self::min_value() && other.1 == U256::from(1u64) { + return Self::min_value(); } let d = (self.1 / other.1) & SIGN_BIT_MASK; if d == U256::zero() { - return I256::zero(); + return Self::zero(); } match (self.0, other.0) { @@ -106,26 +106,26 @@ impl Div for I256 { | (Sign::Plus, Sign::Zero) | (Sign::Zero, Sign::Zero) | (Sign::Plus, Sign::Plus) - | (Sign::Minus, Sign::Minus) => I256(Sign::Plus, d), + | (Sign::Minus, Sign::Minus) => Self(Sign::Plus, d), (Sign::Zero, Sign::Minus) | (Sign::Plus, Sign::Minus) | (Sign::Minus, Sign::Zero) - | (Sign::Minus, Sign::Plus) => I256(Sign::Minus, d), + | (Sign::Minus, Sign::Plus) => Self(Sign::Minus, d), } } } impl Rem for I256 { - type Output = I256; + type Output = Self; - fn rem(self, other: I256) -> I256 { + fn rem(self, other: Self) -> Self { let r = (self.1 % other.1) & SIGN_BIT_MASK; if r == U256::zero() { - return I256::zero(); + return Self::zero(); } - I256(self.0, r) + Self(self.0, r) } } diff --git a/core/src/valids.rs b/core/src/valids.rs index 22a082104..b7674d7d7 100644 --- a/core/src/valids.rs +++ b/core/src/valids.rs @@ -7,6 +7,7 @@ pub struct Valids(Vec); impl Valids { /// Create a new valid mapping from given code bytes. + #[must_use] pub fn new(code: &[u8]) -> Self { let mut valids: Vec = Vec::with_capacity(code.len()); valids.resize(code.len(), false); @@ -24,24 +25,27 @@ impl Valids { } } - Valids(valids) + Self(valids) } /// Get the length of the valid mapping. This is the same as the /// code bytes. #[inline] + #[must_use] pub fn len(&self) -> usize { self.0.len() } /// Returns true if the valids list is empty #[inline] + #[must_use] pub fn is_empty(&self) -> bool { self.len() == 0 } /// Returns `true` if the position is a valid jump destination. If /// not, returns `false`. + #[must_use] pub fn is_valid(&self, position: usize) -> bool { if position >= self.0.len() { return false; diff --git a/gasometer/src/costs.rs b/gasometer/src/costs.rs index 9fc1f40d7..57b9861b0 100644 --- a/gasometer/src/costs.rs +++ b/gasometer/src/costs.rs @@ -11,7 +11,7 @@ pub fn call_extra_check(gas: U256, after_gas: u64, config: &Config) -> Result<() } } -pub fn suicide_refund(already_removed: bool) -> i64 { +pub const fn suicide_refund(already_removed: bool) -> i64 { if already_removed { 0 } else { @@ -185,7 +185,7 @@ pub fn sha3_cost(len: U256) -> Result { Ok(gas.as_u64()) } -pub fn sload_cost(is_cold: bool, config: &Config) -> u64 { +pub const fn sload_cost(is_cold: bool, config: &Config) -> u64 { if config.increase_state_access_gas { if is_cold { config.gas_sload_cold @@ -280,7 +280,7 @@ pub fn call_cost( + new_cost(is_call_or_staticcall, new_account, transfers_value, config) } -pub fn address_access_cost(is_cold: bool, regular_value: u64, config: &Config) -> u64 { +pub const fn address_access_cost(is_cold: bool, regular_value: u64, config: &Config) -> u64 { if config.increase_state_access_gas { if is_cold { config.gas_account_access_cold @@ -292,7 +292,7 @@ pub fn address_access_cost(is_cold: bool, regular_value: u64, config: &Config) - } } -fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 { +const fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 { if is_call_or_callcode && transfers_value { G_CALLVALUE as u64 } else { @@ -300,7 +300,7 @@ fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 { } } -fn new_cost( +const fn new_cost( is_call_or_staticcall: bool, new_account: bool, transfers_value: bool, diff --git a/gasometer/src/lib.rs b/gasometer/src/lib.rs index 4b070d86d..2d94eb9ba 100644 --- a/gasometer/src/lib.rs +++ b/gasometer/src/lib.rs @@ -76,7 +76,7 @@ pub struct Gasometer<'config> { impl<'config> Gasometer<'config> { /// Create a new gasometer with given gas limit and config. - pub fn new(gas_limit: u64, config: &'config Config) -> Self { + pub const fn new(gas_limit: u64, config: &'config Config) -> Self { Self { gas_limit, config, @@ -105,28 +105,27 @@ impl<'config> Gasometer<'config> { #[inline] /// Reference of the config. - pub fn config(&self) -> &'config Config { + pub const fn config(&self) -> &'config Config { self.config } #[inline] /// Gas limit. - pub fn gas_limit(&self) -> u64 { + pub const fn gas_limit(&self) -> u64 { self.gas_limit } #[inline] /// Remaining gas. pub fn gas(&self) -> u64 { - match self.inner.as_ref() { - Ok(inner) => self.gas_limit - inner.used_gas - inner.memory_gas, - Err(_) => 0, - } + self.inner.as_ref().map_or(0, |inner| { + self.gas_limit - inner.used_gas - inner.memory_gas + }) } #[inline] /// Total used gas. - pub fn total_used_gas(&self) -> u64 { + pub const fn total_used_gas(&self) -> u64 { match self.inner.as_ref() { Ok(inner) => inner.used_gas + inner.memory_gas, Err(_) => self.gas_limit, @@ -136,10 +135,7 @@ impl<'config> Gasometer<'config> { #[inline] /// Refunded gas. pub fn refunded_gas(&self) -> i64 { - match self.inner.as_ref() { - Ok(inner) => inner.refunded_gas, - Err(_) => 0, - } + self.inner.as_ref().map_or(0, |inner| inner.refunded_gas) } /// Explicitly fail the gasometer with out of gas. Return `OutOfGas` error. @@ -194,7 +190,7 @@ impl<'config> Gasometer<'config> { let gas = self.gas(); // Extract a mutable reference to `Inner` to avoid checking `Result` // repeatedly. Tuning performance as this function is on the hot path. - let mut inner_mut = match &mut self.inner { + let inner_mut = match &mut self.inner { Ok(inner) => inner, Err(err) => return Err(err.clone()), }; @@ -335,7 +331,7 @@ pub fn create_transaction_cost(data: &[u8], access_list: &[(H160, Vec)]) - } } -pub fn init_code_cost(data: &[u8]) -> u64 { +pub const fn init_code_cost(data: &[u8]) -> u64 { // As per EIP-3860: // > We define initcode_cost(initcode) to equal INITCODE_WORD_COST * ceil(len(initcode) / 32). // where INITCODE_WORD_COST is 2. @@ -1056,7 +1052,7 @@ pub enum TransactionCost { impl MemoryCost { /// Join two memory cost together. - pub fn join(self, other: MemoryCost) -> MemoryCost { + pub const fn join(self, other: Self) -> Self { if self.len == 0 { return other; } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5e9292ebb..cff10c61c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -104,28 +104,28 @@ impl Runtime { } /// Get a reference to the machine. - pub fn machine(&self) -> &Machine { + pub const fn machine(&self) -> &Machine { &self.machine } /// Get a reference to the execution context. - pub fn context(&self) -> &Context { + pub const fn context(&self) -> &Context { &self.context } /// Step the runtime. - pub fn step<'a, H: Handler + InterpreterHandler>( - &'a mut self, + pub fn step( + &mut self, handler: &mut H, - ) -> Result<(), Capture>> { + ) -> Result<(), Capture>> { step!(self, handler, return Err; Ok) } /// Loop stepping the runtime until it stops. - pub fn run<'a, H: Handler + InterpreterHandler>( - &'a mut self, + pub fn run( + &mut self, handler: &mut H, - ) -> Capture> { + ) -> Capture> { loop { step!(self, handler, return;) } @@ -260,8 +260,8 @@ pub struct Config { impl Config { /// Frontier hard fork configuration. - pub const fn frontier() -> Config { - Config { + pub const fn frontier() -> Self { + Self { gas_ext_code: 20, gas_ext_code_hash: 20, gas_balance: 20, @@ -314,8 +314,8 @@ impl Config { } /// Istanbul hard fork configuration. - pub const fn istanbul() -> Config { - Config { + pub const fn istanbul() -> Self { + Self { gas_ext_code: 700, gas_ext_code_hash: 700, gas_balance: 700, @@ -368,26 +368,26 @@ impl Config { } /// Berlin hard fork configuration. - pub const fn berlin() -> Config { + pub const fn berlin() -> Self { Self::config_with_derived_values(DerivedConfigInputs::berlin()) } /// london hard fork configuration. - pub const fn london() -> Config { + pub const fn london() -> Self { Self::config_with_derived_values(DerivedConfigInputs::london()) } /// The Merge (Paris) hard fork configuration. - pub const fn merge() -> Config { + pub const fn merge() -> Self { Self::config_with_derived_values(DerivedConfigInputs::merge()) } /// Shanghai hard fork configuration. - pub const fn shanghai() -> Config { + pub const fn shanghai() -> Self { Self::config_with_derived_values(DerivedConfigInputs::shanghai()) } - const fn config_with_derived_values(inputs: DerivedConfigInputs) -> Config { + const fn config_with_derived_values(inputs: DerivedConfigInputs) -> Self { let DerivedConfigInputs { gas_storage_read_warm, gas_sload_cold, @@ -412,7 +412,7 @@ impl Config { }; let max_refund_quotient = if decrease_clears_refund { 5 } else { 2 }; - Config { + Self { gas_ext_code: 0, gas_ext_code_hash: 0, gas_balance: 0, diff --git a/src/backend/memory.rs b/src/backend/memory.rs index 37e7718b1..3fcbe5da1 100644 --- a/src/backend/memory.rs +++ b/src/backend/memory.rs @@ -75,7 +75,7 @@ impl<'vicinity> MemoryBackend<'vicinity> { } /// Get the underlying `BTreeMap` storing the state. - pub fn state(&self) -> &BTreeMap { + pub const fn state(&self) -> &BTreeMap { &self.state } @@ -179,7 +179,7 @@ impl<'vicinity> ApplyBackend for MemoryBackend<'vicinity> { reset_storage, } => { let is_empty = { - let account = self.state.entry(address).or_insert_with(Default::default); + let account = self.state.entry(address).or_default(); account.balance = basic.balance; account.nonce = basic.nonce; if let Some(code) = code { diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 1babbfbab..ad972539c 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -130,15 +130,12 @@ impl<'config> StackSubstateMetadata<'config> { Self { gasometer: Gasometer::new(gas_limit, self.gasometer.config()), is_static: is_static || self.is_static, - depth: match self.depth { - None => Some(0), - Some(n) => Some(n + 1), - }, + depth: self.depth.map_or(Some(0), |n| Some(n + 1)), accessed: self.accessed.as_ref().map(|_| Accessed::default()), } } - pub fn gasometer(&self) -> &Gasometer<'config> { + pub const fn gasometer(&self) -> &Gasometer<'config> { &self.gasometer } @@ -146,11 +143,11 @@ impl<'config> StackSubstateMetadata<'config> { &mut self.gasometer } - pub fn is_static(&self) -> bool { + pub const fn is_static(&self) -> bool { self.is_static } - pub fn depth(&self) -> Option { + pub const fn depth(&self) -> Option { self.depth } @@ -184,7 +181,7 @@ impl<'config> StackSubstateMetadata<'config> { } } - pub fn accessed(&self) -> &Option { + pub const fn accessed(&self) -> &Option { &self.accessed } } @@ -242,17 +239,17 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecutor<'config, 'precompiles, S, P> { /// Return a reference of the Config. - pub fn config(&self) -> &'config Config { + pub const fn config(&self) -> &'config Config { self.config } /// Return a reference to the precompile set. - pub fn precompiles(&self) -> &'precompiles P { + pub const fn precompiles(&self) -> &'precompiles P { self.precompile_set } /// Create a new stack-based executor with given precompiles. - pub fn new_with_precompiles( + pub const fn new_with_precompiles( state: S, config: &'config Config, precompile_set: &'precompiles P, @@ -264,7 +261,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> } } - pub fn state(&self) -> &S { + pub const fn state(&self) -> &S { &self.state } @@ -272,6 +269,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> &mut self.state } + #[allow(clippy::missing_const_for_fn)] pub fn into_state(self) -> S { self.state } @@ -728,7 +726,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> }; } - fn l64(gas: u64) -> u64 { + const fn l64(gas: u64) -> u64 { gas - gas / 64 } @@ -853,7 +851,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> }; } - fn l64(gas: u64) -> u64 { + const fn l64(gas: u64) -> u64 { gas - gas / 64 } diff --git a/src/executor/stack/memory.rs b/src/executor/stack/memory.rs index 957b2b3d5..c6c31eace 100644 --- a/src/executor/stack/memory.rs +++ b/src/executor/stack/memory.rs @@ -27,7 +27,7 @@ pub struct MemoryStackSubstate<'config> { } impl<'config> MemoryStackSubstate<'config> { - pub fn new(metadata: StackSubstateMetadata<'config>) -> Self { + pub const fn new(metadata: StackSubstateMetadata<'config>) -> Self { Self { metadata, parent: None, @@ -46,7 +46,7 @@ impl<'config> MemoryStackSubstate<'config> { &mut self.logs } - pub fn metadata(&self) -> &StackSubstateMetadata<'config> { + pub const fn metadata(&self) -> &StackSubstateMetadata<'config> { &self.metadata } @@ -175,13 +175,14 @@ impl<'config> MemoryStackSubstate<'config> { } pub fn known_account(&self, address: H160) -> Option<&MemoryStackAccount> { - if let Some(account) = self.accounts.get(&address) { - Some(account) - } else if let Some(parent) = self.parent.as_ref() { - parent.known_account(address) - } else { - None - } + self.accounts.get(&address).map_or_else( + || { + self.parent + .as_ref() + .and_then(|parent| parent.known_account(address)) + }, + Some, + ) } pub fn known_basic(&self, address: H160) -> Option { @@ -556,7 +557,7 @@ impl<'backend, 'config, B: Backend> StackState<'config> for MemoryStackState<'ba } impl<'backend, 'config, B: Backend> MemoryStackState<'backend, 'config, B> { - pub fn new(metadata: StackSubstateMetadata<'config>, backend: &'backend B) -> Self { + pub const fn new(metadata: StackSubstateMetadata<'config>, backend: &'backend B) -> Self { Self { backend, substate: MemoryStackSubstate::new(metadata), diff --git a/src/executor/stack/precompile.rs b/src/executor/stack/precompile.rs index 716eed631..c2b7dbe78 100644 --- a/src/executor/stack/precompile.rs +++ b/src/executor/stack/precompile.rs @@ -28,8 +28,8 @@ pub enum PrecompileFailure { } impl From for PrecompileFailure { - fn from(error: ExitError) -> PrecompileFailure { - PrecompileFailure::Error { exit_status: error } + fn from(error: ExitError) -> Self { + Self::Error { exit_status: error } } } From 6b8d03b9e36db99f8705b7cf135a38572b1a9d9d Mon Sep 17 00:00:00 2001 From: Evgeny Ukhanov Date: Wed, 20 Sep 2023 00:03:28 +0200 Subject: [PATCH 17/23] Extend traits --- core/src/lib.rs | 2 ++ src/executor/stack/executor.rs | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/core/src/lib.rs b/core/src/lib.rs index 49c798ec3..e7a776890 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -13,8 +13,10 @@ mod opcode; mod stack; mod utils; mod valids; +mod external; pub use crate::error::{Capture, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed, Trap}; +pub use crate::external::ExternalOperation; pub use crate::memory::Memory; pub use crate::opcode::Opcode; pub use crate::stack::Stack; diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index ad972539c..4042d65a0 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -1491,6 +1491,25 @@ impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Pr .record_cost(cost) } + /// Record Substrate specific cost. + fn record_external_cost( + &mut self, + ref_time: Option, + proof_size: Option, + storage_growth: Option, + ) -> Result<(), ExitError> { + self.executor + .state + .record_external_cost(ref_time, proof_size, storage_growth) + } + + /// Refund Substrate specific cost. + fn refund_external_cost(&mut self, ref_time: Option, proof_size: Option) { + self.executor + .state + .refund_external_cost(ref_time, proof_size); + } + /// Retreive the remaining gas. fn remaining_gas(&self) -> u64 { self.executor.state.metadata().gasometer.gas() From 7ca758f132417001f2b9261479682cb8e600a06a Mon Sep 17 00:00:00 2001 From: Evgeny Ukhanov Date: Wed, 20 Sep 2023 00:15:21 +0200 Subject: [PATCH 18/23] Apply trait --- src/executor/stack/executor.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 4042d65a0..2532d7506 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -1390,6 +1390,10 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler capture } + + fn record_external_operation(&mut self, op: crate::ExternalOperation) -> Result<(), ExitError> { + self.state.record_external_operation(op) + } } struct StackExecutorHandle<'inner, 'config, 'precompiles, S, P> { From b1363ecba56a4c049cdf0b24cf08d35eedbf6f2c Mon Sep 17 00:00:00 2001 From: Evgeny Ukhanov Date: Wed, 20 Sep 2023 00:25:40 +0200 Subject: [PATCH 19/23] Apply trait --- src/executor/stack/executor.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 2532d7506..86526bf44 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -226,6 +226,14 @@ pub trait StackState<'config>: Backend { fn code_hash(&self, address: H160) -> H256 { H256::from_slice(Keccak256::digest(self.code(address)).as_slice()) } + + fn record_external_operation( + &mut self, + _op: crate::ExternalOperation, + ) -> Result<(), ExitError> { + Ok(()) + } + } /// Stack-based executor. From ce649abeebea465f414f6b6fb90aa3b8c5d0ce6c Mon Sep 17 00:00:00 2001 From: Evgeny Ukhanov Date: Wed, 20 Sep 2023 00:34:40 +0200 Subject: [PATCH 20/23] Apply trait --- src/executor/stack/executor.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 86526bf44..c61b1b928 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -234,6 +234,7 @@ pub trait StackState<'config>: Backend { Ok(()) } + fn refund_external_cost(&mut self, _ref_time: Option, _proof_size: Option) {} } /// Stack-based executor. @@ -1515,6 +1516,7 @@ impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Pr .record_external_cost(ref_time, proof_size, storage_growth) } + /// Refund Substrate specific cost. fn refund_external_cost(&mut self, ref_time: Option, proof_size: Option) { self.executor From 8e85ebffa0cf39749f70fa0eeae290bbf851b9a0 Mon Sep 17 00:00:00 2001 From: Evgeny Ukhanov Date: Wed, 20 Sep 2023 00:48:16 +0200 Subject: [PATCH 21/23] Added methods --- core/src/lib.rs | 2 +- src/executor/stack/executor.rs | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index e7a776890..86af7948b 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -8,12 +8,12 @@ extern crate alloc; mod error; mod eval; +mod external; mod memory; mod opcode; mod stack; mod utils; mod valids; -mod external; pub use crate::error::{Capture, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed, Trap}; pub use crate::external::ExternalOperation; diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index c61b1b928..2e29ceb6b 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -234,6 +234,24 @@ pub trait StackState<'config>: Backend { Ok(()) } + fn record_external_dynamic_opcode_cost( + &mut self, + _opcode: Opcode, + _gas_cost: crate::gasometer::GasCost, + _target: StorageTarget, + ) -> Result<(), ExitError> { + Ok(()) + } + + fn record_external_cost( + &mut self, + _ref_time: Option, + _proof_size: Option, + _storage_growth: Option, + ) -> Result<(), ExitError> { + Ok(()) + } + fn refund_external_cost(&mut self, _ref_time: Option, _proof_size: Option) {} } @@ -1516,7 +1534,6 @@ impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Pr .record_external_cost(ref_time, proof_size, storage_growth) } - /// Refund Substrate specific cost. fn refund_external_cost(&mut self, ref_time: Option, proof_size: Option) { self.executor From 7d0a7f617e7598bf70dcbb46a8976ab2c87b8256 Mon Sep 17 00:00:00 2001 From: Evgeny Ukhanov Date: Wed, 20 Sep 2023 01:30:52 +0200 Subject: [PATCH 22/23] Update toolchain --- core/src/lib.rs | 4 +++- rust-toolchain.toml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index 86af7948b..627d4b998 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -117,7 +117,9 @@ impl Machine { /// Inspect the machine's next opcode and current stack. #[must_use] pub fn inspect(&self) -> Option<(Opcode, &Stack)> { - let Ok(position) = self.position else { return None }; + let Ok(position) = self.position else { + return None; + }; self.code.get(position).map(|v| (Opcode(*v), &self.stack)) } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 62b0c05be..052b739c9 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.70.0" +channel = "1.72.1" profile = "minimal" components = [ "rustfmt", "clippy" ] From c6c23ac54539a37e7a81f16d14a3fc0606733f55 Mon Sep 17 00:00:00 2001 From: Michael Birch Date: Thu, 21 Sep 2023 13:26:04 -0400 Subject: [PATCH 23/23] Fix(returndatacopy): Data offset out of bounds is still an error even if len==0 (#31) --- core/src/lib.rs | 4 +++- runtime/src/eval/system.rs | 26 ++++++++++++++------------ rust-toolchain.toml | 2 +- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index 49c798ec3..32eab6db9 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -115,7 +115,9 @@ impl Machine { /// Inspect the machine's next opcode and current stack. #[must_use] pub fn inspect(&self) -> Option<(Opcode, &Stack)> { - let Ok(position) = self.position else { return None }; + let Ok(position) = self.position else { + return None; + }; self.code.get(position).map(|v| (Opcode(*v), &self.stack)) } diff --git a/runtime/src/eval/system.rs b/runtime/src/eval/system.rs index ae5bd3abc..7a1841b1b 100644 --- a/runtime/src/eval/system.rs +++ b/runtime/src/eval/system.rs @@ -148,18 +148,20 @@ pub fn returndatacopy(runtime: &mut Runtime) -> Control { pop_u256!(runtime, data_offset); pop_usize!(runtime, len); - // If `len` is zero then nothing happens, regardless of the - // value of the other parameters. In particular, `memory_offset` - // might be larger than `usize::MAX`, hence why we check this first. - if len == 0 { - return Control::Continue; - } - - // SAFETY: this cast is safe because if `len > 0` then gas cost of memory - // would have already been taken into account at this point. It is impossible - // to have a memory offset greater than `usize::MAX` for any gas limit less - // than `u64::MAX` (and gas limits higher than this are disallowed in general). - let memory_offset = memory_offset.as_usize(); + // If `len` is zero then nothing happens to the memory, regardless + // of the value of `memory_offset`. In particular, the value taken + // from the stack might be larger than `usize::MAX`, hence why the + // `as_usize` cast is not always safe. But because the value does + // not matter when `len == 0` we can safely set it equal to zero instead. + let memory_offset = if len == 0 { + 0 + } else { + // SAFETY: this cast is safe because if `len > 0` then gas cost of memory + // would have already been taken into account at this point. It is impossible + // to have a memory offset greater than `usize::MAX` for any gas limit less + // than `u64::MAX` (and gas limits higher than this are disallowed in general). + memory_offset.as_usize() + }; try_or_fail!(runtime .machine diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 294c8a2ca..052b739c9 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.68.2" +channel = "1.72.1" profile = "minimal" components = [ "rustfmt", "clippy" ]