From 81f6d57ea5a5e4cf3dc8203bb52d5f4b2a832fae Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Tue, 30 Jan 2024 12:21:41 -0800 Subject: [PATCH 01/45] mems offset --- crates/interpreter/src/inner_models.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/crates/interpreter/src/inner_models.rs b/crates/interpreter/src/inner_models.rs index 0c29f72778..8774449530 100644 --- a/crates/interpreter/src/inner_models.rs +++ b/crates/interpreter/src/inner_models.rs @@ -1,8 +1,7 @@ +pub use crate::primitives::CreateScheme; use crate::primitives::{Address, Bytes, TransactTo, TxEnv, U256}; use alloc::boxed::Box; - -pub use crate::primitives::CreateScheme; - +use std::ops::Range as Ranger; /// Inputs for a call. #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -19,6 +18,8 @@ pub struct CallInputs { pub context: CallContext, /// Whether this is a static call. pub is_static: bool, + /// The return memory offset where the output of the call is written. + pub return_memory_offset: Ranger, } /// Inputs for a create call. @@ -39,7 +40,11 @@ pub struct CreateInputs { impl CallInputs { /// Creates new call inputs. - pub fn new(tx_env: &TxEnv, gas_limit: u64) -> Option { + pub fn new( + tx_env: &TxEnv, + gas_limit: u64, + return_memory_offset: Ranger, + ) -> Option { let TransactTo::Call(address) = tx_env.transact_to else { return None; }; @@ -61,12 +66,17 @@ impl CallInputs { scheme: CallScheme::Call, }, is_static: false, + return_memory_offset, }) } /// Returns boxed call inputs. - pub fn new_boxed(tx_env: &TxEnv, gas_limit: u64) -> Option> { - Self::new(tx_env, gas_limit).map(Box::new) + pub fn new_boxed( + tx_env: &TxEnv, + gas_limit: u64, + return_memory_offset: Ranger, + ) -> Option> { + Self::new(tx_env, gas_limit, return_memory_offset).map(Box::new) } } From 3cfbdcef7e15f31ac61f34ddcdc8a315e400d3c8 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Tue, 30 Jan 2024 12:23:00 -0800 Subject: [PATCH 02/45] Update evm.rs --- crates/revm/src/evm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 2f97f1d682..5e9f95d3df 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -276,7 +276,7 @@ impl Evm<'_, EXT, DB> { let first_frame_or_result = match ctx.evm.env.tx.transact_to { TransactTo::Call(_) => exec.call( ctx, - CallInputs::new_boxed(&ctx.evm.env.tx, gas_limit).unwrap(), + CallInputs::new_boxed(&ctx.evm.env.tx, gas_limit, 0..0).unwrap(), 0..0, ), TransactTo::Create(_) => exec.create( From ad8b6af7d68efbd4f2ee60ed8901dc039af838bb Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Tue, 30 Jan 2024 12:23:18 -0800 Subject: [PATCH 03/45] Update context.rs --- crates/revm/src/context.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index 6c4fb6f161..ec469bb230 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -544,6 +544,7 @@ pub(crate) mod test_utils { scheme: revm_interpreter::CallScheme::Call, }, is_static: false, + return_memory_offset: 0..0, } } From 9923c2857410f2dcb71767d34ebec1e348ce00bc Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Tue, 30 Jan 2024 12:23:36 -0800 Subject: [PATCH 04/45] Update host.rs --- crates/interpreter/src/host.rs | 545 ++++++++++++++++++++++++++++++--- 1 file changed, 506 insertions(+), 39 deletions(-) diff --git a/crates/interpreter/src/host.rs b/crates/interpreter/src/host.rs index 87000e7aaa..d6c55e2e6d 100644 --- a/crates/interpreter/src/host.rs +++ b/crates/interpreter/src/host.rs @@ -1,55 +1,522 @@ +mod call_helpers; + +pub use call_helpers::{calc_call_gas, get_memory_input_and_out_ranges}; + use crate::{ - primitives::{Address, Bytecode, Env, Log, B256, U256}, - SelfDestructResult, + gas::{self, COLD_ACCOUNT_ACCESS_COST, WARM_STORAGE_READ_COST}, + interpreter::{Interpreter, InterpreterAction}, + primitives::{Address, Bytes, Log, LogData, Spec, SpecId::*, B256, U256}, + CallContext, CallInputs, CallScheme, CreateInputs, CreateScheme, Host, InstructionResult, + Transfer, MAX_INITCODE_SIZE, }; +use alloc::{boxed::Box, vec::Vec}; +use core::cmp::min; +use revm_primitives::BLOCK_HASH_HISTORY; + +pub fn balance(interpreter: &mut Interpreter, host: &mut H) { + pop_address!(interpreter, address); + let Some((balance, is_cold)) = host.balance(address) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + gas!( + interpreter, + if SPEC::enabled(ISTANBUL) { + // EIP-1884: Repricing for trie-size-dependent opcodes + gas::account_access_gas::(is_cold) + } else if SPEC::enabled(TANGERINE) { + 400 + } else { + 20 + } + ); + push!(interpreter, balance); +} + +/// EIP-1884: Repricing for trie-size-dependent opcodes +pub fn selfbalance(interpreter: &mut Interpreter, host: &mut H) { + check!(interpreter, ISTANBUL); + gas!(interpreter, gas::LOW); + let Some((balance, _)) = host.balance(interpreter.contract.address) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + push!(interpreter, balance); +} + +pub fn extcodesize(interpreter: &mut Interpreter, host: &mut H) { + pop_address!(interpreter, address); + let Some((code, is_cold)) = host.code(address) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + if SPEC::enabled(BERLIN) { + gas!( + interpreter, + if is_cold { + COLD_ACCOUNT_ACCESS_COST + } else { + WARM_STORAGE_READ_COST + } + ); + } else if SPEC::enabled(TANGERINE) { + gas!(interpreter, 700); + } else { + gas!(interpreter, 20); + } + + push!(interpreter, U256::from(code.len())); +} + +/// EIP-1052: EXTCODEHASH opcode +pub fn extcodehash(interpreter: &mut Interpreter, host: &mut H) { + check!(interpreter, CONSTANTINOPLE); + pop_address!(interpreter, address); + let Some((code_hash, is_cold)) = host.code_hash(address) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + if SPEC::enabled(BERLIN) { + gas!( + interpreter, + if is_cold { + COLD_ACCOUNT_ACCESS_COST + } else { + WARM_STORAGE_READ_COST + } + ); + } else if SPEC::enabled(ISTANBUL) { + gas!(interpreter, 700); + } else { + gas!(interpreter, 400); + } + push_b256!(interpreter, code_hash); +} + +pub fn extcodecopy(interpreter: &mut Interpreter, host: &mut H) { + pop_address!(interpreter, address); + pop!(interpreter, memory_offset, code_offset, len_u256); + + let Some((code, is_cold)) = host.code(address) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + + let len = as_usize_or_fail!(interpreter, len_u256); + gas_or_fail!( + interpreter, + gas::extcodecopy_cost::(len as u64, is_cold) + ); + if len == 0 { + return; + } + let memory_offset = as_usize_or_fail!(interpreter, memory_offset); + let code_offset = min(as_usize_saturated!(code_offset), code.len()); + shared_memory_resize!(interpreter, memory_offset, len); + + // Note: this can't panic because we resized memory to fit. + interpreter + .shared_memory + .set_data(memory_offset, code_offset, len, code.bytes()); +} + +pub fn blockhash(interpreter: &mut Interpreter, host: &mut H) { + gas!(interpreter, gas::BLOCKHASH); + pop_top!(interpreter, number); -mod dummy; -pub use dummy::DummyHost; + if let Some(diff) = host.env().block.number.checked_sub(*number) { + let diff = as_usize_saturated!(diff); + // blockhash should push zero if number is same as current block number. + if diff <= BLOCK_HASH_HISTORY && diff != 0 { + let Some(hash) = host.block_hash(*number) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + *number = U256::from_be_bytes(hash.0); + return; + } + } + *number = U256::ZERO; +} + +pub fn sload(interpreter: &mut Interpreter, host: &mut H) { + pop!(interpreter, index); -/// EVM context host. -pub trait Host { - /// Returns a mutable reference to the environment. - fn env(&mut self) -> &mut Env; + let Some((value, is_cold)) = host.sload(interpreter.contract.address, index) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + gas!(interpreter, gas::sload_cost::(is_cold)); + push!(interpreter, value); +} - /// Load an account. - /// - /// Returns (is_cold, is_new_account) - fn load_account(&mut self, address: Address) -> Option<(bool, bool)>; +pub fn sstore(interpreter: &mut Interpreter, host: &mut H) { + check_staticcall!(interpreter); + + pop!(interpreter, index, value); + let Some((original, old, new, is_cold)) = + host.sstore(interpreter.contract.address, index, value) + else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + gas_or_fail!(interpreter, { + let remaining_gas = interpreter.gas.remaining(); + gas::sstore_cost::(original, old, new, remaining_gas, is_cold) + }); + refund!(interpreter, gas::sstore_refund::(original, old, new)); +} + +/// EIP-1153: Transient storage opcodes +/// Store value to transient storage +pub fn tstore(interpreter: &mut Interpreter, host: &mut H) { + check!(interpreter, CANCUN); + check_staticcall!(interpreter); + gas!(interpreter, gas::WARM_STORAGE_READ_COST); + + pop!(interpreter, index, value); + + host.tstore(interpreter.contract.address, index, value); +} - /// Get the block hash of the given block `number`. - fn block_hash(&mut self, number: U256) -> Option; +/// EIP-1153: Transient storage opcodes +/// Load value from transient storage +pub fn tload(interpreter: &mut Interpreter, host: &mut H) { + check!(interpreter, CANCUN); + gas!(interpreter, gas::WARM_STORAGE_READ_COST); - /// Get balance of `address` and if the account is cold. - fn balance(&mut self, address: Address) -> Option<(U256, bool)>; + pop_top!(interpreter, index); - /// Get code of `address` and if the account is cold. - fn code(&mut self, address: Address) -> Option<(Bytecode, bool)>; + *index = host.tload(interpreter.contract.address, *index); +} + +pub fn log(interpreter: &mut Interpreter, host: &mut H) { + check_staticcall!(interpreter); + + pop!(interpreter, offset, len); + let len = as_usize_or_fail!(interpreter, len); + gas_or_fail!(interpreter, gas::log_cost(N as u8, len as u64)); + let data = if len == 0 { + Bytes::new() + } else { + let offset = as_usize_or_fail!(interpreter, offset); + shared_memory_resize!(interpreter, offset, len); + Bytes::copy_from_slice(interpreter.shared_memory.slice(offset, len)) + }; + + if interpreter.stack.len() < N { + interpreter.instruction_result = InstructionResult::StackUnderflow; + return; + } + + let mut topics = Vec::with_capacity(N); + for _ in 0..N { + // SAFETY: stack bounds already checked few lines above + topics.push(B256::from(unsafe { interpreter.stack.pop_unsafe() })); + } + + let log = Log { + address: interpreter.contract.address, + data: LogData::new(topics, data).expect("LogData should have <=4 topics"), + }; + + host.log(log); +} - /// Get code hash of `address` and if the account is cold. - fn code_hash(&mut self, address: Address) -> Option<(B256, bool)>; +pub fn selfdestruct(interpreter: &mut Interpreter, host: &mut H) { + check_staticcall!(interpreter); + pop_address!(interpreter, target); - /// Get storage value of `address` at `index` and if the account is cold. - fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)>; + let Some(res) = host.selfdestruct(interpreter.contract.address, target) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; - /// Set storage value of account address at index. - /// - /// Returns (original, present, new, is_cold). - fn sstore( - &mut self, - address: Address, - index: U256, - value: U256, - ) -> Option<(U256, U256, U256, bool)>; + // EIP-3529: Reduction in refunds + if !SPEC::enabled(LONDON) && !res.previously_destroyed { + refund!(interpreter, gas::SELFDESTRUCT) + } + gas!(interpreter, gas::selfdestruct_cost::(res)); + + interpreter.instruction_result = InstructionResult::SelfDestruct; +} + +pub fn create( + interpreter: &mut Interpreter, + host: &mut H, +) { + check_staticcall!(interpreter); + + // EIP-1014: Skinny CREATE2 + if IS_CREATE2 { + check!(interpreter, PETERSBURG); + } + + pop!(interpreter, value, code_offset, len); + let len = as_usize_or_fail!(interpreter, len); + + let mut code = Bytes::new(); + if len != 0 { + // EIP-3860: Limit and meter initcode + if SPEC::enabled(SHANGHAI) { + // Limit is set as double of max contract bytecode size + let max_initcode_size = host + .env() + .cfg + .limit_contract_code_size + .map(|limit| limit.saturating_mul(2)) + .unwrap_or(MAX_INITCODE_SIZE); + if len > max_initcode_size { + interpreter.instruction_result = InstructionResult::CreateInitCodeSizeLimit; + return; + } + gas!(interpreter, gas::initcode_cost(len as u64)); + } + + let code_offset = as_usize_or_fail!(interpreter, code_offset); + shared_memory_resize!(interpreter, code_offset, len); + code = Bytes::copy_from_slice(interpreter.shared_memory.slice(code_offset, len)); + } + + // EIP-1014: Skinny CREATE2 + let scheme = if IS_CREATE2 { + pop!(interpreter, salt); + gas_or_fail!(interpreter, gas::create2_cost(len)); + CreateScheme::Create2 { salt } + } else { + gas!(interpreter, gas::CREATE); + CreateScheme::Create + }; + + let mut gas_limit = interpreter.gas().remaining(); + + // EIP-150: Gas cost changes for IO-heavy operations + if SPEC::enabled(TANGERINE) { + // take remaining gas and deduce l64 part of it. + gas_limit -= gas_limit / 64 + } + gas!(interpreter, gas_limit); + + // Call host to interact with target contract + interpreter.next_action = InterpreterAction::Create { + inputs: Box::new(CreateInputs { + caller: interpreter.contract.address, + scheme, + value, + init_code: code, + gas_limit, + }), + }; + interpreter.instruction_result = InstructionResult::CallOrCreate; +} + +pub fn call(interpreter: &mut Interpreter, host: &mut H) { + pop!(interpreter, local_gas_limit); + pop_address!(interpreter, to); + // max gas limit is not possible in real ethereum situation. + let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); + + pop!(interpreter, value); + if interpreter.is_static && value != U256::ZERO { + interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic; + return; + } + + let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { + return; + }; + + let Some(mut gas_limit) = calc_call_gas::( + interpreter, + host, + to, + value != U256::ZERO, + local_gas_limit, + true, + true, + ) else { + return; + }; + + gas!(interpreter, gas_limit); + + // add call stipend if there is value to be transferred. + if value != U256::ZERO { + gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND); + } + + // Call host to interact with target contract + interpreter.next_action = InterpreterAction::Call { + inputs: Box::new(CallInputs { + contract: to, + transfer: Transfer { + source: interpreter.contract.address, + target: to, + value, + }, + input, + gas_limit, + context: CallContext { + address: to, + caller: interpreter.contract.address, + code_address: to, + apparent_value: value, + scheme: CallScheme::Call, + }, + is_static: interpreter.is_static, + return_memory_offset: return_memory_offset.clone(), + }), + return_memory_offset, + }; + interpreter.instruction_result = InstructionResult::CallOrCreate; +} + +pub fn call_code(interpreter: &mut Interpreter, host: &mut H) { + pop!(interpreter, local_gas_limit); + pop_address!(interpreter, to); + // max gas limit is not possible in real ethereum situation. + let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); + + pop!(interpreter, value); + let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { + return; + }; + + let Some(mut gas_limit) = calc_call_gas::( + interpreter, + host, + to, + value != U256::ZERO, + local_gas_limit, + true, + false, + ) else { + return; + }; + + gas!(interpreter, gas_limit); + + // add call stipend if there is value to be transferred. + if value != U256::ZERO { + gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND); + } + + // Call host to interact with target contract + interpreter.next_action = InterpreterAction::Call { + inputs: Box::new(CallInputs { + contract: to, + transfer: Transfer { + source: interpreter.contract.address, + target: interpreter.contract.address, + value, + }, + input, + gas_limit, + context: CallContext { + address: interpreter.contract.address, + caller: interpreter.contract.address, + code_address: to, + apparent_value: value, + scheme: CallScheme::CallCode, + }, + is_static: interpreter.is_static, + return_memory_offset: return_memory_offset.clone(), + }), + return_memory_offset, + }; + interpreter.instruction_result = InstructionResult::CallOrCreate; +} + +pub fn delegate_call(interpreter: &mut Interpreter, host: &mut H) { + check!(interpreter, HOMESTEAD); + pop!(interpreter, local_gas_limit); + pop_address!(interpreter, to); + // max gas limit is not possible in real ethereum situation. + let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); + + let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { + return; + }; + + let Some(gas_limit) = + calc_call_gas::(interpreter, host, to, false, local_gas_limit, false, false) + else { + return; + }; + + gas!(interpreter, gas_limit); + + // Call host to interact with target contract + interpreter.next_action = InterpreterAction::Call { + inputs: Box::new(CallInputs { + contract: to, + // This is dummy send for StaticCall and DelegateCall, + // it should do nothing and not touch anything. + transfer: Transfer { + source: interpreter.contract.address, + target: interpreter.contract.address, + value: U256::ZERO, + }, + input, + gas_limit, + context: CallContext { + address: interpreter.contract.address, + caller: interpreter.contract.caller, + code_address: to, + apparent_value: interpreter.contract.value, + scheme: CallScheme::DelegateCall, + }, + is_static: interpreter.is_static, + return_memory_offset: return_memory_offset.clone(), + }), + return_memory_offset, + }; + interpreter.instruction_result = InstructionResult::CallOrCreate; +} - /// Get the transient storage value of `address` at `index`. - fn tload(&mut self, address: Address, index: U256) -> U256; +pub fn static_call(interpreter: &mut Interpreter, host: &mut H) { + check!(interpreter, BYZANTIUM); + pop!(interpreter, local_gas_limit); + pop_address!(interpreter, to); + // max gas limit is not possible in real ethereum situation. + let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); - /// Set the transient storage value of `address` at `index`. - fn tstore(&mut self, address: Address, index: U256, value: U256); + let value = U256::ZERO; + let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { + return; + }; - /// Emit a log owned by `address` with given `LogData`. - fn log(&mut self, log: Log); + let Some(gas_limit) = + calc_call_gas::(interpreter, host, to, false, local_gas_limit, false, true) + else { + return; + }; + gas!(interpreter, gas_limit); - /// Mark `address` to be deleted, with funds transferred to `target`. - fn selfdestruct(&mut self, address: Address, target: Address) -> Option; + // Call host to interact with target contract + interpreter.next_action = InterpreterAction::Call { + inputs: Box::new(CallInputs { + contract: to, + // This is dummy send for StaticCall and DelegateCall, + // it should do nothing and not touch anything. + transfer: Transfer { + source: interpreter.contract.address, + target: interpreter.contract.address, + value: U256::ZERO, + }, + input, + gas_limit, + context: CallContext { + address: to, + caller: interpreter.contract.address, + code_address: to, + apparent_value: value, + scheme: CallScheme::StaticCall, + }, + is_static: true, + return_memory_offset: return_memory_offset.clone(), + }), + return_memory_offset, + }; + interpreter.instruction_result = InstructionResult::CallOrCreate; } From 1bce713b0f6d9b5fd40c9c49673cd85fa6c2ad3a Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Tue, 30 Jan 2024 12:27:07 -0800 Subject: [PATCH 05/45] Update host.rs --- crates/interpreter/src/host.rs | 545 +++------------------------------ 1 file changed, 39 insertions(+), 506 deletions(-) diff --git a/crates/interpreter/src/host.rs b/crates/interpreter/src/host.rs index d6c55e2e6d..87000e7aaa 100644 --- a/crates/interpreter/src/host.rs +++ b/crates/interpreter/src/host.rs @@ -1,522 +1,55 @@ -mod call_helpers; - -pub use call_helpers::{calc_call_gas, get_memory_input_and_out_ranges}; - use crate::{ - gas::{self, COLD_ACCOUNT_ACCESS_COST, WARM_STORAGE_READ_COST}, - interpreter::{Interpreter, InterpreterAction}, - primitives::{Address, Bytes, Log, LogData, Spec, SpecId::*, B256, U256}, - CallContext, CallInputs, CallScheme, CreateInputs, CreateScheme, Host, InstructionResult, - Transfer, MAX_INITCODE_SIZE, + primitives::{Address, Bytecode, Env, Log, B256, U256}, + SelfDestructResult, }; -use alloc::{boxed::Box, vec::Vec}; -use core::cmp::min; -use revm_primitives::BLOCK_HASH_HISTORY; - -pub fn balance(interpreter: &mut Interpreter, host: &mut H) { - pop_address!(interpreter, address); - let Some((balance, is_cold)) = host.balance(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - gas!( - interpreter, - if SPEC::enabled(ISTANBUL) { - // EIP-1884: Repricing for trie-size-dependent opcodes - gas::account_access_gas::(is_cold) - } else if SPEC::enabled(TANGERINE) { - 400 - } else { - 20 - } - ); - push!(interpreter, balance); -} - -/// EIP-1884: Repricing for trie-size-dependent opcodes -pub fn selfbalance(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, ISTANBUL); - gas!(interpreter, gas::LOW); - let Some((balance, _)) = host.balance(interpreter.contract.address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - push!(interpreter, balance); -} - -pub fn extcodesize(interpreter: &mut Interpreter, host: &mut H) { - pop_address!(interpreter, address); - let Some((code, is_cold)) = host.code(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - if SPEC::enabled(BERLIN) { - gas!( - interpreter, - if is_cold { - COLD_ACCOUNT_ACCESS_COST - } else { - WARM_STORAGE_READ_COST - } - ); - } else if SPEC::enabled(TANGERINE) { - gas!(interpreter, 700); - } else { - gas!(interpreter, 20); - } - - push!(interpreter, U256::from(code.len())); -} - -/// EIP-1052: EXTCODEHASH opcode -pub fn extcodehash(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, CONSTANTINOPLE); - pop_address!(interpreter, address); - let Some((code_hash, is_cold)) = host.code_hash(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - if SPEC::enabled(BERLIN) { - gas!( - interpreter, - if is_cold { - COLD_ACCOUNT_ACCESS_COST - } else { - WARM_STORAGE_READ_COST - } - ); - } else if SPEC::enabled(ISTANBUL) { - gas!(interpreter, 700); - } else { - gas!(interpreter, 400); - } - push_b256!(interpreter, code_hash); -} - -pub fn extcodecopy(interpreter: &mut Interpreter, host: &mut H) { - pop_address!(interpreter, address); - pop!(interpreter, memory_offset, code_offset, len_u256); - - let Some((code, is_cold)) = host.code(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - - let len = as_usize_or_fail!(interpreter, len_u256); - gas_or_fail!( - interpreter, - gas::extcodecopy_cost::(len as u64, is_cold) - ); - if len == 0 { - return; - } - let memory_offset = as_usize_or_fail!(interpreter, memory_offset); - let code_offset = min(as_usize_saturated!(code_offset), code.len()); - shared_memory_resize!(interpreter, memory_offset, len); - - // Note: this can't panic because we resized memory to fit. - interpreter - .shared_memory - .set_data(memory_offset, code_offset, len, code.bytes()); -} - -pub fn blockhash(interpreter: &mut Interpreter, host: &mut H) { - gas!(interpreter, gas::BLOCKHASH); - pop_top!(interpreter, number); - if let Some(diff) = host.env().block.number.checked_sub(*number) { - let diff = as_usize_saturated!(diff); - // blockhash should push zero if number is same as current block number. - if diff <= BLOCK_HASH_HISTORY && diff != 0 { - let Some(hash) = host.block_hash(*number) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - *number = U256::from_be_bytes(hash.0); - return; - } - } - *number = U256::ZERO; -} - -pub fn sload(interpreter: &mut Interpreter, host: &mut H) { - pop!(interpreter, index); +mod dummy; +pub use dummy::DummyHost; - let Some((value, is_cold)) = host.sload(interpreter.contract.address, index) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - gas!(interpreter, gas::sload_cost::(is_cold)); - push!(interpreter, value); -} +/// EVM context host. +pub trait Host { + /// Returns a mutable reference to the environment. + fn env(&mut self) -> &mut Env; -pub fn sstore(interpreter: &mut Interpreter, host: &mut H) { - check_staticcall!(interpreter); - - pop!(interpreter, index, value); - let Some((original, old, new, is_cold)) = - host.sstore(interpreter.contract.address, index, value) - else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - gas_or_fail!(interpreter, { - let remaining_gas = interpreter.gas.remaining(); - gas::sstore_cost::(original, old, new, remaining_gas, is_cold) - }); - refund!(interpreter, gas::sstore_refund::(original, old, new)); -} - -/// EIP-1153: Transient storage opcodes -/// Store value to transient storage -pub fn tstore(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, CANCUN); - check_staticcall!(interpreter); - gas!(interpreter, gas::WARM_STORAGE_READ_COST); - - pop!(interpreter, index, value); - - host.tstore(interpreter.contract.address, index, value); -} + /// Load an account. + /// + /// Returns (is_cold, is_new_account) + fn load_account(&mut self, address: Address) -> Option<(bool, bool)>; -/// EIP-1153: Transient storage opcodes -/// Load value from transient storage -pub fn tload(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, CANCUN); - gas!(interpreter, gas::WARM_STORAGE_READ_COST); + /// Get the block hash of the given block `number`. + fn block_hash(&mut self, number: U256) -> Option; - pop_top!(interpreter, index); + /// Get balance of `address` and if the account is cold. + fn balance(&mut self, address: Address) -> Option<(U256, bool)>; - *index = host.tload(interpreter.contract.address, *index); -} - -pub fn log(interpreter: &mut Interpreter, host: &mut H) { - check_staticcall!(interpreter); - - pop!(interpreter, offset, len); - let len = as_usize_or_fail!(interpreter, len); - gas_or_fail!(interpreter, gas::log_cost(N as u8, len as u64)); - let data = if len == 0 { - Bytes::new() - } else { - let offset = as_usize_or_fail!(interpreter, offset); - shared_memory_resize!(interpreter, offset, len); - Bytes::copy_from_slice(interpreter.shared_memory.slice(offset, len)) - }; - - if interpreter.stack.len() < N { - interpreter.instruction_result = InstructionResult::StackUnderflow; - return; - } - - let mut topics = Vec::with_capacity(N); - for _ in 0..N { - // SAFETY: stack bounds already checked few lines above - topics.push(B256::from(unsafe { interpreter.stack.pop_unsafe() })); - } - - let log = Log { - address: interpreter.contract.address, - data: LogData::new(topics, data).expect("LogData should have <=4 topics"), - }; - - host.log(log); -} + /// Get code of `address` and if the account is cold. + fn code(&mut self, address: Address) -> Option<(Bytecode, bool)>; -pub fn selfdestruct(interpreter: &mut Interpreter, host: &mut H) { - check_staticcall!(interpreter); - pop_address!(interpreter, target); + /// Get code hash of `address` and if the account is cold. + fn code_hash(&mut self, address: Address) -> Option<(B256, bool)>; - let Some(res) = host.selfdestruct(interpreter.contract.address, target) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; + /// Get storage value of `address` at `index` and if the account is cold. + fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)>; - // EIP-3529: Reduction in refunds - if !SPEC::enabled(LONDON) && !res.previously_destroyed { - refund!(interpreter, gas::SELFDESTRUCT) - } - gas!(interpreter, gas::selfdestruct_cost::(res)); - - interpreter.instruction_result = InstructionResult::SelfDestruct; -} - -pub fn create( - interpreter: &mut Interpreter, - host: &mut H, -) { - check_staticcall!(interpreter); - - // EIP-1014: Skinny CREATE2 - if IS_CREATE2 { - check!(interpreter, PETERSBURG); - } - - pop!(interpreter, value, code_offset, len); - let len = as_usize_or_fail!(interpreter, len); - - let mut code = Bytes::new(); - if len != 0 { - // EIP-3860: Limit and meter initcode - if SPEC::enabled(SHANGHAI) { - // Limit is set as double of max contract bytecode size - let max_initcode_size = host - .env() - .cfg - .limit_contract_code_size - .map(|limit| limit.saturating_mul(2)) - .unwrap_or(MAX_INITCODE_SIZE); - if len > max_initcode_size { - interpreter.instruction_result = InstructionResult::CreateInitCodeSizeLimit; - return; - } - gas!(interpreter, gas::initcode_cost(len as u64)); - } - - let code_offset = as_usize_or_fail!(interpreter, code_offset); - shared_memory_resize!(interpreter, code_offset, len); - code = Bytes::copy_from_slice(interpreter.shared_memory.slice(code_offset, len)); - } - - // EIP-1014: Skinny CREATE2 - let scheme = if IS_CREATE2 { - pop!(interpreter, salt); - gas_or_fail!(interpreter, gas::create2_cost(len)); - CreateScheme::Create2 { salt } - } else { - gas!(interpreter, gas::CREATE); - CreateScheme::Create - }; - - let mut gas_limit = interpreter.gas().remaining(); - - // EIP-150: Gas cost changes for IO-heavy operations - if SPEC::enabled(TANGERINE) { - // take remaining gas and deduce l64 part of it. - gas_limit -= gas_limit / 64 - } - gas!(interpreter, gas_limit); - - // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Create { - inputs: Box::new(CreateInputs { - caller: interpreter.contract.address, - scheme, - value, - init_code: code, - gas_limit, - }), - }; - interpreter.instruction_result = InstructionResult::CallOrCreate; -} - -pub fn call(interpreter: &mut Interpreter, host: &mut H) { - pop!(interpreter, local_gas_limit); - pop_address!(interpreter, to); - // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); - - pop!(interpreter, value); - if interpreter.is_static && value != U256::ZERO { - interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic; - return; - } - - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { - return; - }; - - let Some(mut gas_limit) = calc_call_gas::( - interpreter, - host, - to, - value != U256::ZERO, - local_gas_limit, - true, - true, - ) else { - return; - }; - - gas!(interpreter, gas_limit); - - // add call stipend if there is value to be transferred. - if value != U256::ZERO { - gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND); - } - - // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Call { - inputs: Box::new(CallInputs { - contract: to, - transfer: Transfer { - source: interpreter.contract.address, - target: to, - value, - }, - input, - gas_limit, - context: CallContext { - address: to, - caller: interpreter.contract.address, - code_address: to, - apparent_value: value, - scheme: CallScheme::Call, - }, - is_static: interpreter.is_static, - return_memory_offset: return_memory_offset.clone(), - }), - return_memory_offset, - }; - interpreter.instruction_result = InstructionResult::CallOrCreate; -} - -pub fn call_code(interpreter: &mut Interpreter, host: &mut H) { - pop!(interpreter, local_gas_limit); - pop_address!(interpreter, to); - // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); - - pop!(interpreter, value); - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { - return; - }; - - let Some(mut gas_limit) = calc_call_gas::( - interpreter, - host, - to, - value != U256::ZERO, - local_gas_limit, - true, - false, - ) else { - return; - }; - - gas!(interpreter, gas_limit); - - // add call stipend if there is value to be transferred. - if value != U256::ZERO { - gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND); - } - - // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Call { - inputs: Box::new(CallInputs { - contract: to, - transfer: Transfer { - source: interpreter.contract.address, - target: interpreter.contract.address, - value, - }, - input, - gas_limit, - context: CallContext { - address: interpreter.contract.address, - caller: interpreter.contract.address, - code_address: to, - apparent_value: value, - scheme: CallScheme::CallCode, - }, - is_static: interpreter.is_static, - return_memory_offset: return_memory_offset.clone(), - }), - return_memory_offset, - }; - interpreter.instruction_result = InstructionResult::CallOrCreate; -} - -pub fn delegate_call(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, HOMESTEAD); - pop!(interpreter, local_gas_limit); - pop_address!(interpreter, to); - // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); - - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { - return; - }; - - let Some(gas_limit) = - calc_call_gas::(interpreter, host, to, false, local_gas_limit, false, false) - else { - return; - }; - - gas!(interpreter, gas_limit); - - // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Call { - inputs: Box::new(CallInputs { - contract: to, - // This is dummy send for StaticCall and DelegateCall, - // it should do nothing and not touch anything. - transfer: Transfer { - source: interpreter.contract.address, - target: interpreter.contract.address, - value: U256::ZERO, - }, - input, - gas_limit, - context: CallContext { - address: interpreter.contract.address, - caller: interpreter.contract.caller, - code_address: to, - apparent_value: interpreter.contract.value, - scheme: CallScheme::DelegateCall, - }, - is_static: interpreter.is_static, - return_memory_offset: return_memory_offset.clone(), - }), - return_memory_offset, - }; - interpreter.instruction_result = InstructionResult::CallOrCreate; -} + /// Set storage value of account address at index. + /// + /// Returns (original, present, new, is_cold). + fn sstore( + &mut self, + address: Address, + index: U256, + value: U256, + ) -> Option<(U256, U256, U256, bool)>; -pub fn static_call(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, BYZANTIUM); - pop!(interpreter, local_gas_limit); - pop_address!(interpreter, to); - // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); + /// Get the transient storage value of `address` at `index`. + fn tload(&mut self, address: Address, index: U256) -> U256; - let value = U256::ZERO; - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { - return; - }; + /// Set the transient storage value of `address` at `index`. + fn tstore(&mut self, address: Address, index: U256, value: U256); - let Some(gas_limit) = - calc_call_gas::(interpreter, host, to, false, local_gas_limit, false, true) - else { - return; - }; - gas!(interpreter, gas_limit); + /// Emit a log owned by `address` with given `LogData`. + fn log(&mut self, log: Log); - // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Call { - inputs: Box::new(CallInputs { - contract: to, - // This is dummy send for StaticCall and DelegateCall, - // it should do nothing and not touch anything. - transfer: Transfer { - source: interpreter.contract.address, - target: interpreter.contract.address, - value: U256::ZERO, - }, - input, - gas_limit, - context: CallContext { - address: to, - caller: interpreter.contract.address, - code_address: to, - apparent_value: value, - scheme: CallScheme::StaticCall, - }, - is_static: true, - return_memory_offset: return_memory_offset.clone(), - }), - return_memory_offset, - }; - interpreter.instruction_result = InstructionResult::CallOrCreate; + /// Mark `address` to be deleted, with funds transferred to `target`. + fn selfdestruct(&mut self, address: Address, target: Address) -> Option; } From 7d4542a772480ac196f35860bc7c26b769a8c793 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Tue, 30 Jan 2024 12:27:31 -0800 Subject: [PATCH 06/45] Update host.rs --- crates/interpreter/src/instructions/host.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 58ae99932e..d6c55e2e6d 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -364,6 +364,7 @@ pub fn call(interpreter: &mut Interpreter, host: &mut H) { scheme: CallScheme::Call, }, is_static: interpreter.is_static, + return_memory_offset: return_memory_offset.clone(), }), return_memory_offset, }; @@ -419,6 +420,7 @@ pub fn call_code(interpreter: &mut Interpreter, host: &mut scheme: CallScheme::CallCode, }, is_static: interpreter.is_static, + return_memory_offset: return_memory_offset.clone(), }), return_memory_offset, }; @@ -465,6 +467,7 @@ pub fn delegate_call(interpreter: &mut Interpreter, host: & scheme: CallScheme::DelegateCall, }, is_static: interpreter.is_static, + return_memory_offset: return_memory_offset.clone(), }), return_memory_offset, }; @@ -511,6 +514,7 @@ pub fn static_call(interpreter: &mut Interpreter, host: &mu scheme: CallScheme::StaticCall, }, is_static: true, + return_memory_offset: return_memory_offset.clone(), }), return_memory_offset, }; From 2db4e5209c60d147a6db516bb36b474b220e82ec Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Tue, 30 Jan 2024 12:31:47 -0800 Subject: [PATCH 07/45] use core instead of std --- crates/interpreter/src/inner_models.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/interpreter/src/inner_models.rs b/crates/interpreter/src/inner_models.rs index 8774449530..a97b651ea8 100644 --- a/crates/interpreter/src/inner_models.rs +++ b/crates/interpreter/src/inner_models.rs @@ -1,7 +1,7 @@ pub use crate::primitives::CreateScheme; use crate::primitives::{Address, Bytes, TransactTo, TxEnv, U256}; use alloc::boxed::Box; -use std::ops::Range as Ranger; +use core::ops::Range as Ranger; /// Inputs for a call. #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] From 47ae24600fd3a0f89a486783a66cb0bff24b7614 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:06:48 -0800 Subject: [PATCH 08/45] Update host.rs --- crates/interpreter/src/instructions/host.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index d6c55e2e6d..317938c10a 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -364,9 +364,8 @@ pub fn call(interpreter: &mut Interpreter, host: &mut H) { scheme: CallScheme::Call, }, is_static: interpreter.is_static, - return_memory_offset: return_memory_offset.clone(), + return_memory_offset: return_memory_offset, }), - return_memory_offset, }; interpreter.instruction_result = InstructionResult::CallOrCreate; } @@ -420,9 +419,8 @@ pub fn call_code(interpreter: &mut Interpreter, host: &mut scheme: CallScheme::CallCode, }, is_static: interpreter.is_static, - return_memory_offset: return_memory_offset.clone(), + return_memory_offset: return_memory_offset, }), - return_memory_offset, }; interpreter.instruction_result = InstructionResult::CallOrCreate; } @@ -467,9 +465,8 @@ pub fn delegate_call(interpreter: &mut Interpreter, host: & scheme: CallScheme::DelegateCall, }, is_static: interpreter.is_static, - return_memory_offset: return_memory_offset.clone(), + return_memory_offset: return_memory_offset, }), - return_memory_offset, }; interpreter.instruction_result = InstructionResult::CallOrCreate; } @@ -514,9 +511,8 @@ pub fn static_call(interpreter: &mut Interpreter, host: &mu scheme: CallScheme::StaticCall, }, is_static: true, - return_memory_offset: return_memory_offset.clone(), + return_memory_offset: return_memory_offset, }), - return_memory_offset, }; interpreter.instruction_result = InstructionResult::CallOrCreate; } From 857c9d9e56148ca5b566d2f263d6bc982818c748 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:07:06 -0800 Subject: [PATCH 09/45] Update evm.rs --- crates/revm/src/evm.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 5e9f95d3df..ed3110bd57 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -195,10 +195,7 @@ impl Evm<'_, EXT, DB> { let exec = &mut self.handler.execution; let frame_or_result = match next_action { - InterpreterAction::Call { - inputs, - return_memory_offset, - } => exec.call(&mut self.context, inputs, return_memory_offset), + InterpreterAction::Call { inputs } => exec.call(&mut self.context, inputs, 0..0), InterpreterAction::Create { inputs } => exec.create(&mut self.context, inputs), InterpreterAction::Return { result } => { // free memory context. From 11cf24d8c709d46c392db0ee3c57541bb7d14c3d Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:07:30 -0800 Subject: [PATCH 10/45] Update interpreter.rs --- crates/interpreter/src/interpreter.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 623c3a6849..b00b85cf0d 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -15,7 +15,7 @@ use crate::{ }; use alloc::boxed::Box; use core::cmp::min; -use core::ops::Range; + use revm_primitives::U256; pub use self::shared_memory::EMPTY_SHARED_MEMORY; @@ -70,10 +70,10 @@ pub enum InterpreterAction { Call { /// Call inputs inputs: Box, - /// The offset into `self.memory` of the return data. - /// - /// This value must be ignored if `self.return_len` is 0. - return_memory_offset: Range, + // /// The offset into `self.memory` of the return data. + // /// + // /// This value must be ignored if `self.return_len` is 0. + // return_memory_offset: Range, }, /// CREATE or CREATE2 instruction called. Create { inputs: Box }, From d3526bcf9b15552fde72f5c4a8d7af6d1c5fde98 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:11:16 -0800 Subject: [PATCH 11/45] clippy --- crates/interpreter/src/instructions/host.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 317938c10a..4919b644a6 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -364,7 +364,7 @@ pub fn call(interpreter: &mut Interpreter, host: &mut H) { scheme: CallScheme::Call, }, is_static: interpreter.is_static, - return_memory_offset: return_memory_offset, + return_memory_offset, }), }; interpreter.instruction_result = InstructionResult::CallOrCreate; @@ -419,7 +419,7 @@ pub fn call_code(interpreter: &mut Interpreter, host: &mut scheme: CallScheme::CallCode, }, is_static: interpreter.is_static, - return_memory_offset: return_memory_offset, + return_memory_offset, }), }; interpreter.instruction_result = InstructionResult::CallOrCreate; @@ -465,7 +465,7 @@ pub fn delegate_call(interpreter: &mut Interpreter, host: & scheme: CallScheme::DelegateCall, }, is_static: interpreter.is_static, - return_memory_offset: return_memory_offset, + return_memory_offset, }), }; interpreter.instruction_result = InstructionResult::CallOrCreate; @@ -511,7 +511,7 @@ pub fn static_call(interpreter: &mut Interpreter, host: &mu scheme: CallScheme::StaticCall, }, is_static: true, - return_memory_offset: return_memory_offset, + return_memory_offset, }), }; interpreter.instruction_result = InstructionResult::CallOrCreate; From 6c33f94f1d66672c0171a2bbb25f409acfa13a0d Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:14:25 -0800 Subject: [PATCH 12/45] Update interpreter.rs --- crates/interpreter/src/interpreter.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index b00b85cf0d..420a7f1558 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -70,10 +70,6 @@ pub enum InterpreterAction { Call { /// Call inputs inputs: Box, - // /// The offset into `self.memory` of the return data. - // /// - // /// This value must be ignored if `self.return_len` is 0. - // return_memory_offset: Range, }, /// CREATE or CREATE2 instruction called. Create { inputs: Box }, From 04b314a9a7ad7eadcb1b0e4f4b58eac272e4e8ed Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:22:47 -0800 Subject: [PATCH 13/45] rename Ranger to Range --- crates/interpreter/src/inner_models.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/interpreter/src/inner_models.rs b/crates/interpreter/src/inner_models.rs index a97b651ea8..b444b77002 100644 --- a/crates/interpreter/src/inner_models.rs +++ b/crates/interpreter/src/inner_models.rs @@ -1,7 +1,7 @@ pub use crate::primitives::CreateScheme; use crate::primitives::{Address, Bytes, TransactTo, TxEnv, U256}; use alloc::boxed::Box; -use core::ops::Range as Ranger; +use core::ops::Range; /// Inputs for a call. #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -19,7 +19,7 @@ pub struct CallInputs { /// Whether this is a static call. pub is_static: bool, /// The return memory offset where the output of the call is written. - pub return_memory_offset: Ranger, + pub return_memory_offset: Range, } /// Inputs for a create call. @@ -43,7 +43,7 @@ impl CallInputs { pub fn new( tx_env: &TxEnv, gas_limit: u64, - return_memory_offset: Ranger, + return_memory_offset: Range, ) -> Option { let TransactTo::Call(address) = tx_env.transact_to else { return None; @@ -74,7 +74,7 @@ impl CallInputs { pub fn new_boxed( tx_env: &TxEnv, gas_limit: u64, - return_memory_offset: Ranger, + return_memory_offset: Range, ) -> Option> { Self::new(tx_env, gas_limit, return_memory_offset).map(Box::new) } From 5589bbc70366d4ffc1c188d421a8ed1af4185936 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:25:12 -0800 Subject: [PATCH 14/45] Update inner_models.rs --- crates/interpreter/src/inner_models.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/interpreter/src/inner_models.rs b/crates/interpreter/src/inner_models.rs index b444b77002..8a72546628 100644 --- a/crates/interpreter/src/inner_models.rs +++ b/crates/interpreter/src/inner_models.rs @@ -2,6 +2,7 @@ pub use crate::primitives::CreateScheme; use crate::primitives::{Address, Bytes, TransactTo, TxEnv, U256}; use alloc::boxed::Box; use core::ops::Range; + /// Inputs for a call. #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -40,11 +41,7 @@ pub struct CreateInputs { impl CallInputs { /// Creates new call inputs. - pub fn new( - tx_env: &TxEnv, - gas_limit: u64, - return_memory_offset: Range, - ) -> Option { + pub fn new(tx_env: &TxEnv, gas_limit: u64, return_memory_offset: Range) -> Option { let TransactTo::Call(address) = tx_env.transact_to else { return None; }; From 35c45b42618dc99d6310463bc1194e0b47a7554b Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Thu, 1 Feb 2024 14:22:43 -0800 Subject: [PATCH 15/45] Update inner_models.rs --- crates/interpreter/src/inner_models.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/crates/interpreter/src/inner_models.rs b/crates/interpreter/src/inner_models.rs index 8a72546628..872c97f472 100644 --- a/crates/interpreter/src/inner_models.rs +++ b/crates/interpreter/src/inner_models.rs @@ -41,7 +41,7 @@ pub struct CreateInputs { impl CallInputs { /// Creates new call inputs. - pub fn new(tx_env: &TxEnv, gas_limit: u64, return_memory_offset: Range) -> Option { + pub fn new(tx_env: &TxEnv, gas_limit: u64) -> Option { let TransactTo::Call(address) = tx_env.transact_to else { return None; }; @@ -63,17 +63,13 @@ impl CallInputs { scheme: CallScheme::Call, }, is_static: false, - return_memory_offset, + return_memory_offset: 0..0, }) } /// Returns boxed call inputs. - pub fn new_boxed( - tx_env: &TxEnv, - gas_limit: u64, - return_memory_offset: Range, - ) -> Option> { - Self::new(tx_env, gas_limit, return_memory_offset).map(Box::new) + pub fn new_boxed(tx_env: &TxEnv, gas_limit: u64) -> Option> { + Self::new(tx_env, gas_limit).map(Box::new) } } From d9ea8d0e4c96069f819657a531e45dc6487f4d33 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Thu, 1 Feb 2024 14:23:04 -0800 Subject: [PATCH 16/45] Update evm.rs --- crates/revm/src/evm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index ed3110bd57..ccc1dc8dcb 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -273,7 +273,7 @@ impl Evm<'_, EXT, DB> { let first_frame_or_result = match ctx.evm.env.tx.transact_to { TransactTo::Call(_) => exec.call( ctx, - CallInputs::new_boxed(&ctx.evm.env.tx, gas_limit, 0..0).unwrap(), + CallInputs::new_boxed(&ctx.evm.env.tx, gas_limit).unwrap(), 0..0, ), TransactTo::Create(_) => exec.create( From d72061865ef0110b1f364ece5f17b283ce2bd49c Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Thu, 1 Feb 2024 16:43:31 -0800 Subject: [PATCH 17/45] Update execution.rs --- crates/revm/src/handler/handle_types/execution.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/crates/revm/src/handler/handle_types/execution.rs b/crates/revm/src/handler/handle_types/execution.rs index b25536935b..7bf8d083c4 100644 --- a/crates/revm/src/handler/handle_types/execution.rs +++ b/crates/revm/src/handler/handle_types/execution.rs @@ -83,13 +83,8 @@ impl<'a, EXT, DB: Database> ExecutionHandler<'a, EXT, DB> { /// Call frame call handler. #[inline] - pub fn call( - &self, - context: &mut Context, - inputs: Box, - return_memory_offset: Range, - ) -> FrameOrResult { - (self.call)(context, inputs, return_memory_offset) + pub fn call(&self, context: &mut Context, inputs: Box) -> FrameOrResult { + (self.call)(context, inputs.clone(), inputs.return_memory_offset) } /// Call registered handler for call return. From 8e1de4191de93bcbabe69e86294558e1244dca6b Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Thu, 1 Feb 2024 16:43:48 -0800 Subject: [PATCH 18/45] Update evm.rs --- crates/revm/src/evm.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index ccc1dc8dcb..9fd3e6d6e4 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -195,7 +195,7 @@ impl Evm<'_, EXT, DB> { let exec = &mut self.handler.execution; let frame_or_result = match next_action { - InterpreterAction::Call { inputs } => exec.call(&mut self.context, inputs, 0..0), + InterpreterAction::Call { inputs } => exec.call(&mut self.context, inputs), InterpreterAction::Create { inputs } => exec.create(&mut self.context, inputs), InterpreterAction::Return { result } => { // free memory context. @@ -274,7 +274,6 @@ impl Evm<'_, EXT, DB> { TransactTo::Call(_) => exec.call( ctx, CallInputs::new_boxed(&ctx.evm.env.tx, gas_limit).unwrap(), - 0..0, ), TransactTo::Create(_) => exec.create( ctx, From a63bb1e315b1aaa8f55d06b76eb3499a705d535a Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Thu, 1 Feb 2024 16:44:22 -0800 Subject: [PATCH 19/45] Update evm.rs --- crates/revm/src/evm.rs | 779 ++++++++++++++++++++++++----------------- 1 file changed, 467 insertions(+), 312 deletions(-) diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 9fd3e6d6e4..4919b644a6 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,363 +1,518 @@ +mod call_helpers; + +pub use call_helpers::{calc_call_gas, get_memory_input_and_out_ranges}; + use crate::{ - builder::{EvmBuilder, HandlerStage, SetGenericStage}, - db::{Database, DatabaseCommit, EmptyDB}, - handler::Handler, - interpreter::{ - opcode::InstructionTables, Host, Interpreter, InterpreterAction, SelfDestructResult, - SharedMemory, - }, - primitives::{ - specification::SpecId, Address, Bytecode, EVMError, EVMResult, Env, ExecutionResult, Log, - ResultAndState, TransactTo, B256, U256, - }, - Context, Frame, FrameOrResult, FrameResult, + gas::{self, COLD_ACCOUNT_ACCESS_COST, WARM_STORAGE_READ_COST}, + interpreter::{Interpreter, InterpreterAction}, + primitives::{Address, Bytes, Log, LogData, Spec, SpecId::*, B256, U256}, + CallContext, CallInputs, CallScheme, CreateInputs, CreateScheme, Host, InstructionResult, + Transfer, MAX_INITCODE_SIZE, }; -use alloc::vec::Vec; -use core::fmt; -use revm_interpreter::{CallInputs, CreateInputs}; - -/// EVM call stack limit. -pub const CALL_STACK_LIMIT: u64 = 1024; - -/// EVM instance containing both internal EVM context and external context -/// and the handler that dictates the logic of EVM (or hardfork specification). -pub struct Evm<'a, EXT, DB: Database> { - /// Context of execution, containing both EVM and external context. - pub context: Context, - /// Handler of EVM that contains all the logic. Handler contains specification id - /// and it different depending on the specified fork. - pub handler: Handler<'a, Self, EXT, DB>, +use alloc::{boxed::Box, vec::Vec}; +use core::cmp::min; +use revm_primitives::BLOCK_HASH_HISTORY; + +pub fn balance(interpreter: &mut Interpreter, host: &mut H) { + pop_address!(interpreter, address); + let Some((balance, is_cold)) = host.balance(address) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + gas!( + interpreter, + if SPEC::enabled(ISTANBUL) { + // EIP-1884: Repricing for trie-size-dependent opcodes + gas::account_access_gas::(is_cold) + } else if SPEC::enabled(TANGERINE) { + 400 + } else { + 20 + } + ); + push!(interpreter, balance); } -impl fmt::Debug for Evm<'_, EXT, DB> -where - EXT: fmt::Debug, - DB: Database + fmt::Debug, - DB::Error: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Evm") - .field("evm context", &self.context.evm) - .finish_non_exhaustive() - } +/// EIP-1884: Repricing for trie-size-dependent opcodes +pub fn selfbalance(interpreter: &mut Interpreter, host: &mut H) { + check!(interpreter, ISTANBUL); + gas!(interpreter, gas::LOW); + let Some((balance, _)) = host.balance(interpreter.contract.address) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + push!(interpreter, balance); } -impl Evm<'_, EXT, DB> { - /// Commit the changes to the database. - pub fn transact_commit(&mut self) -> Result> { - let ResultAndState { result, state } = self.transact()?; - self.context.evm.db.commit(state); - Ok(result) +pub fn extcodesize(interpreter: &mut Interpreter, host: &mut H) { + pop_address!(interpreter, address); + let Some((code, is_cold)) = host.code(address) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + if SPEC::enabled(BERLIN) { + gas!( + interpreter, + if is_cold { + COLD_ACCOUNT_ACCESS_COST + } else { + WARM_STORAGE_READ_COST + } + ); + } else if SPEC::enabled(TANGERINE) { + gas!(interpreter, 700); + } else { + gas!(interpreter, 20); } + + push!(interpreter, U256::from(code.len())); } -impl<'a> Evm<'a, (), EmptyDB> { - /// Returns evm builder with empty database and empty external context. - pub fn builder() -> EvmBuilder<'a, SetGenericStage, (), EmptyDB> { - EvmBuilder::default() +/// EIP-1052: EXTCODEHASH opcode +pub fn extcodehash(interpreter: &mut Interpreter, host: &mut H) { + check!(interpreter, CONSTANTINOPLE); + pop_address!(interpreter, address); + let Some((code_hash, is_cold)) = host.code_hash(address) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + if SPEC::enabled(BERLIN) { + gas!( + interpreter, + if is_cold { + COLD_ACCOUNT_ACCESS_COST + } else { + WARM_STORAGE_READ_COST + } + ); + } else if SPEC::enabled(ISTANBUL) { + gas!(interpreter, 700); + } else { + gas!(interpreter, 400); } + push_b256!(interpreter, code_hash); } -impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { - /// Create new EVM. - pub fn new( - mut context: Context, - handler: Handler<'a, Self, EXT, DB>, - ) -> Evm<'a, EXT, DB> { - context.evm.journaled_state.set_spec_id(handler.spec_id); - Evm { context, handler } +pub fn extcodecopy(interpreter: &mut Interpreter, host: &mut H) { + pop_address!(interpreter, address); + pop!(interpreter, memory_offset, code_offset, len_u256); + + let Some((code, is_cold)) = host.code(address) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + + let len = as_usize_or_fail!(interpreter, len_u256); + gas_or_fail!( + interpreter, + gas::extcodecopy_cost::(len as u64, is_cold) + ); + if len == 0 { + return; } + let memory_offset = as_usize_or_fail!(interpreter, memory_offset); + let code_offset = min(as_usize_saturated!(code_offset), code.len()); + shared_memory_resize!(interpreter, memory_offset, len); + + // Note: this can't panic because we resized memory to fit. + interpreter + .shared_memory + .set_data(memory_offset, code_offset, len, code.bytes()); +} - /// Allow for evm setting to be modified by feeding current evm - /// into the builder for modifications. - pub fn modify(self) -> EvmBuilder<'a, HandlerStage, EXT, DB> { - EvmBuilder::new(self) +pub fn blockhash(interpreter: &mut Interpreter, host: &mut H) { + gas!(interpreter, gas::BLOCKHASH); + pop_top!(interpreter, number); + + if let Some(diff) = host.env().block.number.checked_sub(*number) { + let diff = as_usize_saturated!(diff); + // blockhash should push zero if number is same as current block number. + if diff <= BLOCK_HASH_HISTORY && diff != 0 { + let Some(hash) = host.block_hash(*number) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + *number = U256::from_be_bytes(hash.0); + return; + } } + *number = U256::ZERO; } -impl Evm<'_, EXT, DB> { - /// Returns specification (hardfork) that the EVM is instanced with. - /// - /// SpecId depends on the handler. - pub fn spec_id(&self) -> SpecId { - self.handler.spec_id - } +pub fn sload(interpreter: &mut Interpreter, host: &mut H) { + pop!(interpreter, index); - /// Pre verify transaction by checking Environment, initial gas spend and if caller - /// has enough balance to pay for the gas. - #[inline] - pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { - self.handler.validation().env(&self.context.evm.env)?; - self.handler - .validation() - .initial_tx_gas(&self.context.evm.env)?; - self.handler - .validation() - .tx_against_state(&mut self.context)?; - Ok(()) - } + let Some((value, is_cold)) = host.sload(interpreter.contract.address, index) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + gas!(interpreter, gas::sload_cost::(is_cold)); + push!(interpreter, value); +} - /// Transact pre-verified transaction - /// - /// This function will not validate the transaction. - #[inline] - pub fn transact_preverified(&mut self) -> EVMResult { - let initial_gas_spend = self - .handler - .validation() - .initial_tx_gas(&self.context.evm.env)?; - let output = self.transact_preverified_inner(initial_gas_spend); - self.handler.post_execution().end(&mut self.context, output) - } +pub fn sstore(interpreter: &mut Interpreter, host: &mut H) { + check_staticcall!(interpreter); + + pop!(interpreter, index, value); + let Some((original, old, new, is_cold)) = + host.sstore(interpreter.contract.address, index, value) + else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + gas_or_fail!(interpreter, { + let remaining_gas = interpreter.gas.remaining(); + gas::sstore_cost::(original, old, new, remaining_gas, is_cold) + }); + refund!(interpreter, gas::sstore_refund::(original, old, new)); +} - /// Transact transaction - /// - /// This function will validate the transaction. - #[inline] - pub fn transact(&mut self) -> EVMResult { - self.handler.validation().env(&self.context.evm.env)?; - let initial_gas_spend = self - .handler - .validation() - .initial_tx_gas(&self.context.evm.env)?; - self.handler - .validation() - .tx_against_state(&mut self.context)?; - - let output = self.transact_preverified_inner(initial_gas_spend); - self.handler.post_execution().end(&mut self.context, output) - } +/// EIP-1153: Transient storage opcodes +/// Store value to transient storage +pub fn tstore(interpreter: &mut Interpreter, host: &mut H) { + check!(interpreter, CANCUN); + check_staticcall!(interpreter); + gas!(interpreter, gas::WARM_STORAGE_READ_COST); - /// Modify spec id, this will create new EVM that matches this spec id. - pub fn modify_spec_id(self, spec_id: SpecId) -> Self { - if self.spec_id() == spec_id { - return self; - } - self.modify().spec_id(spec_id).build() - } + pop!(interpreter, index, value); - /// Returns internal database and external struct. - #[inline] - pub fn into_context(self) -> Context { - self.context - } + host.tstore(interpreter.contract.address, index, value); +} - /// Starts the main loop and returns outcome of the execution. - pub fn start_the_loop(&mut self, first_frame: Frame) -> FrameResult { - // take instruction talbe - let table = self - .handler - .take_instruction_table() - .expect("Instruction table should be present"); +/// EIP-1153: Transient storage opcodes +/// Load value from transient storage +pub fn tload(interpreter: &mut Interpreter, host: &mut H) { + check!(interpreter, CANCUN); + gas!(interpreter, gas::WARM_STORAGE_READ_COST); - // run main loop - let frame_result = match &table { - InstructionTables::Plain(table) => self.run_the_loop(table, first_frame), - InstructionTables::Boxed(table) => self.run_the_loop(table, first_frame), - }; + pop_top!(interpreter, index); - // return back instruction table - self.handler.set_instruction_table(table); + *index = host.tload(interpreter.contract.address, *index); +} - frame_result +pub fn log(interpreter: &mut Interpreter, host: &mut H) { + check_staticcall!(interpreter); + + pop!(interpreter, offset, len); + let len = as_usize_or_fail!(interpreter, len); + gas_or_fail!(interpreter, gas::log_cost(N as u8, len as u64)); + let data = if len == 0 { + Bytes::new() + } else { + let offset = as_usize_or_fail!(interpreter, offset); + shared_memory_resize!(interpreter, offset, len); + Bytes::copy_from_slice(interpreter.shared_memory.slice(offset, len)) + }; + + if interpreter.stack.len() < N { + interpreter.instruction_result = InstructionResult::StackUnderflow; + return; } - /// Runs main call loop. - #[inline] - pub fn run_the_loop( - &mut self, - instruction_table: &[FN; 256], - first_frame: Frame, - ) -> FrameResult - where - FN: Fn(&mut Interpreter, &mut Self), - { - let mut call_stack: Vec = Vec::with_capacity(1025); - call_stack.push(first_frame); - - #[cfg(feature = "memory_limit")] - let mut shared_memory = - SharedMemory::new_with_memory_limit(self.context.evm.env.cfg.memory_limit); - #[cfg(not(feature = "memory_limit"))] - let mut shared_memory = SharedMemory::new(); - - shared_memory.new_context(); - - // peek last stack frame. - let mut stack_frame = call_stack.last_mut().unwrap(); - - loop { - // run interpreter - let interpreter = &mut stack_frame.frame_data_mut().interpreter; - let next_action = interpreter.run(shared_memory, instruction_table, self); - // take shared memory back. - shared_memory = interpreter.take_memory(); - - let exec = &mut self.handler.execution; - let frame_or_result = match next_action { - InterpreterAction::Call { inputs } => exec.call(&mut self.context, inputs), - InterpreterAction::Create { inputs } => exec.create(&mut self.context, inputs), - InterpreterAction::Return { result } => { - // free memory context. - shared_memory.free_context(); - - // pop last frame from the stack and consume it to create FrameResult. - let returned_frame = call_stack - .pop() - .expect("We just returned from Interpreter frame"); - - let ctx = &mut self.context; - FrameOrResult::Result(match returned_frame { - Frame::Call(frame) => { - // return_call - FrameResult::Call(exec.call_return(ctx, frame, result)) - } - Frame::Create(frame) => { - // return_create - FrameResult::Create(exec.create_return(ctx, frame, result)) - } - }) - } - InterpreterAction::None => unreachable!("InterpreterAction::None is not expected"), - }; - - // handle result - match frame_or_result { - FrameOrResult::Frame(frame) => { - shared_memory.new_context(); - call_stack.push(frame); - stack_frame = call_stack.last_mut().unwrap(); - } - FrameOrResult::Result(result) => { - let Some(top_frame) = call_stack.last_mut() else { - // Break the look if there are no more frames. - return result; - }; - stack_frame = top_frame; - let ctx = &mut self.context; - // Insert result to the top frame. - match result { - FrameResult::Call(outcome) => { - // return_call - exec.insert_call_outcome(ctx, stack_frame, &mut shared_memory, outcome) - } - FrameResult::Create(outcome) => { - // return_create - exec.insert_create_outcome(ctx, stack_frame, outcome) - } - } - } - } - } + let mut topics = Vec::with_capacity(N); + for _ in 0..N { + // SAFETY: stack bounds already checked few lines above + topics.push(B256::from(unsafe { interpreter.stack.pop_unsafe() })); } - /// Transact pre-verified transaction. - fn transact_preverified_inner(&mut self, initial_gas_spend: u64) -> EVMResult { - let ctx = &mut self.context; - let pre_exec = self.handler.pre_execution(); - - // load access list and beneficiary if needed. - pre_exec.load_accounts(ctx)?; - - // load precompiles - let precompiles = pre_exec.load_precompiles(); - ctx.evm.set_precompiles(precompiles); - - // deduce caller balance with its limit. - pre_exec.deduct_caller(ctx)?; - - let gas_limit = ctx.evm.env.tx.gas_limit - initial_gas_spend; - - let exec = self.handler.execution(); - // call inner handling of call/create - let first_frame_or_result = match ctx.evm.env.tx.transact_to { - TransactTo::Call(_) => exec.call( - ctx, - CallInputs::new_boxed(&ctx.evm.env.tx, gas_limit).unwrap(), - ), - TransactTo::Create(_) => exec.create( - ctx, - CreateInputs::new_boxed(&ctx.evm.env.tx, gas_limit).unwrap(), - ), - }; - - // Starts the main running loop. - let mut result = match first_frame_or_result { - FrameOrResult::Frame(first_frame) => self.start_the_loop(first_frame), - FrameOrResult::Result(result) => result, - }; - - let ctx = &mut self.context; - - // handle output of call/create calls. - self.handler.execution().last_frame_return(ctx, &mut result); - - let post_exec = self.handler.post_execution(); - // Reimburse the caller - post_exec.reimburse_caller(ctx, result.gas())?; - // Reward beneficiary - post_exec.reward_beneficiary(ctx, result.gas())?; - // Returns output of transaction. - post_exec.output(ctx, result) - } + let log = Log { + address: interpreter.contract.address, + data: LogData::new(topics, data).expect("LogData should have <=4 topics"), + }; + + host.log(log); } -impl Host for Evm<'_, EXT, DB> { - fn env(&mut self) -> &mut Env { - self.context.evm.env() - } +pub fn selfdestruct(interpreter: &mut Interpreter, host: &mut H) { + check_staticcall!(interpreter); + pop_address!(interpreter, target); - fn block_hash(&mut self, number: U256) -> Option { - self.context.evm.block_hash(number) - } + let Some(res) = host.selfdestruct(interpreter.contract.address, target) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; - fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { - self.context.evm.load_account(address) + // EIP-3529: Reduction in refunds + if !SPEC::enabled(LONDON) && !res.previously_destroyed { + refund!(interpreter, gas::SELFDESTRUCT) } + gas!(interpreter, gas::selfdestruct_cost::(res)); - fn balance(&mut self, address: Address) -> Option<(U256, bool)> { - self.context.evm.balance(address) - } + interpreter.instruction_result = InstructionResult::SelfDestruct; +} - fn code(&mut self, address: Address) -> Option<(Bytecode, bool)> { - self.context.evm.code(address) - } +pub fn create( + interpreter: &mut Interpreter, + host: &mut H, +) { + check_staticcall!(interpreter); - fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { - self.context.evm.code_hash(address) + // EIP-1014: Skinny CREATE2 + if IS_CREATE2 { + check!(interpreter, PETERSBURG); } - fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { - self.context.evm.sload(address, index) - } + pop!(interpreter, value, code_offset, len); + let len = as_usize_or_fail!(interpreter, len); + + let mut code = Bytes::new(); + if len != 0 { + // EIP-3860: Limit and meter initcode + if SPEC::enabled(SHANGHAI) { + // Limit is set as double of max contract bytecode size + let max_initcode_size = host + .env() + .cfg + .limit_contract_code_size + .map(|limit| limit.saturating_mul(2)) + .unwrap_or(MAX_INITCODE_SIZE); + if len > max_initcode_size { + interpreter.instruction_result = InstructionResult::CreateInitCodeSizeLimit; + return; + } + gas!(interpreter, gas::initcode_cost(len as u64)); + } - fn sstore( - &mut self, - address: Address, - index: U256, - value: U256, - ) -> Option<(U256, U256, U256, bool)> { - self.context.evm.sstore(address, index, value) + let code_offset = as_usize_or_fail!(interpreter, code_offset); + shared_memory_resize!(interpreter, code_offset, len); + code = Bytes::copy_from_slice(interpreter.shared_memory.slice(code_offset, len)); } - fn tload(&mut self, address: Address, index: U256) -> U256 { - self.context.evm.tload(address, index) + // EIP-1014: Skinny CREATE2 + let scheme = if IS_CREATE2 { + pop!(interpreter, salt); + gas_or_fail!(interpreter, gas::create2_cost(len)); + CreateScheme::Create2 { salt } + } else { + gas!(interpreter, gas::CREATE); + CreateScheme::Create + }; + + let mut gas_limit = interpreter.gas().remaining(); + + // EIP-150: Gas cost changes for IO-heavy operations + if SPEC::enabled(TANGERINE) { + // take remaining gas and deduce l64 part of it. + gas_limit -= gas_limit / 64 } + gas!(interpreter, gas_limit); + + // Call host to interact with target contract + interpreter.next_action = InterpreterAction::Create { + inputs: Box::new(CreateInputs { + caller: interpreter.contract.address, + scheme, + value, + init_code: code, + gas_limit, + }), + }; + interpreter.instruction_result = InstructionResult::CallOrCreate; +} - fn tstore(&mut self, address: Address, index: U256, value: U256) { - self.context.evm.tstore(address, index, value) +pub fn call(interpreter: &mut Interpreter, host: &mut H) { + pop!(interpreter, local_gas_limit); + pop_address!(interpreter, to); + // max gas limit is not possible in real ethereum situation. + let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); + + pop!(interpreter, value); + if interpreter.is_static && value != U256::ZERO { + interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic; + return; } - fn log(&mut self, log: Log) { - self.context.evm.journaled_state.log(log); + let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { + return; + }; + + let Some(mut gas_limit) = calc_call_gas::( + interpreter, + host, + to, + value != U256::ZERO, + local_gas_limit, + true, + true, + ) else { + return; + }; + + gas!(interpreter, gas_limit); + + // add call stipend if there is value to be transferred. + if value != U256::ZERO { + gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND); } - fn selfdestruct(&mut self, address: Address, target: Address) -> Option { - self.context - .evm - .journaled_state - .selfdestruct(address, target, &mut self.context.evm.db) - .map_err(|e| self.context.evm.error = Some(e)) - .ok() + // Call host to interact with target contract + interpreter.next_action = InterpreterAction::Call { + inputs: Box::new(CallInputs { + contract: to, + transfer: Transfer { + source: interpreter.contract.address, + target: to, + value, + }, + input, + gas_limit, + context: CallContext { + address: to, + caller: interpreter.contract.address, + code_address: to, + apparent_value: value, + scheme: CallScheme::Call, + }, + is_static: interpreter.is_static, + return_memory_offset, + }), + }; + interpreter.instruction_result = InstructionResult::CallOrCreate; +} + +pub fn call_code(interpreter: &mut Interpreter, host: &mut H) { + pop!(interpreter, local_gas_limit); + pop_address!(interpreter, to); + // max gas limit is not possible in real ethereum situation. + let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); + + pop!(interpreter, value); + let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { + return; + }; + + let Some(mut gas_limit) = calc_call_gas::( + interpreter, + host, + to, + value != U256::ZERO, + local_gas_limit, + true, + false, + ) else { + return; + }; + + gas!(interpreter, gas_limit); + + // add call stipend if there is value to be transferred. + if value != U256::ZERO { + gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND); } + + // Call host to interact with target contract + interpreter.next_action = InterpreterAction::Call { + inputs: Box::new(CallInputs { + contract: to, + transfer: Transfer { + source: interpreter.contract.address, + target: interpreter.contract.address, + value, + }, + input, + gas_limit, + context: CallContext { + address: interpreter.contract.address, + caller: interpreter.contract.address, + code_address: to, + apparent_value: value, + scheme: CallScheme::CallCode, + }, + is_static: interpreter.is_static, + return_memory_offset, + }), + }; + interpreter.instruction_result = InstructionResult::CallOrCreate; +} + +pub fn delegate_call(interpreter: &mut Interpreter, host: &mut H) { + check!(interpreter, HOMESTEAD); + pop!(interpreter, local_gas_limit); + pop_address!(interpreter, to); + // max gas limit is not possible in real ethereum situation. + let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); + + let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { + return; + }; + + let Some(gas_limit) = + calc_call_gas::(interpreter, host, to, false, local_gas_limit, false, false) + else { + return; + }; + + gas!(interpreter, gas_limit); + + // Call host to interact with target contract + interpreter.next_action = InterpreterAction::Call { + inputs: Box::new(CallInputs { + contract: to, + // This is dummy send for StaticCall and DelegateCall, + // it should do nothing and not touch anything. + transfer: Transfer { + source: interpreter.contract.address, + target: interpreter.contract.address, + value: U256::ZERO, + }, + input, + gas_limit, + context: CallContext { + address: interpreter.contract.address, + caller: interpreter.contract.caller, + code_address: to, + apparent_value: interpreter.contract.value, + scheme: CallScheme::DelegateCall, + }, + is_static: interpreter.is_static, + return_memory_offset, + }), + }; + interpreter.instruction_result = InstructionResult::CallOrCreate; +} + +pub fn static_call(interpreter: &mut Interpreter, host: &mut H) { + check!(interpreter, BYZANTIUM); + pop!(interpreter, local_gas_limit); + pop_address!(interpreter, to); + // max gas limit is not possible in real ethereum situation. + let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); + + let value = U256::ZERO; + let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { + return; + }; + + let Some(gas_limit) = + calc_call_gas::(interpreter, host, to, false, local_gas_limit, false, true) + else { + return; + }; + gas!(interpreter, gas_limit); + + // Call host to interact with target contract + interpreter.next_action = InterpreterAction::Call { + inputs: Box::new(CallInputs { + contract: to, + // This is dummy send for StaticCall and DelegateCall, + // it should do nothing and not touch anything. + transfer: Transfer { + source: interpreter.contract.address, + target: interpreter.contract.address, + value: U256::ZERO, + }, + input, + gas_limit, + context: CallContext { + address: to, + caller: interpreter.contract.address, + code_address: to, + apparent_value: value, + scheme: CallScheme::StaticCall, + }, + is_static: true, + return_memory_offset, + }), + }; + interpreter.instruction_result = InstructionResult::CallOrCreate; } From 6450b24502322ded8193a8400a7515f84cc780a1 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Thu, 1 Feb 2024 16:44:43 -0800 Subject: [PATCH 20/45] Update evm.rs --- crates/revm/src/evm.rs | 779 +++++++++++++++++------------------------ 1 file changed, 312 insertions(+), 467 deletions(-) diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 4919b644a6..9fd3e6d6e4 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,518 +1,363 @@ -mod call_helpers; - -pub use call_helpers::{calc_call_gas, get_memory_input_and_out_ranges}; - use crate::{ - gas::{self, COLD_ACCOUNT_ACCESS_COST, WARM_STORAGE_READ_COST}, - interpreter::{Interpreter, InterpreterAction}, - primitives::{Address, Bytes, Log, LogData, Spec, SpecId::*, B256, U256}, - CallContext, CallInputs, CallScheme, CreateInputs, CreateScheme, Host, InstructionResult, - Transfer, MAX_INITCODE_SIZE, + builder::{EvmBuilder, HandlerStage, SetGenericStage}, + db::{Database, DatabaseCommit, EmptyDB}, + handler::Handler, + interpreter::{ + opcode::InstructionTables, Host, Interpreter, InterpreterAction, SelfDestructResult, + SharedMemory, + }, + primitives::{ + specification::SpecId, Address, Bytecode, EVMError, EVMResult, Env, ExecutionResult, Log, + ResultAndState, TransactTo, B256, U256, + }, + Context, Frame, FrameOrResult, FrameResult, }; -use alloc::{boxed::Box, vec::Vec}; -use core::cmp::min; -use revm_primitives::BLOCK_HASH_HISTORY; - -pub fn balance(interpreter: &mut Interpreter, host: &mut H) { - pop_address!(interpreter, address); - let Some((balance, is_cold)) = host.balance(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - gas!( - interpreter, - if SPEC::enabled(ISTANBUL) { - // EIP-1884: Repricing for trie-size-dependent opcodes - gas::account_access_gas::(is_cold) - } else if SPEC::enabled(TANGERINE) { - 400 - } else { - 20 - } - ); - push!(interpreter, balance); +use alloc::vec::Vec; +use core::fmt; +use revm_interpreter::{CallInputs, CreateInputs}; + +/// EVM call stack limit. +pub const CALL_STACK_LIMIT: u64 = 1024; + +/// EVM instance containing both internal EVM context and external context +/// and the handler that dictates the logic of EVM (or hardfork specification). +pub struct Evm<'a, EXT, DB: Database> { + /// Context of execution, containing both EVM and external context. + pub context: Context, + /// Handler of EVM that contains all the logic. Handler contains specification id + /// and it different depending on the specified fork. + pub handler: Handler<'a, Self, EXT, DB>, } -/// EIP-1884: Repricing for trie-size-dependent opcodes -pub fn selfbalance(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, ISTANBUL); - gas!(interpreter, gas::LOW); - let Some((balance, _)) = host.balance(interpreter.contract.address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - push!(interpreter, balance); -} - -pub fn extcodesize(interpreter: &mut Interpreter, host: &mut H) { - pop_address!(interpreter, address); - let Some((code, is_cold)) = host.code(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - if SPEC::enabled(BERLIN) { - gas!( - interpreter, - if is_cold { - COLD_ACCOUNT_ACCESS_COST - } else { - WARM_STORAGE_READ_COST - } - ); - } else if SPEC::enabled(TANGERINE) { - gas!(interpreter, 700); - } else { - gas!(interpreter, 20); +impl fmt::Debug for Evm<'_, EXT, DB> +where + EXT: fmt::Debug, + DB: Database + fmt::Debug, + DB::Error: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Evm") + .field("evm context", &self.context.evm) + .finish_non_exhaustive() } - - push!(interpreter, U256::from(code.len())); } -/// EIP-1052: EXTCODEHASH opcode -pub fn extcodehash(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, CONSTANTINOPLE); - pop_address!(interpreter, address); - let Some((code_hash, is_cold)) = host.code_hash(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - if SPEC::enabled(BERLIN) { - gas!( - interpreter, - if is_cold { - COLD_ACCOUNT_ACCESS_COST - } else { - WARM_STORAGE_READ_COST - } - ); - } else if SPEC::enabled(ISTANBUL) { - gas!(interpreter, 700); - } else { - gas!(interpreter, 400); +impl Evm<'_, EXT, DB> { + /// Commit the changes to the database. + pub fn transact_commit(&mut self) -> Result> { + let ResultAndState { result, state } = self.transact()?; + self.context.evm.db.commit(state); + Ok(result) } - push_b256!(interpreter, code_hash); } -pub fn extcodecopy(interpreter: &mut Interpreter, host: &mut H) { - pop_address!(interpreter, address); - pop!(interpreter, memory_offset, code_offset, len_u256); - - let Some((code, is_cold)) = host.code(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - - let len = as_usize_or_fail!(interpreter, len_u256); - gas_or_fail!( - interpreter, - gas::extcodecopy_cost::(len as u64, is_cold) - ); - if len == 0 { - return; +impl<'a> Evm<'a, (), EmptyDB> { + /// Returns evm builder with empty database and empty external context. + pub fn builder() -> EvmBuilder<'a, SetGenericStage, (), EmptyDB> { + EvmBuilder::default() } - let memory_offset = as_usize_or_fail!(interpreter, memory_offset); - let code_offset = min(as_usize_saturated!(code_offset), code.len()); - shared_memory_resize!(interpreter, memory_offset, len); - - // Note: this can't panic because we resized memory to fit. - interpreter - .shared_memory - .set_data(memory_offset, code_offset, len, code.bytes()); } -pub fn blockhash(interpreter: &mut Interpreter, host: &mut H) { - gas!(interpreter, gas::BLOCKHASH); - pop_top!(interpreter, number); - - if let Some(diff) = host.env().block.number.checked_sub(*number) { - let diff = as_usize_saturated!(diff); - // blockhash should push zero if number is same as current block number. - if diff <= BLOCK_HASH_HISTORY && diff != 0 { - let Some(hash) = host.block_hash(*number) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - *number = U256::from_be_bytes(hash.0); - return; - } +impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { + /// Create new EVM. + pub fn new( + mut context: Context, + handler: Handler<'a, Self, EXT, DB>, + ) -> Evm<'a, EXT, DB> { + context.evm.journaled_state.set_spec_id(handler.spec_id); + Evm { context, handler } } - *number = U256::ZERO; -} -pub fn sload(interpreter: &mut Interpreter, host: &mut H) { - pop!(interpreter, index); - - let Some((value, is_cold)) = host.sload(interpreter.contract.address, index) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - gas!(interpreter, gas::sload_cost::(is_cold)); - push!(interpreter, value); -} - -pub fn sstore(interpreter: &mut Interpreter, host: &mut H) { - check_staticcall!(interpreter); - - pop!(interpreter, index, value); - let Some((original, old, new, is_cold)) = - host.sstore(interpreter.contract.address, index, value) - else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - gas_or_fail!(interpreter, { - let remaining_gas = interpreter.gas.remaining(); - gas::sstore_cost::(original, old, new, remaining_gas, is_cold) - }); - refund!(interpreter, gas::sstore_refund::(original, old, new)); + /// Allow for evm setting to be modified by feeding current evm + /// into the builder for modifications. + pub fn modify(self) -> EvmBuilder<'a, HandlerStage, EXT, DB> { + EvmBuilder::new(self) + } } -/// EIP-1153: Transient storage opcodes -/// Store value to transient storage -pub fn tstore(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, CANCUN); - check_staticcall!(interpreter); - gas!(interpreter, gas::WARM_STORAGE_READ_COST); - - pop!(interpreter, index, value); - - host.tstore(interpreter.contract.address, index, value); -} +impl Evm<'_, EXT, DB> { + /// Returns specification (hardfork) that the EVM is instanced with. + /// + /// SpecId depends on the handler. + pub fn spec_id(&self) -> SpecId { + self.handler.spec_id + } -/// EIP-1153: Transient storage opcodes -/// Load value from transient storage -pub fn tload(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, CANCUN); - gas!(interpreter, gas::WARM_STORAGE_READ_COST); + /// Pre verify transaction by checking Environment, initial gas spend and if caller + /// has enough balance to pay for the gas. + #[inline] + pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { + self.handler.validation().env(&self.context.evm.env)?; + self.handler + .validation() + .initial_tx_gas(&self.context.evm.env)?; + self.handler + .validation() + .tx_against_state(&mut self.context)?; + Ok(()) + } - pop_top!(interpreter, index); + /// Transact pre-verified transaction + /// + /// This function will not validate the transaction. + #[inline] + pub fn transact_preverified(&mut self) -> EVMResult { + let initial_gas_spend = self + .handler + .validation() + .initial_tx_gas(&self.context.evm.env)?; + let output = self.transact_preverified_inner(initial_gas_spend); + self.handler.post_execution().end(&mut self.context, output) + } - *index = host.tload(interpreter.contract.address, *index); -} + /// Transact transaction + /// + /// This function will validate the transaction. + #[inline] + pub fn transact(&mut self) -> EVMResult { + self.handler.validation().env(&self.context.evm.env)?; + let initial_gas_spend = self + .handler + .validation() + .initial_tx_gas(&self.context.evm.env)?; + self.handler + .validation() + .tx_against_state(&mut self.context)?; + + let output = self.transact_preverified_inner(initial_gas_spend); + self.handler.post_execution().end(&mut self.context, output) + } -pub fn log(interpreter: &mut Interpreter, host: &mut H) { - check_staticcall!(interpreter); - - pop!(interpreter, offset, len); - let len = as_usize_or_fail!(interpreter, len); - gas_or_fail!(interpreter, gas::log_cost(N as u8, len as u64)); - let data = if len == 0 { - Bytes::new() - } else { - let offset = as_usize_or_fail!(interpreter, offset); - shared_memory_resize!(interpreter, offset, len); - Bytes::copy_from_slice(interpreter.shared_memory.slice(offset, len)) - }; - - if interpreter.stack.len() < N { - interpreter.instruction_result = InstructionResult::StackUnderflow; - return; + /// Modify spec id, this will create new EVM that matches this spec id. + pub fn modify_spec_id(self, spec_id: SpecId) -> Self { + if self.spec_id() == spec_id { + return self; + } + self.modify().spec_id(spec_id).build() } - let mut topics = Vec::with_capacity(N); - for _ in 0..N { - // SAFETY: stack bounds already checked few lines above - topics.push(B256::from(unsafe { interpreter.stack.pop_unsafe() })); + /// Returns internal database and external struct. + #[inline] + pub fn into_context(self) -> Context { + self.context } - let log = Log { - address: interpreter.contract.address, - data: LogData::new(topics, data).expect("LogData should have <=4 topics"), - }; + /// Starts the main loop and returns outcome of the execution. + pub fn start_the_loop(&mut self, first_frame: Frame) -> FrameResult { + // take instruction talbe + let table = self + .handler + .take_instruction_table() + .expect("Instruction table should be present"); - host.log(log); -} + // run main loop + let frame_result = match &table { + InstructionTables::Plain(table) => self.run_the_loop(table, first_frame), + InstructionTables::Boxed(table) => self.run_the_loop(table, first_frame), + }; -pub fn selfdestruct(interpreter: &mut Interpreter, host: &mut H) { - check_staticcall!(interpreter); - pop_address!(interpreter, target); + // return back instruction table + self.handler.set_instruction_table(table); - let Some(res) = host.selfdestruct(interpreter.contract.address, target) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; + frame_result + } + + /// Runs main call loop. + #[inline] + pub fn run_the_loop( + &mut self, + instruction_table: &[FN; 256], + first_frame: Frame, + ) -> FrameResult + where + FN: Fn(&mut Interpreter, &mut Self), + { + let mut call_stack: Vec = Vec::with_capacity(1025); + call_stack.push(first_frame); + + #[cfg(feature = "memory_limit")] + let mut shared_memory = + SharedMemory::new_with_memory_limit(self.context.evm.env.cfg.memory_limit); + #[cfg(not(feature = "memory_limit"))] + let mut shared_memory = SharedMemory::new(); + + shared_memory.new_context(); + + // peek last stack frame. + let mut stack_frame = call_stack.last_mut().unwrap(); + + loop { + // run interpreter + let interpreter = &mut stack_frame.frame_data_mut().interpreter; + let next_action = interpreter.run(shared_memory, instruction_table, self); + // take shared memory back. + shared_memory = interpreter.take_memory(); + + let exec = &mut self.handler.execution; + let frame_or_result = match next_action { + InterpreterAction::Call { inputs } => exec.call(&mut self.context, inputs), + InterpreterAction::Create { inputs } => exec.create(&mut self.context, inputs), + InterpreterAction::Return { result } => { + // free memory context. + shared_memory.free_context(); + + // pop last frame from the stack and consume it to create FrameResult. + let returned_frame = call_stack + .pop() + .expect("We just returned from Interpreter frame"); + + let ctx = &mut self.context; + FrameOrResult::Result(match returned_frame { + Frame::Call(frame) => { + // return_call + FrameResult::Call(exec.call_return(ctx, frame, result)) + } + Frame::Create(frame) => { + // return_create + FrameResult::Create(exec.create_return(ctx, frame, result)) + } + }) + } + InterpreterAction::None => unreachable!("InterpreterAction::None is not expected"), + }; - // EIP-3529: Reduction in refunds - if !SPEC::enabled(LONDON) && !res.previously_destroyed { - refund!(interpreter, gas::SELFDESTRUCT) + // handle result + match frame_or_result { + FrameOrResult::Frame(frame) => { + shared_memory.new_context(); + call_stack.push(frame); + stack_frame = call_stack.last_mut().unwrap(); + } + FrameOrResult::Result(result) => { + let Some(top_frame) = call_stack.last_mut() else { + // Break the look if there are no more frames. + return result; + }; + stack_frame = top_frame; + let ctx = &mut self.context; + // Insert result to the top frame. + match result { + FrameResult::Call(outcome) => { + // return_call + exec.insert_call_outcome(ctx, stack_frame, &mut shared_memory, outcome) + } + FrameResult::Create(outcome) => { + // return_create + exec.insert_create_outcome(ctx, stack_frame, outcome) + } + } + } + } + } } - gas!(interpreter, gas::selfdestruct_cost::(res)); - interpreter.instruction_result = InstructionResult::SelfDestruct; + /// Transact pre-verified transaction. + fn transact_preverified_inner(&mut self, initial_gas_spend: u64) -> EVMResult { + let ctx = &mut self.context; + let pre_exec = self.handler.pre_execution(); + + // load access list and beneficiary if needed. + pre_exec.load_accounts(ctx)?; + + // load precompiles + let precompiles = pre_exec.load_precompiles(); + ctx.evm.set_precompiles(precompiles); + + // deduce caller balance with its limit. + pre_exec.deduct_caller(ctx)?; + + let gas_limit = ctx.evm.env.tx.gas_limit - initial_gas_spend; + + let exec = self.handler.execution(); + // call inner handling of call/create + let first_frame_or_result = match ctx.evm.env.tx.transact_to { + TransactTo::Call(_) => exec.call( + ctx, + CallInputs::new_boxed(&ctx.evm.env.tx, gas_limit).unwrap(), + ), + TransactTo::Create(_) => exec.create( + ctx, + CreateInputs::new_boxed(&ctx.evm.env.tx, gas_limit).unwrap(), + ), + }; + + // Starts the main running loop. + let mut result = match first_frame_or_result { + FrameOrResult::Frame(first_frame) => self.start_the_loop(first_frame), + FrameOrResult::Result(result) => result, + }; + + let ctx = &mut self.context; + + // handle output of call/create calls. + self.handler.execution().last_frame_return(ctx, &mut result); + + let post_exec = self.handler.post_execution(); + // Reimburse the caller + post_exec.reimburse_caller(ctx, result.gas())?; + // Reward beneficiary + post_exec.reward_beneficiary(ctx, result.gas())?; + // Returns output of transaction. + post_exec.output(ctx, result) + } } -pub fn create( - interpreter: &mut Interpreter, - host: &mut H, -) { - check_staticcall!(interpreter); - - // EIP-1014: Skinny CREATE2 - if IS_CREATE2 { - check!(interpreter, PETERSBURG); +impl Host for Evm<'_, EXT, DB> { + fn env(&mut self) -> &mut Env { + self.context.evm.env() } - pop!(interpreter, value, code_offset, len); - let len = as_usize_or_fail!(interpreter, len); - - let mut code = Bytes::new(); - if len != 0 { - // EIP-3860: Limit and meter initcode - if SPEC::enabled(SHANGHAI) { - // Limit is set as double of max contract bytecode size - let max_initcode_size = host - .env() - .cfg - .limit_contract_code_size - .map(|limit| limit.saturating_mul(2)) - .unwrap_or(MAX_INITCODE_SIZE); - if len > max_initcode_size { - interpreter.instruction_result = InstructionResult::CreateInitCodeSizeLimit; - return; - } - gas!(interpreter, gas::initcode_cost(len as u64)); - } + fn block_hash(&mut self, number: U256) -> Option { + self.context.evm.block_hash(number) + } - let code_offset = as_usize_or_fail!(interpreter, code_offset); - shared_memory_resize!(interpreter, code_offset, len); - code = Bytes::copy_from_slice(interpreter.shared_memory.slice(code_offset, len)); + fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { + self.context.evm.load_account(address) } - // EIP-1014: Skinny CREATE2 - let scheme = if IS_CREATE2 { - pop!(interpreter, salt); - gas_or_fail!(interpreter, gas::create2_cost(len)); - CreateScheme::Create2 { salt } - } else { - gas!(interpreter, gas::CREATE); - CreateScheme::Create - }; - - let mut gas_limit = interpreter.gas().remaining(); - - // EIP-150: Gas cost changes for IO-heavy operations - if SPEC::enabled(TANGERINE) { - // take remaining gas and deduce l64 part of it. - gas_limit -= gas_limit / 64 + fn balance(&mut self, address: Address) -> Option<(U256, bool)> { + self.context.evm.balance(address) } - gas!(interpreter, gas_limit); - - // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Create { - inputs: Box::new(CreateInputs { - caller: interpreter.contract.address, - scheme, - value, - init_code: code, - gas_limit, - }), - }; - interpreter.instruction_result = InstructionResult::CallOrCreate; -} -pub fn call(interpreter: &mut Interpreter, host: &mut H) { - pop!(interpreter, local_gas_limit); - pop_address!(interpreter, to); - // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); + fn code(&mut self, address: Address) -> Option<(Bytecode, bool)> { + self.context.evm.code(address) + } - pop!(interpreter, value); - if interpreter.is_static && value != U256::ZERO { - interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic; - return; + fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { + self.context.evm.code_hash(address) } - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { - return; - }; - - let Some(mut gas_limit) = calc_call_gas::( - interpreter, - host, - to, - value != U256::ZERO, - local_gas_limit, - true, - true, - ) else { - return; - }; - - gas!(interpreter, gas_limit); - - // add call stipend if there is value to be transferred. - if value != U256::ZERO { - gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND); + fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { + self.context.evm.sload(address, index) } - // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Call { - inputs: Box::new(CallInputs { - contract: to, - transfer: Transfer { - source: interpreter.contract.address, - target: to, - value, - }, - input, - gas_limit, - context: CallContext { - address: to, - caller: interpreter.contract.address, - code_address: to, - apparent_value: value, - scheme: CallScheme::Call, - }, - is_static: interpreter.is_static, - return_memory_offset, - }), - }; - interpreter.instruction_result = InstructionResult::CallOrCreate; -} + fn sstore( + &mut self, + address: Address, + index: U256, + value: U256, + ) -> Option<(U256, U256, U256, bool)> { + self.context.evm.sstore(address, index, value) + } -pub fn call_code(interpreter: &mut Interpreter, host: &mut H) { - pop!(interpreter, local_gas_limit); - pop_address!(interpreter, to); - // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); - - pop!(interpreter, value); - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { - return; - }; - - let Some(mut gas_limit) = calc_call_gas::( - interpreter, - host, - to, - value != U256::ZERO, - local_gas_limit, - true, - false, - ) else { - return; - }; - - gas!(interpreter, gas_limit); - - // add call stipend if there is value to be transferred. - if value != U256::ZERO { - gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND); + fn tload(&mut self, address: Address, index: U256) -> U256 { + self.context.evm.tload(address, index) } - // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Call { - inputs: Box::new(CallInputs { - contract: to, - transfer: Transfer { - source: interpreter.contract.address, - target: interpreter.contract.address, - value, - }, - input, - gas_limit, - context: CallContext { - address: interpreter.contract.address, - caller: interpreter.contract.address, - code_address: to, - apparent_value: value, - scheme: CallScheme::CallCode, - }, - is_static: interpreter.is_static, - return_memory_offset, - }), - }; - interpreter.instruction_result = InstructionResult::CallOrCreate; -} + fn tstore(&mut self, address: Address, index: U256, value: U256) { + self.context.evm.tstore(address, index, value) + } -pub fn delegate_call(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, HOMESTEAD); - pop!(interpreter, local_gas_limit); - pop_address!(interpreter, to); - // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); - - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { - return; - }; - - let Some(gas_limit) = - calc_call_gas::(interpreter, host, to, false, local_gas_limit, false, false) - else { - return; - }; - - gas!(interpreter, gas_limit); - - // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Call { - inputs: Box::new(CallInputs { - contract: to, - // This is dummy send for StaticCall and DelegateCall, - // it should do nothing and not touch anything. - transfer: Transfer { - source: interpreter.contract.address, - target: interpreter.contract.address, - value: U256::ZERO, - }, - input, - gas_limit, - context: CallContext { - address: interpreter.contract.address, - caller: interpreter.contract.caller, - code_address: to, - apparent_value: interpreter.contract.value, - scheme: CallScheme::DelegateCall, - }, - is_static: interpreter.is_static, - return_memory_offset, - }), - }; - interpreter.instruction_result = InstructionResult::CallOrCreate; -} + fn log(&mut self, log: Log) { + self.context.evm.journaled_state.log(log); + } -pub fn static_call(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, BYZANTIUM); - pop!(interpreter, local_gas_limit); - pop_address!(interpreter, to); - // max gas limit is not possible in real ethereum situation. - let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); - - let value = U256::ZERO; - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { - return; - }; - - let Some(gas_limit) = - calc_call_gas::(interpreter, host, to, false, local_gas_limit, false, true) - else { - return; - }; - gas!(interpreter, gas_limit); - - // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Call { - inputs: Box::new(CallInputs { - contract: to, - // This is dummy send for StaticCall and DelegateCall, - // it should do nothing and not touch anything. - transfer: Transfer { - source: interpreter.contract.address, - target: interpreter.contract.address, - value: U256::ZERO, - }, - input, - gas_limit, - context: CallContext { - address: to, - caller: interpreter.contract.address, - code_address: to, - apparent_value: value, - scheme: CallScheme::StaticCall, - }, - is_static: true, - return_memory_offset, - }), - }; - interpreter.instruction_result = InstructionResult::CallOrCreate; + fn selfdestruct(&mut self, address: Address, target: Address) -> Option { + self.context + .evm + .journaled_state + .selfdestruct(address, target, &mut self.context.evm.db) + .map_err(|e| self.context.evm.error = Some(e)) + .ok() + } } From baee6c0ea3ddad39d408ea3fdb6b45ac7c06d2ea Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Thu, 1 Feb 2024 16:45:09 -0800 Subject: [PATCH 21/45] Update host.rs From bf5cb66b8e08075e338ca7994c29bfb5b6a874c0 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Thu, 1 Feb 2024 16:46:56 -0800 Subject: [PATCH 22/45] Update crates/interpreter/src/interpreter.rs Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com> --- crates/interpreter/src/interpreter.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 420a7f1558..0c1e440f1c 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -15,7 +15,6 @@ use crate::{ }; use alloc::boxed::Box; use core::cmp::min; - use revm_primitives::U256; pub use self::shared_memory::EMPTY_SHARED_MEMORY; From b4a601f5b36c3d521d8b5465bd5fe0db0ae76c10 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:24:16 -0800 Subject: [PATCH 23/45] Update execution.rs --- crates/revm/src/handler/mainnet/execution.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/revm/src/handler/mainnet/execution.rs b/crates/revm/src/handler/mainnet/execution.rs index b5a47a9f7f..93e777d21f 100644 --- a/crates/revm/src/handler/mainnet/execution.rs +++ b/crates/revm/src/handler/mainnet/execution.rs @@ -8,7 +8,7 @@ use crate::{ CallFrame, Context, CreateFrame, Frame, FrameOrResult, FrameResult, }; use alloc::boxed::Box; -use core::ops::Range; + use revm_interpreter::{CallOutcome, InterpreterResult}; /// Helper function called inside [`last_frame_return`] @@ -61,11 +61,9 @@ pub fn last_frame_return( pub fn call( context: &mut Context, inputs: Box, - return_memory_offset: Range, ) -> FrameOrResult { - context - .evm - .make_call_frame(&inputs, return_memory_offset.clone()) + let mems = inputs.return_memory_offset.clone(); + context.evm.make_call_frame(&inputs, mems) } #[inline] From 91fc750056deecc5c6cd4e83c82b76030792ed90 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:24:36 -0800 Subject: [PATCH 24/45] Update execution.rs --- crates/revm/src/handler/handle_types/execution.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/revm/src/handler/handle_types/execution.rs b/crates/revm/src/handler/handle_types/execution.rs index 7bf8d083c4..7ed7aa5e8c 100644 --- a/crates/revm/src/handler/handle_types/execution.rs +++ b/crates/revm/src/handler/handle_types/execution.rs @@ -5,7 +5,7 @@ use crate::{ CallFrame, Context, CreateFrame, Frame, FrameOrResult, FrameResult, }; use alloc::{boxed::Box, sync::Arc}; -use core::ops::Range; + use revm_interpreter::{CallOutcome, CreateOutcome, InterpreterResult}; /// Handles first frame return handle. @@ -14,7 +14,7 @@ pub type LastFrameReturnHandle<'a, EXT, DB> = /// Handle sub call. pub type FrameCallHandle<'a, EXT, DB> = - Arc, Box, Range) -> FrameOrResult + 'a>; + Arc, Box) -> FrameOrResult + 'a>; /// Handle call return pub type FrameCallReturnHandle<'a, EXT, DB> = @@ -84,7 +84,7 @@ impl<'a, EXT, DB: Database> ExecutionHandler<'a, EXT, DB> { /// Call frame call handler. #[inline] pub fn call(&self, context: &mut Context, inputs: Box) -> FrameOrResult { - (self.call)(context, inputs.clone(), inputs.return_memory_offset) + (self.call)(context, inputs.clone()) } /// Call registered handler for call return. From 25d0b77743e452bb539a1b8c1df70ed50d108c82 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:24:57 -0800 Subject: [PATCH 25/45] Update handler_register.rs --- crates/revm/src/inspector/handler_register.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index db63f334a9..8c52db96c5 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -152,16 +152,17 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( // Call handler let call_input_stack_inner = call_input_stack.clone(); let old_handle = handler.execution.call.clone(); - handler.execution.call = Arc::new(move |ctx, mut inputs, range| -> FrameOrResult { + handler.execution.call = Arc::new(move |ctx, mut inputs| -> FrameOrResult { let inspector = ctx.external.get_inspector(); + let mems = inputs.return_memory_offset.clone(); // call inspector callto change input or return outcome. - if let Some(outcome) = inspector.call(&mut ctx.evm, &mut inputs, range.clone()) { + if let Some(outcome) = inspector.call(&mut ctx.evm, &mut inputs, mems) { call_input_stack_inner.borrow_mut().push(inputs.clone()); return FrameOrResult::Result(FrameResult::Call(outcome)); } call_input_stack_inner.borrow_mut().push(inputs.clone()); - let mut frame_or_result = old_handle(ctx, inputs, range); + let mut frame_or_result = old_handle(ctx, inputs); let inspector = ctx.external.get_inspector(); if let FrameOrResult::Frame(frame) = &mut frame_or_result { From 59a7d683eb1bd1e779ca435f877139859d71dc6d Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:40:24 -0800 Subject: [PATCH 26/45] Update context.rs --- crates/revm/src/context.rs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index ec469bb230..38a7a64e47 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -13,7 +13,6 @@ use crate::{ FrameOrResult, JournalCheckpoint, CALL_STACK_LIMIT, }; use alloc::boxed::Box; -use core::ops::Range; /// Main Context structure that contains both EvmContext and External context. pub struct Context { @@ -301,11 +300,7 @@ impl EvmContext { } /// Make call frame - pub fn make_call_frame( - &mut self, - inputs: &CallInputs, - return_memory_range: Range, - ) -> FrameOrResult { + pub fn make_call_frame(&mut self, inputs: &CallInputs) -> FrameOrResult { let gas = Gas::new(inputs.gas_limit); let return_result = |instruction_result: InstructionResult| { @@ -315,7 +310,7 @@ impl EvmContext { gas, output: Bytes::new(), }, - return_memory_range.clone(), + inputs.return_memory_offset.clone(), ) }; @@ -364,7 +359,7 @@ impl EvmContext { } else { self.journaled_state.checkpoint_revert(checkpoint); } - FrameOrResult::new_call_result(result, return_memory_range) + FrameOrResult::new_call_result(result, inputs.return_memory_offset.clone()) } else if !bytecode.is_empty() { let contract = Box::new(Contract::new_with_context( inputs.input.clone(), @@ -374,7 +369,7 @@ impl EvmContext { )); // Create interpreter and executes call and push new CallStackFrame. FrameOrResult::new_call_frame( - return_memory_range, + inputs.return_memory_offset.clone(), checkpoint, Interpreter::new(contract, gas.limit(), inputs.is_static), ) @@ -616,7 +611,7 @@ mod tests { evm_context.journaled_state.depth = CALL_STACK_LIMIT as usize + 1; let contract = address!("dead10000000000000000000000000000001dead"); let call_inputs = test_utils::create_mock_call_inputs(contract); - let res = evm_context.make_call_frame(&call_inputs, 0..0); + let res = evm_context.make_call_frame(&call_inputs); let FrameOrResult::Result(err) = res else { panic!("Expected FrameOrResult::Result"); }; @@ -637,7 +632,7 @@ mod tests { let contract = address!("dead10000000000000000000000000000001dead"); let mut call_inputs = test_utils::create_mock_call_inputs(contract); call_inputs.transfer.value = U256::from(1); - let res = evm_context.make_call_frame(&call_inputs, 0..0); + let res = evm_context.make_call_frame(&call_inputs); let FrameOrResult::Result(result) = res else { panic!("Expected FrameOrResult::Result"); }; @@ -658,7 +653,7 @@ mod tests { let mut evm_context = create_cache_db_evm_context_with_balance(Box::new(env), cdb, bal); let contract = address!("dead10000000000000000000000000000001dead"); let call_inputs = test_utils::create_mock_call_inputs(contract); - let res = evm_context.make_call_frame(&call_inputs, 0..0); + let res = evm_context.make_call_frame(&call_inputs); let FrameOrResult::Result(result) = res else { panic!("Expected FrameOrResult::Result"); }; @@ -683,7 +678,7 @@ mod tests { ); let mut evm_context = create_cache_db_evm_context_with_balance(Box::new(env), cdb, bal); let call_inputs = test_utils::create_mock_call_inputs(contract); - let res = evm_context.make_call_frame(&call_inputs, 0..0); + let res = evm_context.make_call_frame(&call_inputs); let FrameOrResult::Frame(Frame::Call(call_frame)) = res else { panic!("Expected FrameOrResult::Frame(Frame::Call(..))"); }; From f505139568abd87b60a66556e6e6629d6832692b Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:41:00 -0800 Subject: [PATCH 27/45] Update frame.rs From bcbcbb10a3a5a62ab25205dc00d9244bce0d6fc2 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:41:25 -0800 Subject: [PATCH 28/45] Update execution.rs --- crates/revm/src/handler/mainnet/execution.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/revm/src/handler/mainnet/execution.rs b/crates/revm/src/handler/mainnet/execution.rs index 93e777d21f..d56d94e99a 100644 --- a/crates/revm/src/handler/mainnet/execution.rs +++ b/crates/revm/src/handler/mainnet/execution.rs @@ -62,8 +62,7 @@ pub fn call( context: &mut Context, inputs: Box, ) -> FrameOrResult { - let mems = inputs.return_memory_offset.clone(); - context.evm.make_call_frame(&inputs, mems) + context.evm.make_call_frame(&inputs) } #[inline] From e2dfde807729f7f64c65f167abb25b1a06e088b1 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:41:47 -0800 Subject: [PATCH 29/45] Update execution.rs From 5f6a870ed11214ac0a4a58c778485161d0257319 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:42:20 -0800 Subject: [PATCH 30/45] Update evm.rs From 1f0111c5e280f753d25741a82022981610bf8cdb Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:42:48 -0800 Subject: [PATCH 31/45] Update handler_register.rs From b6da825592a9fb2b390ba3cb653d42608d6b71e8 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:46:22 -0800 Subject: [PATCH 32/45] Update eip3155.rs --- crates/revm/src/inspector/eip3155.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/revm/src/inspector/eip3155.rs b/crates/revm/src/inspector/eip3155.rs index e3af683c69..920ca79b39 100644 --- a/crates/revm/src/inspector/eip3155.rs +++ b/crates/revm/src/inspector/eip3155.rs @@ -5,7 +5,6 @@ use crate::{ EvmContext, GetInspector, Inspector, }; -use core::ops::Range; use revm_interpreter::CallOutcome; use revm_interpreter::CreateOutcome; use serde_json::json; @@ -92,7 +91,6 @@ impl Inspector for TracerEip3155 { &mut self, _context: &mut EvmContext, _inputs: &mut CallInputs, - _return_memory_offset: Range, ) -> Option { None } From 6a5c2a218bca3a8b04ffec029cdbb6d5133268f6 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:46:44 -0800 Subject: [PATCH 33/45] Update eip3155.rs From 07166937ace447b21868cce1f0a03fb49d61ef45 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:47:06 -0800 Subject: [PATCH 34/45] Update customprinter.rs --- crates/revm/src/inspector/customprinter.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/revm/src/inspector/customprinter.rs b/crates/revm/src/inspector/customprinter.rs index 4f1212c471..14f3a4f490 100644 --- a/crates/revm/src/inspector/customprinter.rs +++ b/crates/revm/src/inspector/customprinter.rs @@ -1,8 +1,6 @@ //! Custom print inspector, it has step level information of execution. //! It is a great tool if some debugging is needed. -use core::ops::Range; - use revm_interpreter::CallOutcome; use revm_interpreter::CreateOutcome; @@ -85,7 +83,6 @@ impl Inspector for CustomPrintTracer { &mut self, _context: &mut EvmContext, inputs: &mut CallInputs, - _return_memory_offset: Range, ) -> Option { println!( "SM CALL: {:?}, context:{:?}, is_static:{:?}, transfer:{:?}, input_size:{:?}", From eb0b8ee56f81cbcd3f7a24744b546417bfff6197 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:48:05 -0800 Subject: [PATCH 35/45] Update inspector.rs --- crates/revm/src/inspector.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index 16fa68b174..e33c081bf7 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -1,5 +1,3 @@ -use core::ops::Range; - use crate::{ interpreter::{CallInputs, CreateInputs, Interpreter}, primitives::{db::Database, Address, Log, U256}, @@ -82,11 +80,10 @@ pub trait Inspector { &mut self, context: &mut EvmContext, inputs: &mut CallInputs, - return_memory_offset: Range, ) -> Option { let _ = context; let _ = inputs; - let _ = return_memory_offset; + let _ = inputs.return_memory_offset; None } From 6218664830b0ebf60739c871d5b98b689201b6b3 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:48:29 -0800 Subject: [PATCH 36/45] Update gas.rs --- crates/revm/src/inspector/gas.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 3b3b1cfb98..e024ce9c8c 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -78,7 +78,6 @@ impl Inspector for GasInspector { #[cfg(test)] mod tests { - use core::ops::Range; use revm_interpreter::CallOutcome; use revm_interpreter::CreateOutcome; @@ -128,9 +127,8 @@ mod tests { &mut self, context: &mut EvmContext, call: &mut CallInputs, - return_memory_offset: Range, ) -> Option { - self.gas_inspector.call(context, call, return_memory_offset) + self.gas_inspector.call(context, call) } fn call_end( From 709c83161e2eb23f63a6de0a9baa62cc9fd83797 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:48:47 -0800 Subject: [PATCH 37/45] Update handler_register.rs --- crates/revm/src/inspector/handler_register.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index 8c52db96c5..9a5f00b6fd 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -156,7 +156,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( let inspector = ctx.external.get_inspector(); let mems = inputs.return_memory_offset.clone(); // call inspector callto change input or return outcome. - if let Some(outcome) = inspector.call(&mut ctx.evm, &mut inputs, mems) { + if let Some(outcome) = inspector.call(&mut ctx.evm, &mut inputs) { call_input_stack_inner.borrow_mut().push(inputs.clone()); return FrameOrResult::Result(FrameResult::Call(outcome)); } @@ -250,7 +250,6 @@ pub fn inspector_instruction< #[cfg(test)] mod tests { - use core::ops::Range; use super::*; use crate::{ @@ -311,7 +310,6 @@ mod tests { &mut self, context: &mut EvmContext, _call: &mut CallInputs, - _return_memory_offset: Range, ) -> Option { if self.call { unreachable!("call should not be called twice") From 80dcd5df627392ceba0437e5e755c21e15557168 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:49:04 -0800 Subject: [PATCH 38/45] Update context.rs From 22e7fd98b8cadb7dfa9e58b5275d33f8bfb23773 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:49:21 -0800 Subject: [PATCH 39/45] Update execution.rs From cd79f7f3bc1f463faa83b21b40a57cea56aad162 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:49:46 -0800 Subject: [PATCH 40/45] Update evm.rs From 5f84bcd3494086d7a9e8068190b73a51be508b7a Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:50:04 -0800 Subject: [PATCH 41/45] Update inner_models.rs From 0e0ae60046b9511849d4fc6eaec881d366c8e2ad Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:53:00 -0800 Subject: [PATCH 42/45] Update handler_register.rs --- crates/revm/src/inspector/handler_register.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index 9a5f00b6fd..af30848f2a 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -154,7 +154,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( let old_handle = handler.execution.call.clone(); handler.execution.call = Arc::new(move |ctx, mut inputs| -> FrameOrResult { let inspector = ctx.external.get_inspector(); - let mems = inputs.return_memory_offset.clone(); + let _mems = inputs.return_memory_offset.clone(); // call inspector callto change input or return outcome. if let Some(outcome) = inspector.call(&mut ctx.evm, &mut inputs) { call_input_stack_inner.borrow_mut().push(inputs.clone()); From 59a5b5ddda45d734cd79106a4ca6df778d95fcd4 Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 16:02:16 -0800 Subject: [PATCH 43/45] Update handler_register.rs --- crates/revm/src/inspector/handler_register.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index af30848f2a..17a669d6b9 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -8,6 +8,7 @@ use crate::{ }; use alloc::{boxed::Box, rc::Rc, sync::Arc, vec::Vec}; +/// Provides access to an `Inspector` instance. pub trait GetInspector<'a, DB: Database> { fn get_inspector(&mut self) -> &mut dyn Inspector; } From cb2e066f568739de9b7d70d1087943feb67491ad Mon Sep 17 00:00:00 2001 From: tesseract <146037313+DoTheBestToGetTheBest@users.noreply.github.com> Date: Wed, 7 Feb 2024 16:06:19 -0800 Subject: [PATCH 44/45] Update inner_models.rs --- crates/interpreter/src/inner_models.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/interpreter/src/inner_models.rs b/crates/interpreter/src/inner_models.rs index 872c97f472..754450ba59 100644 --- a/crates/interpreter/src/inner_models.rs +++ b/crates/interpreter/src/inner_models.rs @@ -109,7 +109,7 @@ impl CreateInputs { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum CallScheme { - /// `CALL` + /// `CALL`. Call, /// `CALLCODE` CallCode, From 1ebe9eb4e1898ff007e1f97ffe30d7f9b2bc4991 Mon Sep 17 00:00:00 2001 From: rakita Date: Mon, 12 Feb 2024 14:48:40 +0100 Subject: [PATCH 45/45] Update crates/revm/src/inspector.rs --- crates/revm/src/inspector.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index e33c081bf7..9379f8864a 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -83,7 +83,6 @@ pub trait Inspector { ) -> Option { let _ = context; let _ = inputs; - let _ = inputs.return_memory_offset; None }