From b2d3e8f95951ee8331c65b6f48f7fe09c7ff6dad Mon Sep 17 00:00:00 2001 From: rakita Date: Tue, 7 Nov 2023 11:26:46 +0100 Subject: [PATCH] feat: Loop call stack (#851) * refactor disabling of beneficiary reward * Refactor call/create instruction return handling * Structure of the loop * cleanup * fix recursive call on call * temp * various fixes * fix bugs, pass all eth tests * refactor setting of next action * cleaup, refactor * refactor main loop * clippy, docs, fmt * remove unused dep --- Cargo.lock | 1 - Cargo.toml | 1 + bins/revme/Cargo.toml | 1 - bins/revme/src/cmd/statetest/runner.rs | 12 +- crates/interpreter/Cargo.toml | 1 - crates/interpreter/src/gas.rs | 3 - crates/interpreter/src/gas/calc.rs | 4 +- crates/interpreter/src/host.rs | 16 +- crates/interpreter/src/host/dummy.rs | 20 +- crates/interpreter/src/instruction_result.rs | 20 +- crates/interpreter/src/instructions.rs | 2 +- .../src/instructions/arithmetic.rs | 22 +- .../interpreter/src/instructions/bitwise.rs | 28 +- .../interpreter/src/instructions/control.rs | 47 +- crates/interpreter/src/instructions/host.rs | 256 ++--- .../interpreter/src/instructions/host_env.rs | 22 +- crates/interpreter/src/instructions/macros.rs | 36 +- crates/interpreter/src/instructions/memory.rs | 10 +- crates/interpreter/src/instructions/opcode.rs | 16 +- crates/interpreter/src/instructions/stack.rs | 10 +- crates/interpreter/src/instructions/system.rs | 24 +- crates/interpreter/src/interpreter.rs | 187 +++- .../src/interpreter/shared_memory.rs | 11 + crates/interpreter/src/lib.rs | 7 +- crates/primitives/Cargo.toml | 1 - crates/revm/Cargo.toml | 1 - crates/revm/benches/bench.rs | 20 +- crates/revm/src/evm.rs | 64 +- crates/revm/src/evm_context.rs | 324 +++++- crates/revm/src/evm_impl.rs | 976 +++++++----------- crates/revm/src/frame.rs | 19 + crates/revm/src/handler.rs | 24 +- crates/revm/src/handler/mainnet.rs | 38 +- crates/revm/src/handler/optimism.rs | 51 +- crates/revm/src/inspector.rs | 76 +- crates/revm/src/inspector/customprinter.rs | 68 +- crates/revm/src/inspector/eip3155.rs | 88 +- crates/revm/src/inspector/gas.rs | 129 +-- crates/revm/src/inspector/instruction.rs | 10 +- crates/revm/src/journaled_state.rs | 4 +- crates/revm/src/lib.rs | 16 +- documentation/src/crates/interpreter.md | 4 - documentation/src/crates/revm.md | 6 +- 43 files changed, 1334 insertions(+), 1342 deletions(-) create mode 100644 crates/revm/src/frame.rs diff --git a/Cargo.lock b/Cargo.lock index 38664f481f..3562eac983 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2446,7 +2446,6 @@ dependencies = [ "indicatif", "plain_hasher", "revm", - "ruint", "serde", "serde_json", "structopt", diff --git a/Cargo.toml b/Cargo.toml index f76b7605fb..fcb04eb722 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ default-members = ["crates/revm"] [profile.release] lto = true codegen-units = 1 +debug = true [profile.ethtests] inherits = "test" diff --git a/bins/revme/Cargo.toml b/bins/revme/Cargo.toml index e6601686bf..03f34cb563 100644 --- a/bins/revme/Cargo.toml +++ b/bins/revme/Cargo.toml @@ -23,7 +23,6 @@ revm = { path = "../../crates/revm", version = "3.5.0", default-features = false alloy-rlp = { version = "0.3", default-features = false, features = [ "arrayvec", ] } -ruint = { version = "1.9.0", features = ["rlp", "serde"] } serde = { version = "1.0", features = ["derive", "rc"] } serde_json = { version = "1.0", features = ["preserve_order"] } structopt = "0.3" diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index a898740d40..3a490174c4 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -71,11 +71,6 @@ fn skip_test(path: &Path) -> bool { // txbyte is of type 02 and we dont parse tx bytes for this test to fail. | "typeTwoBerlin.json" - // Test checks if nonce overflows. We are handling this correctly but we are not parsing - // exception in testsuite There are more nonce overflow tests that are in internal - // call/create, and those tests are passing and are enabled. - | "CreateTransactionHighNonce.json" - // Need to handle Test errors | "transactionIntinsicBug.json" @@ -83,11 +78,11 @@ fn skip_test(path: &Path) -> bool { | "HighGasPrice.json" | "CREATE_HighNonce.json" | "CREATE_HighNonceMinus1.json" + | "CreateTransactionHighNonce.json" // Skip test where basefee/accesslist/difficulty is present but it shouldn't be supported in // London/Berlin/TheMerge. https://github.com/ethereum/tests/blob/5b7e1ab3ffaf026d99d20b17bb30f533a2c80c8b/GeneralStateTests/stExample/eip1559.json#L130 // It is expected to not execute these tests. - | "accessListExample.json" | "basefeeExample.json" | "eip1559.json" | "mergeTest.json" @@ -98,7 +93,6 @@ fn skip_test(path: &Path) -> bool { | "static_Call50000_sha256.json" | "loopMul.json" | "CALLBlake2f_MaxRounds.json" - | "shiftCombinations.json" ) || path_str.contains("stEOF") } @@ -349,8 +343,6 @@ pub fn execute_test_suite( evm.database(&mut state); let path = path.display(); - println!("Test {name:?} (index: {index}, path: {path}) failed:\n{e}"); - println!("\nTraces:"); let _ = evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)); @@ -359,6 +351,8 @@ pub fn execute_test_suite( println!("\nState before: {cache_state:#?}"); println!("\nState after: {:#?}", evm.db().unwrap().cache); println!("\nEnvironment: {env:#?}"); + println!("\nTest name: {name:?} (index: {index}, path: {path}) failed:\n{e}"); + return Err(e); } } diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml index 155deacb02..e0dc8e4b28 100644 --- a/crates/interpreter/Cargo.toml +++ b/crates/interpreter/Cargo.toml @@ -32,7 +32,6 @@ dev = [ "optional_no_base_fee", ] memory_limit = ["revm-primitives/memory_limit"] -no_gas_measuring = ["revm-primitives/no_gas_measuring"] optional_balance_check = ["revm-primitives/optional_balance_check"] optional_block_gas_limit = ["revm-primitives/optional_block_gas_limit"] optional_eip3607 = ["revm-primitives/optional_eip3607"] diff --git a/crates/interpreter/src/gas.rs b/crates/interpreter/src/gas.rs index 21d633dc1f..2d9ebd6f05 100644 --- a/crates/interpreter/src/gas.rs +++ b/crates/interpreter/src/gas.rs @@ -88,9 +88,6 @@ impl Gas { /// Records an explicit cost. /// /// Returns `false` if the gas limit is exceeded. - /// - /// This function is called on every instruction in the interpreter if the feature - /// `no_gas_measuring` is not enabled. #[inline(always)] pub fn record_cost(&mut self, cost: u64) -> bool { let all_used_gas = self.all_used_gas.saturating_add(cost); diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index 118775ee9f..98d26948ab 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -263,14 +263,12 @@ pub fn selfdestruct_cost(res: SelfDestructResult) -> u64 { } pub fn call_cost( - value: U256, + transfers_value: bool, is_new: bool, is_cold: bool, is_call_or_callcode: bool, is_call_or_staticcall: bool, ) -> u64 { - let transfers_value = value != U256::default(); - let call_gas = if SPEC::enabled(BERLIN) { if is_cold { COLD_ACCOUNT_ACCESS_COST diff --git a/crates/interpreter/src/host.rs b/crates/interpreter/src/host.rs index 8bdd9ff973..93242a633e 100644 --- a/crates/interpreter/src/host.rs +++ b/crates/interpreter/src/host.rs @@ -1,7 +1,7 @@ use crate::primitives::Bytecode; use crate::{ primitives::{Address, Bytes, Env, B256, U256}, - CallInputs, CreateInputs, Gas, InstructionResult, SelfDestructResult, SharedMemory, + SelfDestructResult, }; use alloc::vec::Vec; pub use dummy::DummyHost; @@ -52,20 +52,6 @@ pub trait Host { /// Emit a log owned by `address` with given `topics` and `data`. fn log(&mut self, address: Address, topics: Vec, data: Bytes); - /// Invoke a call operation. - fn call( - &mut self, - input: &mut CallInputs, - shared_memory: &mut SharedMemory, - ) -> (InstructionResult, Gas, Bytes); - - /// Invoke a create operation. - fn create( - &mut self, - inputs: &mut CreateInputs, - shared_memory: &mut SharedMemory, - ) -> (InstructionResult, Option
, Gas, Bytes); - /// Mark `address` to be deleted, with funds transferred to `target`. fn selfdestruct(&mut self, address: Address, target: Address) -> Option; } diff --git a/crates/interpreter/src/host/dummy.rs b/crates/interpreter/src/host/dummy.rs index 867948e69e..d6b1f57c06 100644 --- a/crates/interpreter/src/host/dummy.rs +++ b/crates/interpreter/src/host/dummy.rs @@ -1,7 +1,7 @@ use crate::primitives::{hash_map::Entry, Bytecode, Bytes, HashMap, U256}; use crate::{ primitives::{Address, Env, Log, B256, KECCAK_EMPTY}, - CallInputs, CreateInputs, Gas, Host, InstructionResult, SelfDestructResult, SharedMemory, + Host, SelfDestructResult, }; use alloc::vec::Vec; @@ -118,22 +118,4 @@ impl Host for DummyHost { fn selfdestruct(&mut self, _address: Address, _target: Address) -> Option { panic!("Selfdestruct is not supported for this host") } - - #[inline] - fn create( - &mut self, - _inputs: &mut CreateInputs, - _shared_memory: &mut SharedMemory, - ) -> (InstructionResult, Option
, Gas, Bytes) { - panic!("Create is not supported for this host") - } - - #[inline] - fn call( - &mut self, - _input: &mut CallInputs, - _shared_memory: &mut SharedMemory, - ) -> (InstructionResult, Gas, Bytes) { - panic!("Call is not supported for this host") - } } diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index 4214dc7288..dd596e6726 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -6,14 +6,17 @@ use crate::primitives::{Eval, Halt}; pub enum InstructionResult { // success codes Continue = 0x00, - Stop = 0x01, - Return = 0x02, - SelfDestruct = 0x03, + Stop, + Return, + SelfDestruct, // revert codes - Revert = 0x20, // revert opcode - CallTooDeep = 0x21, - OutOfFund = 0x22, + Revert = 0x10, // revert opcode + CallTooDeep, + OutOfFund, + + // Actions + CallOrCreate = 0x20, // error codes OutOfGas = 0x50, @@ -95,8 +98,10 @@ pub enum SuccessOrHalt { Revert, Halt(Halt), FatalExternalError, - // this is internal opcode. + /// Internal instruction that signals Interpreter should continue running. InternalContinue, + /// Internal instruction that signals subcall. + InternalCallOrCreate, } impl SuccessOrHalt { @@ -145,6 +150,7 @@ impl From for SuccessOrHalt { InstructionResult::Return => Self::Success(Eval::Return), InstructionResult::SelfDestruct => Self::Success(Eval::SelfDestruct), InstructionResult::Revert => Self::Revert, + InstructionResult::CallOrCreate => Self::InternalCallOrCreate, // used only in interpreter loop InstructionResult::CallTooDeep => Self::Halt(Halt::CallTooDeep), // not gonna happen for first call InstructionResult::OutOfFund => Self::Halt(Halt::OutOfFund), // Check for first call is done separately. InstructionResult::OutOfGas => Self::Halt(Halt::OutOfGas( diff --git a/crates/interpreter/src/instructions.rs b/crates/interpreter/src/instructions.rs index b313f8421d..ae56015699 100644 --- a/crates/interpreter/src/instructions.rs +++ b/crates/interpreter/src/instructions.rs @@ -1,7 +1,7 @@ //! EVM opcode implementations. #[macro_use] -mod macros; +pub mod macros; pub mod arithmetic; pub mod bitwise; diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index ab82b9a114..e0700ee3f3 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -5,25 +5,25 @@ use crate::{ Host, InstructionResult, Interpreter, }; -pub fn wrapped_add(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn wrapped_add(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); *op2 = op1.wrapping_add(*op2); } -pub fn wrapping_mul(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn wrapping_mul(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); *op2 = op1.wrapping_mul(*op2); } -pub fn wrapping_sub(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn wrapping_sub(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); *op2 = op1.wrapping_sub(*op2); } -pub fn div(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn div(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); if *op2 != U256::ZERO { @@ -31,13 +31,13 @@ pub fn div(interpreter: &mut Interpreter<'_>, _host: &mut H) { } } -pub fn sdiv(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn sdiv(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); *op2 = i256_div(op1, *op2); } -pub fn rem(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn rem(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); if *op2 != U256::ZERO { @@ -45,7 +45,7 @@ pub fn rem(interpreter: &mut Interpreter<'_>, _host: &mut H) { } } -pub fn smod(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn smod(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); if *op2 != U256::ZERO { @@ -53,19 +53,19 @@ pub fn smod(interpreter: &mut Interpreter<'_>, _host: &mut H) { } } -pub fn addmod(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn addmod(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::MID); pop_top!(interpreter, op1, op2, op3); *op3 = op1.add_mod(op2, *op3) } -pub fn mulmod(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn mulmod(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::MID); pop_top!(interpreter, op1, op2, op3); *op3 = op1.mul_mod(op2, *op3) } -pub fn exp(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn exp(interpreter: &mut Interpreter, _host: &mut H) { pop_top!(interpreter, op1, op2); gas_or_fail!(interpreter, gas::exp_cost::(*op2)); *op2 = op1.pow(*op2); @@ -86,7 +86,7 @@ pub fn exp(interpreter: &mut Interpreter<'_>, _host: &mut H /// `y | !mask` where `|` is the bitwise `OR` and `!` is bitwise negation. Similarly, if /// `b == 0` then the yellow paper says the output should start with all zeros, then end with /// bits from `b`; this is equal to `y & mask` where `&` is bitwise `AND`. -pub fn signextend(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn signextend(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::LOW); pop_top!(interpreter, op1, op2); if op1 < U256::from(32) { diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index d8abe677bf..d9fffbfe3e 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -6,67 +6,67 @@ use crate::{ }; use core::cmp::Ordering; -pub fn lt(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn lt(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); *op2 = U256::from(op1 < *op2); } -pub fn gt(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn gt(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); *op2 = U256::from(op1 > *op2); } -pub fn slt(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn slt(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); *op2 = U256::from(i256_cmp(&op1, op2) == Ordering::Less); } -pub fn sgt(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn sgt(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); *op2 = U256::from(i256_cmp(&op1, op2) == Ordering::Greater); } -pub fn eq(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn eq(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); *op2 = U256::from(op1 == *op2); } -pub fn iszero(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn iszero(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1); *op1 = U256::from(*op1 == U256::ZERO); } -pub fn bitand(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn bitand(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); *op2 = op1 & *op2; } -pub fn bitor(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn bitor(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); *op2 = op1 | *op2; } -pub fn bitxor(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn bitxor(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); *op2 = op1 ^ *op2; } -pub fn not(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn not(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1); *op1 = !*op1; } -pub fn byte(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn byte(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); @@ -80,7 +80,7 @@ pub fn byte(interpreter: &mut Interpreter<'_>, _host: &mut H) { } /// EIP-145: Bitwise shifting instructions in EVM -pub fn shl(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn shl(interpreter: &mut Interpreter, _host: &mut H) { check!(interpreter, CONSTANTINOPLE); gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); @@ -88,7 +88,7 @@ pub fn shl(interpreter: &mut Interpreter<'_>, _host: &mut H } /// EIP-145: Bitwise shifting instructions in EVM -pub fn shr(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn shr(interpreter: &mut Interpreter, _host: &mut H) { check!(interpreter, CONSTANTINOPLE); gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); @@ -96,7 +96,7 @@ pub fn shr(interpreter: &mut Interpreter<'_>, _host: &mut H } /// EIP-145: Bitwise shifting instructions in EVM -pub fn sar(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn sar(interpreter: &mut Interpreter, _host: &mut H) { check!(interpreter, CONSTANTINOPLE); gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs index beb2f2c8ce..104c0690b5 100644 --- a/crates/interpreter/src/instructions/control.rs +++ b/crates/interpreter/src/instructions/control.rs @@ -1,10 +1,12 @@ +use revm_primitives::Bytes; + use crate::{ gas, primitives::{Spec, U256}, - Host, InstructionResult, Interpreter, + Host, InstructionResult, Interpreter, InterpreterResult, }; -pub fn jump(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn jump(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::MID); pop!(interpreter, dest); let dest = as_usize_or_fail!(interpreter, dest, InstructionResult::InvalidJump); @@ -18,7 +20,7 @@ pub fn jump(interpreter: &mut Interpreter<'_>, _host: &mut H) { } } -pub fn jumpi(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn jumpi(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::HIGH); pop!(interpreter, dest, value); if value != U256::ZERO { @@ -34,50 +36,61 @@ pub fn jumpi(interpreter: &mut Interpreter<'_>, _host: &mut H) { } } -pub fn jumpdest(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn jumpdest(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::JUMPDEST); } -pub fn pc(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn pc(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::BASE); // - 1 because we have already advanced the instruction pointer in `Interpreter::step` push!(interpreter, U256::from(interpreter.program_counter() - 1)); } #[inline(always)] -fn return_inner(interpreter: &mut Interpreter<'_>, result: InstructionResult) { +fn return_inner(interpreter: &mut Interpreter, instruction_result: InstructionResult) { // zero gas cost // gas!(interpreter, gas::ZERO); pop!(interpreter, offset, len); let len = as_usize_or_fail!(interpreter, len); - // important: offset must be ignored if len is zero + // important: offset must be ignored if len is zeros + let mut output = Bytes::default(); if len != 0 { let offset = as_usize_or_fail!(interpreter, offset); shared_memory_resize!(interpreter, offset, len); - interpreter.return_offset = offset; + + output = interpreter.shared_memory.slice(offset, len).to_vec().into() } - interpreter.return_len = len; - interpreter.instruction_result = result; + interpreter.instruction_result = instruction_result; + interpreter.next_action = Some(crate::InterpreterAction::Return { + result: InterpreterResult { + output, + gas: interpreter.gas, + result: instruction_result, + }, + }); } -pub fn ret(interpreter: &mut Interpreter<'_>, _host: &mut H) { - return_inner(interpreter, InstructionResult::Return) +pub fn ret(interpreter: &mut Interpreter, _host: &mut H) { + return_inner(interpreter, InstructionResult::Return); } /// EIP-140: REVERT instruction -pub fn revert(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn revert(interpreter: &mut Interpreter, _host: &mut H) { check!(interpreter, BYZANTIUM); - return_inner(interpreter, InstructionResult::Revert) + return_inner(interpreter, InstructionResult::Revert); } -pub fn stop(interpreter: &mut Interpreter<'_>, _host: &mut H) { +/// Stop opcode. This opcode halts the execution. +pub fn stop(interpreter: &mut Interpreter, _host: &mut H) { interpreter.instruction_result = InstructionResult::Stop; } -pub fn invalid(interpreter: &mut Interpreter<'_>, _host: &mut H) { +/// Invalid opcode. This opcode halts the execution. +pub fn invalid(interpreter: &mut Interpreter, _host: &mut H) { interpreter.instruction_result = InstructionResult::InvalidFEOpcode; } -pub fn not_found(interpreter: &mut Interpreter<'_>, _host: &mut H) { +/// Unknown opcode. This opcode halts the execution. +pub fn unknown(interpreter: &mut Interpreter, _host: &mut H) { interpreter.instruction_result = InstructionResult::OpcodeNotFound; } diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 3a7c4166b4..2ce4c9b6df 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -1,16 +1,17 @@ +use crate::interpreter::InterpreterAction; use crate::primitives::{Address, Bytes, Spec, SpecId::*, B256, U256}; use crate::MAX_INITCODE_SIZE; use crate::{ gas::{self, COLD_ACCOUNT_ACCESS_COST, WARM_STORAGE_READ_COST}, interpreter::Interpreter, - return_ok, return_revert, CallContext, CallInputs, CallScheme, CreateInputs, CreateScheme, - Host, InstructionResult, Transfer, + CallContext, CallInputs, CallScheme, CreateInputs, CreateScheme, Host, InstructionResult, + Transfer, }; 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) { +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; @@ -31,7 +32,7 @@ pub fn balance(interpreter: &mut Interpreter<'_>, host: &mu } /// EIP-1884: Repricing for trie-size-dependent opcodes -pub fn selfbalance(interpreter: &mut Interpreter<'_>, host: &mut H) { +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 { @@ -41,7 +42,7 @@ pub fn selfbalance(interpreter: &mut Interpreter<'_>, host: push!(interpreter, balance); } -pub fn extcodesize(interpreter: &mut Interpreter<'_>, host: &mut H) { +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; @@ -66,7 +67,7 @@ pub fn extcodesize(interpreter: &mut Interpreter<'_>, host: } /// EIP-1052: EXTCODEHASH opcode -pub fn extcodehash(interpreter: &mut Interpreter<'_>, host: &mut H) { +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 { @@ -90,7 +91,7 @@ pub fn extcodehash(interpreter: &mut Interpreter<'_>, host: push_b256!(interpreter, code_hash); } -pub fn extcodecopy(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn extcodecopy(interpreter: &mut Interpreter, host: &mut H) { pop_address!(interpreter, address); pop!(interpreter, memory_offset, code_offset, len_u256); @@ -117,7 +118,7 @@ pub fn extcodecopy(interpreter: &mut Interpreter<'_>, host: .set_data(memory_offset, code_offset, len, code.bytes()); } -pub fn blockhash(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn blockhash(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BLOCKHASH); pop_top!(interpreter, number); @@ -136,7 +137,7 @@ pub fn blockhash(interpreter: &mut Interpreter<'_>, host: &mut H) { *number = U256::ZERO; } -pub fn sload(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn sload(interpreter: &mut Interpreter, host: &mut H) { pop!(interpreter, index); let Some((value, is_cold)) = host.sload(interpreter.contract.address, index) else { @@ -147,7 +148,7 @@ pub fn sload(interpreter: &mut Interpreter<'_>, host: &mut push!(interpreter, value); } -pub fn sstore(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn sstore(interpreter: &mut Interpreter, host: &mut H) { check_staticcall!(interpreter); pop!(interpreter, index, value); @@ -166,7 +167,7 @@ pub fn sstore(interpreter: &mut Interpreter<'_>, host: &mut /// EIP-1153: Transient storage opcodes /// Store value to transient storage -pub fn tstore(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn tstore(interpreter: &mut Interpreter, host: &mut H) { check!(interpreter, CANCUN); check_staticcall!(interpreter); gas!(interpreter, gas::WARM_STORAGE_READ_COST); @@ -178,7 +179,7 @@ pub fn tstore(interpreter: &mut Interpreter<'_>, host: &mut /// EIP-1153: Transient storage opcodes /// Load value from transient storage -pub fn tload(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn tload(interpreter: &mut Interpreter, host: &mut H) { check!(interpreter, CANCUN); gas!(interpreter, gas::WARM_STORAGE_READ_COST); @@ -187,7 +188,7 @@ pub fn tload(interpreter: &mut Interpreter<'_>, host: &mut *index = host.tload(interpreter.contract.address, *index); } -pub fn log(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn log(interpreter: &mut Interpreter, host: &mut H) { check_staticcall!(interpreter); pop!(interpreter, offset, len); @@ -217,7 +218,7 @@ pub fn log(interpreter: &mut Interpreter<'_>, host: &mu host.log(interpreter.contract.address, topics, data); } -pub fn selfdestruct(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn selfdestruct(interpreter: &mut Interpreter, host: &mut H) { check_staticcall!(interpreter); pop_address!(interpreter, target); @@ -235,11 +236,9 @@ pub fn selfdestruct(interpreter: &mut Interpreter<'_>, host interpreter.instruction_result = InstructionResult::SelfDestruct; } -#[inline(never)] -pub fn prepare_create_inputs( - interpreter: &mut Interpreter<'_>, +pub fn create( + interpreter: &mut Interpreter, host: &mut H, - create_inputs: &mut Option>, ) { check_staticcall!(interpreter); @@ -248,14 +247,11 @@ pub fn prepare_create_inputs( check!(interpreter, PETERSBURG); } - interpreter.return_data_buffer = Bytes::new(); - pop!(interpreter, value, code_offset, len); let len = as_usize_or_fail!(interpreter, len); - let code = if len == 0 { - Bytes::new() - } else { + 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 @@ -274,9 +270,10 @@ pub fn prepare_create_inputs( let code_offset = as_usize_or_fail!(interpreter, code_offset); shared_memory_resize!(interpreter, code_offset, len); - Bytes::copy_from_slice(interpreter.shared_memory.slice(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)); @@ -295,88 +292,54 @@ pub fn prepare_create_inputs( } gas!(interpreter, gas_limit); - *create_inputs = Some(Box::new(CreateInputs { - caller: interpreter.contract.address, - scheme, - value, - init_code: code, - gas_limit, - })); -} - -pub fn create( - interpreter: &mut Interpreter<'_>, - host: &mut H, -) { - let mut create_input: Option> = None; - prepare_create_inputs::(interpreter, host, &mut create_input); - - let Some(mut create_input) = create_input else { - return; - }; - - let (return_reason, address, gas, return_data) = - host.create(&mut create_input, interpreter.shared_memory); - - interpreter.return_data_buffer = match return_reason { - // Save data to return data buffer if the create reverted - return_revert!() => return_data, - // Otherwise clear it - _ => Bytes::new(), - }; - - match return_reason { - return_ok!() => { - push_b256!(interpreter, address.unwrap_or_default().into_word()); - - if crate::USE_GAS { - interpreter.gas.erase_cost(gas.remaining()); - interpreter.gas.record_refund(gas.refunded()); - } - } - return_revert!() => { - push_b256!(interpreter, B256::ZERO); - - if crate::USE_GAS { - interpreter.gas.erase_cost(gas.remaining()); - } - } - InstructionResult::FatalExternalError => { - interpreter.instruction_result = InstructionResult::FatalExternalError; - } - _ => { - push_b256!(interpreter, B256::ZERO); - } - } + // Call host to interact with target contract + interpreter.next_action = Some(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) { +pub fn call(interpreter: &mut Interpreter, host: &mut H) { call_inner::(CallScheme::Call, interpreter, host); } -pub fn call_code(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn call_code(interpreter: &mut Interpreter, host: &mut H) { call_inner::(CallScheme::CallCode, interpreter, host); } -pub fn delegate_call(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn delegate_call(interpreter: &mut Interpreter, host: &mut H) { call_inner::(CallScheme::DelegateCall, interpreter, host); } -pub fn static_call(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn static_call(interpreter: &mut Interpreter, host: &mut H) { call_inner::(CallScheme::StaticCall, interpreter, host); } -#[inline(never)] -fn prepare_call_inputs( - interpreter: &mut Interpreter<'_>, +pub fn call_inner( scheme: CallScheme, + interpreter: &mut Interpreter, host: &mut H, - result_len: &mut usize, - result_offset: &mut usize, - result_call_inputs: &mut Option>, ) { + match scheme { + // EIP-7: DELEGATECALL + CallScheme::DelegateCall => check!(interpreter, HOMESTEAD), + // EIP-214: New opcode STATICCALL + CallScheme::StaticCall => check!(interpreter, BYZANTIUM), + _ => (), + } + pop!(interpreter, local_gas_limit); pop_address!(interpreter, to); + + // max gas limit is not possible in real ethereum situation. + // But for tests we would not like to fail on this. + // Gas limit for subcall is taken as min of this value and current gas limit. let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); let value = match scheme { @@ -406,10 +369,10 @@ fn prepare_call_inputs( Bytes::new() }; - *result_len = as_usize_or_fail!(interpreter, out_len); - *result_offset = if *result_len != 0 { + let out_len = as_usize_or_fail!(interpreter, out_len); + let out_offset = if out_len != 0 { let out_offset = as_usize_or_fail!(interpreter, out_offset); - shared_memory_resize!(interpreter, out_offset, *result_len); + shared_memory_resize!(interpreter, out_offset, out_len); out_offset } else { usize::MAX //unrealistic value so we are sure it is not used @@ -439,24 +402,24 @@ fn prepare_call_inputs( }, }; - let transfer = if scheme == CallScheme::Call { - Transfer { + let transfer = match scheme { + CallScheme::Call => Transfer { source: interpreter.contract.address, target: to, value, - } - } else if scheme == CallScheme::CallCode { - Transfer { + }, + CallScheme::CallCode => Transfer { source: interpreter.contract.address, target: interpreter.contract.address, value, - } - } else { - //this is dummy send for StaticCall and DelegateCall, it should do nothing and dont touch anything. - Transfer { - source: interpreter.contract.address, - target: interpreter.contract.address, - value: U256::ZERO, + }, + _ => { + //this is dummy send for StaticCall and DelegateCall, it should do nothing and dont touch anything. + Transfer { + source: interpreter.contract.address, + target: interpreter.contract.address, + value: U256::ZERO, + } } }; @@ -470,7 +433,7 @@ fn prepare_call_inputs( gas!( interpreter, gas::call_cost::( - value, + value != U256::ZERO, is_new, is_cold, matches!(scheme, CallScheme::Call | CallScheme::CallCode), @@ -495,78 +458,17 @@ fn prepare_call_inputs( } let is_static = matches!(scheme, CallScheme::StaticCall) || interpreter.is_static; - *result_call_inputs = Some(Box::new(CallInputs { - contract: to, - transfer, - input, - gas_limit, - context, - is_static, - })); -} - -pub fn call_inner( - scheme: CallScheme, - interpreter: &mut Interpreter<'_>, - host: &mut H, -) { - match scheme { - // EIP-7: DELEGATECALL - CallScheme::DelegateCall => check!(interpreter, HOMESTEAD), - // EIP-214: New opcode STATICCALL - CallScheme::StaticCall => check!(interpreter, BYZANTIUM), - _ => (), - } - interpreter.return_data_buffer = Bytes::new(); - - let mut out_offset: usize = 0; - let mut out_len: usize = 0; - let mut call_input: Option> = None; - prepare_call_inputs::( - interpreter, - scheme, - host, - &mut out_len, - &mut out_offset, - &mut call_input, - ); - - let Some(mut call_input) = call_input else { - return; - }; - // Call host to interact with target contract - let (reason, gas, return_data) = host.call(&mut call_input, interpreter.shared_memory); - - interpreter.return_data_buffer = return_data; - let target_len = min(out_len, interpreter.return_data_buffer.len()); - - match reason { - return_ok!() => { - // return unspend gas. - if crate::USE_GAS { - interpreter.gas.erase_cost(gas.remaining()); - interpreter.gas.record_refund(gas.refunded()); - } - interpreter - .shared_memory - .set(out_offset, &interpreter.return_data_buffer[..target_len]); - push!(interpreter, U256::from(1)); - } - return_revert!() => { - if crate::USE_GAS { - interpreter.gas.erase_cost(gas.remaining()); - } - interpreter - .shared_memory - .set(out_offset, &interpreter.return_data_buffer[..target_len]); - push!(interpreter, U256::ZERO); - } - InstructionResult::FatalExternalError => { - interpreter.instruction_result = InstructionResult::FatalExternalError; - } - _ => { - push!(interpreter, U256::ZERO); - } - } + interpreter.next_action = Some(InterpreterAction::SubCall { + inputs: Box::new(CallInputs { + contract: to, + transfer, + input, + gas_limit, + context, + is_static, + }), + return_memory_offset: out_offset..out_offset + out_len, + }); + interpreter.instruction_result = InstructionResult::CallOrCreate; } diff --git a/crates/interpreter/src/instructions/host_env.rs b/crates/interpreter/src/instructions/host_env.rs index 6f964e0ccc..cce993bac6 100644 --- a/crates/interpreter/src/instructions/host_env.rs +++ b/crates/interpreter/src/instructions/host_env.rs @@ -5,28 +5,28 @@ use crate::{ }; /// EIP-1344: ChainID opcode -pub fn chainid(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn chainid(interpreter: &mut Interpreter, host: &mut H) { check!(interpreter, ISTANBUL); gas!(interpreter, gas::BASE); push!(interpreter, U256::from(host.env().cfg.chain_id)); } -pub fn coinbase(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn coinbase(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); push_b256!(interpreter, host.env().block.coinbase.into_word()); } -pub fn timestamp(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn timestamp(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); push!(interpreter, host.env().block.timestamp); } -pub fn number(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn number(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); push!(interpreter, host.env().block.number); } -pub fn difficulty(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn difficulty(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); if SPEC::enabled(MERGE) { push_b256!(interpreter, host.env().block.prevrandao.unwrap()); @@ -35,30 +35,30 @@ pub fn difficulty(interpreter: &mut Interpreter<'_>, host: } } -pub fn gaslimit(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn gaslimit(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); push!(interpreter, host.env().block.gas_limit); } -pub fn gasprice(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn gasprice(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); push!(interpreter, host.env().effective_gas_price()); } /// EIP-3198: BASEFEE opcode -pub fn basefee(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn basefee(interpreter: &mut Interpreter, host: &mut H) { check!(interpreter, LONDON); gas!(interpreter, gas::BASE); push!(interpreter, host.env().block.basefee); } -pub fn origin(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn origin(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BASE); push_b256!(interpreter, host.env().tx.caller.into_word()); } // EIP-4844: Shard Blob Transactions -pub fn blob_hash(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn blob_hash(interpreter: &mut Interpreter, host: &mut H) { check!(interpreter, CANCUN); gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, index); @@ -70,7 +70,7 @@ pub fn blob_hash(interpreter: &mut Interpreter<'_>, host: & } /// EIP-7516: BLOBBASEFEE opcode -pub fn blob_basefee(interpreter: &mut Interpreter<'_>, host: &mut H) { +pub fn blob_basefee(interpreter: &mut Interpreter, host: &mut H) { check!(interpreter, CANCUN); gas!(interpreter, gas::BASE); push!( diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index 1207c0b392..ae13ee4644 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -19,32 +19,26 @@ macro_rules! check { macro_rules! gas { ($interp:expr, $gas:expr) => { - if crate::USE_GAS { - if !$interp.gas.record_cost($gas) { - $interp.instruction_result = InstructionResult::OutOfGas; - return; - } + if !$interp.gas.record_cost($gas) { + $interp.instruction_result = InstructionResult::OutOfGas; + return; } }; } macro_rules! refund { ($interp:expr, $gas:expr) => { - if crate::USE_GAS { - $interp.gas.record_refund($gas); - } + $interp.gas.record_refund($gas) }; } macro_rules! gas_or_fail { ($interp:expr, $gas:expr) => { - if crate::USE_GAS { - match $gas { - Some(gas_used) => gas!($interp, gas_used), - None => { - $interp.instruction_result = InstructionResult::OutOfGas; - return; - } + match $gas { + Some(gas_used) => gas!($interp, gas_used), + None => { + $interp.instruction_result = InstructionResult::OutOfGas; + return; } } }; @@ -62,12 +56,10 @@ macro_rules! shared_memory_resize { } if new_size > $interp.shared_memory.len() { - if crate::USE_GAS { - let num_bytes = new_size / 32; - if !$interp.gas.record_memory(crate::gas::memory_gas(num_bytes)) { - $interp.instruction_result = InstructionResult::MemoryLimitOOG; - return; - } + let num_bytes = new_size / 32; + if !$interp.gas.record_memory(crate::gas::memory_gas(num_bytes)) { + $interp.instruction_result = InstructionResult::MemoryLimitOOG; + return; } $interp.shared_memory.resize(new_size); } @@ -161,6 +153,7 @@ macro_rules! pop_top { }; } +#[macro_export] macro_rules! push_b256 { ($interp:expr, $($x:expr),* $(,)?) => ($( match $interp.stack.push_b256($x) { @@ -173,6 +166,7 @@ macro_rules! push_b256 { )*) } +#[macro_export] macro_rules! push { ($interp:expr, $($x:expr),* $(,)?) => ($( match $interp.stack.push($x) { diff --git a/crates/interpreter/src/instructions/memory.rs b/crates/interpreter/src/instructions/memory.rs index faa9924416..6a63eb6b03 100644 --- a/crates/interpreter/src/instructions/memory.rs +++ b/crates/interpreter/src/instructions/memory.rs @@ -5,7 +5,7 @@ use crate::{ }; use core::cmp::max; -pub fn mload(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn mload(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop!(interpreter, index); let index = as_usize_or_fail!(interpreter, index); @@ -13,7 +13,7 @@ pub fn mload(interpreter: &mut Interpreter<'_>, _host: &mut H) { push!(interpreter, interpreter.shared_memory.get_u256(index)); } -pub fn mstore(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn mstore(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop!(interpreter, index, value); let index = as_usize_or_fail!(interpreter, index); @@ -21,7 +21,7 @@ pub fn mstore(interpreter: &mut Interpreter<'_>, _host: &mut H) { interpreter.shared_memory.set_u256(index, value); } -pub fn mstore8(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn mstore8(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop!(interpreter, index, value); let index = as_usize_or_fail!(interpreter, index); @@ -29,13 +29,13 @@ pub fn mstore8(interpreter: &mut Interpreter<'_>, _host: &mut H) { interpreter.shared_memory.set_byte(index, value.byte(0)) } -pub fn msize(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn msize(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::BASE); push!(interpreter, U256::from(interpreter.shared_memory.len())); } // EIP-5656: MCOPY - Memory copying instruction -pub fn mcopy(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn mcopy(interpreter: &mut Interpreter, _host: &mut H) { check!(interpreter, CANCUN); pop!(interpreter, dst, src, len); diff --git a/crates/interpreter/src/instructions/opcode.rs b/crates/interpreter/src/instructions/opcode.rs index f6ebb0a390..5e93c77d10 100644 --- a/crates/interpreter/src/instructions/opcode.rs +++ b/crates/interpreter/src/instructions/opcode.rs @@ -11,7 +11,7 @@ use alloc::sync::Arc; use core::fmt; /// EVM opcode function signature. -pub type Instruction = fn(&mut Interpreter<'_>, &mut H); +pub type Instruction = fn(&mut Interpreter, &mut H); /// Instruction table is list of instruction function pointers mapped to /// 256 EVM opcodes. @@ -21,7 +21,7 @@ pub type InstructionTable = [Instruction; 256]; pub type InstructionTableArc = Arc>; /// EVM opcode function signature. -pub type BoxedInstruction<'a, H> = Box, &mut H) + 'a>; +pub type BoxedInstruction<'a, H> = Box; /// A table of instructions. pub type BoxedInstructionTable<'a, H> = [BoxedInstruction<'a, H>; 256]; @@ -35,12 +35,20 @@ pub type BoxedInstructionTableArc<'a, H> = Arc>; /// Note that `Plain` variant gives us 10-20% faster Interpreter execution. /// /// Boxed variant can be used to wrap plain function pointer with closure. -#[derive(Clone)] pub enum InstructionTables<'a, H> { Plain(InstructionTableArc), Boxed(BoxedInstructionTableArc<'a, H>), } +impl<'a, H> Clone for InstructionTables<'a, H> { + fn clone(&self) -> Self { + match self { + Self::Plain(table) => Self::Plain(table.clone()), + Self::Boxed(table) => Self::Boxed(table.clone()), + } + } +} + macro_rules! opcodes { ($($val:literal => $name:ident => $f:expr),* $(,)?) => { // Constants for each opcode. This also takes care of duplicate names. @@ -67,7 +75,7 @@ macro_rules! opcodes { pub fn instruction(opcode: u8) -> Instruction { match opcode { $($name => $f,)* - _ => control::not_found, + _ => control::unknown, } } }; diff --git a/crates/interpreter/src/instructions/stack.rs b/crates/interpreter/src/instructions/stack.rs index ed8af5e483..6913116330 100644 --- a/crates/interpreter/src/instructions/stack.rs +++ b/crates/interpreter/src/instructions/stack.rs @@ -4,7 +4,7 @@ use crate::{ Host, InstructionResult, Interpreter, }; -pub fn pop(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn pop(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::BASE); if let Err(result) = interpreter.stack.pop() { interpreter.instruction_result = result; @@ -14,7 +14,7 @@ pub fn pop(interpreter: &mut Interpreter<'_>, _host: &mut H) { /// EIP-3855: PUSH0 instruction /// /// Introduce a new instruction which pushes the constant value 0 onto the stack. -pub fn push0(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn push0(interpreter: &mut Interpreter, _host: &mut H) { check!(interpreter, SHANGHAI); gas!(interpreter, gas::BASE); if let Err(result) = interpreter.stack.push(U256::ZERO) { @@ -22,7 +22,7 @@ pub fn push0(interpreter: &mut Interpreter<'_>, _host: &mut } } -pub fn push(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn push(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); // SAFETY: In analysis we append trailing bytes to the bytecode so that this is safe to do // without bounds checking. @@ -37,14 +37,14 @@ pub fn push(interpreter: &mut Interpreter<'_>, _host: & interpreter.instruction_pointer = unsafe { ip.add(N) }; } -pub fn dup(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn dup(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); if let Err(result) = interpreter.stack.dup::() { interpreter.instruction_result = result; } } -pub fn swap(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn swap(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); if let Err(result) = interpreter.stack.swap::() { interpreter.instruction_result = result; diff --git a/crates/interpreter/src/instructions/system.rs b/crates/interpreter/src/instructions/system.rs index 887a640489..9ae285bb1e 100644 --- a/crates/interpreter/src/instructions/system.rs +++ b/crates/interpreter/src/instructions/system.rs @@ -4,7 +4,7 @@ use crate::{ Host, InstructionResult, Interpreter, }; -pub fn keccak256(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn keccak256(interpreter: &mut Interpreter, _host: &mut H) { pop!(interpreter, from, len); let len = as_usize_or_fail!(interpreter, len); gas_or_fail!(interpreter, gas::keccak256_cost(len as u64)); @@ -19,22 +19,22 @@ pub fn keccak256(interpreter: &mut Interpreter<'_>, _host: &mut H) { push_b256!(interpreter, hash); } -pub fn address(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn address(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::BASE); push_b256!(interpreter, interpreter.contract.address.into_word()); } -pub fn caller(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn caller(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::BASE); push_b256!(interpreter, interpreter.contract.caller.into_word()); } -pub fn codesize(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn codesize(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::BASE); push!(interpreter, U256::from(interpreter.contract.bytecode.len())); } -pub fn codecopy(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn codecopy(interpreter: &mut Interpreter, _host: &mut H) { pop!(interpreter, memory_offset, code_offset, len); let len = as_usize_or_fail!(interpreter, len); gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64)); @@ -54,7 +54,7 @@ pub fn codecopy(interpreter: &mut Interpreter<'_>, _host: &mut H) { ); } -pub fn calldataload(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn calldataload(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop!(interpreter, index); let index = as_usize_saturated!(index); @@ -70,17 +70,17 @@ pub fn calldataload(interpreter: &mut Interpreter<'_>, _host: &mut H) { push_b256!(interpreter, load); } -pub fn calldatasize(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn calldatasize(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::BASE); push!(interpreter, U256::from(interpreter.contract.input.len())); } -pub fn callvalue(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn callvalue(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::BASE); push!(interpreter, interpreter.contract.value); } -pub fn calldatacopy(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn calldatacopy(interpreter: &mut Interpreter, _host: &mut H) { pop!(interpreter, memory_offset, data_offset, len); let len = as_usize_or_fail!(interpreter, len); gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64)); @@ -101,7 +101,7 @@ pub fn calldatacopy(interpreter: &mut Interpreter<'_>, _host: &mut H) { } /// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY -pub fn returndatasize(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn returndatasize(interpreter: &mut Interpreter, _host: &mut H) { check!(interpreter, BYZANTIUM); gas!(interpreter, gas::BASE); push!( @@ -111,7 +111,7 @@ pub fn returndatasize(interpreter: &mut Interpreter<'_>, _h } /// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY -pub fn returndatacopy(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn returndatacopy(interpreter: &mut Interpreter, _host: &mut H) { check!(interpreter, BYZANTIUM); pop!(interpreter, memory_offset, offset, len); let len = as_usize_or_fail!(interpreter, len); @@ -132,7 +132,7 @@ pub fn returndatacopy(interpreter: &mut Interpreter<'_>, _h } } -pub fn gas(interpreter: &mut Interpreter<'_>, _host: &mut H) { +pub fn gas(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::BASE); push!(interpreter, U256::from(interpreter.gas.remaining())); } diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index e4ce89f7ad..b152419a16 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -9,8 +9,16 @@ pub use shared_memory::{next_multiple_of_32, SharedMemory}; pub use stack::{Stack, STACK_LIMIT}; use crate::primitives::Bytes; -use crate::{Gas, Host, InstructionResult}; +use crate::{ + push, push_b256, return_ok, return_revert, CallInputs, CreateInputs, Gas, Host, + InstructionResult, +}; use alloc::boxed::Box; +use core::cmp::min; +use core::ops::Range; +use revm_primitives::{Address, U256}; + +pub use self::shared_memory::EMPTY_SHARED_MEMORY; /// EIP-170: Contract code size limit /// @@ -21,7 +29,7 @@ pub const MAX_CODE_SIZE: usize = 0x6000; pub const MAX_INITCODE_SIZE: usize = 2 * MAX_CODE_SIZE; #[derive(Debug)] -pub struct Interpreter<'a> { +pub struct Interpreter { /// Contract information and invoking data pub contract: Box, /// The current instruction pointer. @@ -32,29 +40,55 @@ pub struct Interpreter<'a> { /// The gas state. pub gas: Gas, /// Shared memory. - pub shared_memory: &'a mut SharedMemory, + /// + /// Note: This field is only set while running the interpreter loop. + /// Otherwise it is taken and replaced with empty shared memory. + pub shared_memory: SharedMemory, /// Stack. pub stack: Stack, /// The return data buffer for internal calls. - pub return_data_buffer: Bytes, - /// The offset into `self.memory` of the return data. + /// It has multi usage: /// - /// This value must be ignored if `self.return_len` is 0. - pub return_offset: usize, - /// The length of the return data. - pub return_len: usize, + /// * It contains the output bytes of call sub call. + /// * When this interpreter finishes execution it contains the output bytes of this contract. + pub return_data_buffer: Bytes, /// Whether the interpreter is in "staticcall" mode, meaning no state changes can happen. pub is_static: bool, + /// Actions that the EVM should do. + /// + /// Set inside CALL or CREATE instructions and RETURN or REVERT instructions. Additionally those instructions will set + /// InstructionResult to CallOrCreate/Return/Revert so we know the reason. + pub next_action: Option, +} + +#[derive(Debug, Clone)] +pub struct InterpreterResult { + pub result: InstructionResult, + pub output: Bytes, + pub gas: Gas, } -impl<'a> Interpreter<'a> { +#[derive(Debug, Clone)] +pub enum InterpreterAction { + SubCall { + /// 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 { + inputs: Box, + }, + Return { + result: InterpreterResult, + }, +} + +impl Interpreter { /// Create new interpreter - pub fn new( - contract: Box, - gas_limit: u64, - is_static: bool, - shared_memory: &'a mut SharedMemory, - ) -> Self { + pub fn new(contract: Box, gas_limit: u64, is_static: bool) -> Self { Self { instruction_pointer: contract.bytecode.as_ptr(), contract, @@ -62,10 +96,77 @@ impl<'a> Interpreter<'a> { instruction_result: InstructionResult::Continue, is_static, return_data_buffer: Bytes::new(), - return_len: 0, - return_offset: 0, - shared_memory, + shared_memory: EMPTY_SHARED_MEMORY, stack: Stack::new(), + next_action: None, + } + } + + /// When sub create call returns we can insert output of that call into this interpreter. + pub fn insert_create_output(&mut self, result: InterpreterResult, address: Option
) { + let interpreter = self; + interpreter.return_data_buffer = match result.result { + // Save data to return data buffer if the create reverted + return_revert!() => result.output, + // Otherwise clear it + _ => Bytes::new(), + }; + + match result.result { + return_ok!() => { + push_b256!(interpreter, address.unwrap_or_default().into_word()); + interpreter.gas.erase_cost(result.gas.remaining()); + interpreter.gas.record_refund(result.gas.refunded()); + } + return_revert!() => { + push!(interpreter, U256::ZERO); + interpreter.gas.erase_cost(result.gas.remaining()); + } + InstructionResult::FatalExternalError => { + interpreter.instruction_result = InstructionResult::FatalExternalError; + } + _ => { + push!(interpreter, U256::ZERO); + } + } + } + + /// When sub call returns we can insert output of that call into this interpreter. + /// + /// Note that shared memory is required as a input field. + /// As SharedMemory inside Interpreter is taken and replaced with empty (not valid) memory. + pub fn insert_call_output( + &mut self, + shared_memory: &mut SharedMemory, + result: InterpreterResult, + memory_return_offset: Range, + ) { + let out_offset = memory_return_offset.start; + let out_len = memory_return_offset.len(); + + let interpreter = self; + interpreter.return_data_buffer = result.output; + let target_len = min(out_len, interpreter.return_data_buffer.len()); + + match result.result { + return_ok!() => { + // return unspend gas. + interpreter.gas.erase_cost(result.gas.remaining()); + interpreter.gas.record_refund(result.gas.refunded()); + shared_memory.set(out_offset, &interpreter.return_data_buffer[..target_len]); + push!(interpreter, U256::from(1)); + } + return_revert!() => { + interpreter.gas.erase_cost(result.gas.remaining()); + shared_memory.set(out_offset, &interpreter.return_data_buffer[..target_len]); + push!(interpreter, U256::ZERO); + } + InstructionResult::FatalExternalError => { + interpreter.instruction_result = InstructionResult::FatalExternalError; + } + _ => { + push!(interpreter, U256::ZERO); + } } } @@ -105,10 +206,12 @@ impl<'a> Interpreter<'a> { } /// Executes the instruction at the current instruction pointer. + /// + /// Internally it will increment instruction pointer by one. #[inline(always)] - pub fn step(&mut self, instruction_table: &[FN; 256], host: &mut H) + fn step(&mut self, instruction_table: &[FN; 256], host: &mut H) where - FN: Fn(&mut Interpreter<'_>, &mut H), + FN: Fn(&mut Interpreter, &mut H), { // Get current opcode. let opcode = unsafe { *self.instruction_pointer }; @@ -122,35 +225,41 @@ impl<'a> Interpreter<'a> { (instruction_table[opcode as usize])(self, host) } + /// Take memory and replace it with empty memory. + pub fn take_memory(&mut self) -> SharedMemory { + core::mem::replace(&mut self.shared_memory, EMPTY_SHARED_MEMORY) + } + /// Executes the interpreter until it returns or stops. pub fn run( &mut self, + shared_memory: SharedMemory, instruction_table: &[FN; 256], host: &mut H, - ) -> InstructionResult + ) -> InterpreterAction where - FN: Fn(&mut Interpreter<'_>, &mut H), + FN: Fn(&mut Interpreter, &mut H), { + self.next_action = None; + self.instruction_result = InstructionResult::Continue; + self.shared_memory = shared_memory; + // main loop while self.instruction_result == InstructionResult::Continue { self.step(instruction_table, host); } - self.instruction_result - } - /// Returns a copy of the interpreter's return value, if any. - #[inline] - pub fn return_value(&self) -> Bytes { - self.return_value_slice().to_vec().into() - } - - /// Returns a reference to the interpreter's return value, if any. - #[inline] - pub fn return_value_slice(&self) -> &[u8] { - if self.return_len == 0 { - &[] - } else { - self.shared_memory - .slice(self.return_offset, self.return_len) + // Return next action if it is some. + if let Some(action) = self.next_action.take() { + return action; + } + // If not, return action without output. + InterpreterAction::Return { + result: InterpreterResult { + result: self.instruction_result, + // return empty bytecode + output: Bytes::new(), + gas: self.gas, + }, } } } diff --git a/crates/interpreter/src/interpreter/shared_memory.rs b/crates/interpreter/src/interpreter/shared_memory.rs index a3f346cd7a..23756ba2bd 100644 --- a/crates/interpreter/src/interpreter/shared_memory.rs +++ b/crates/interpreter/src/interpreter/shared_memory.rs @@ -26,6 +26,17 @@ pub struct SharedMemory { memory_limit: u64, } +/// Empty shared memory. +/// +/// Used as placeholder inside Interpreter when it is not running. +pub const EMPTY_SHARED_MEMORY: SharedMemory = SharedMemory { + buffer: Vec::new(), + checkpoints: Vec::new(), + last_checkpoint: 0, + #[cfg(feature = "memory_limit")] + memory_limit: u64::MAX, +}; + impl fmt::Debug for SharedMemory { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SharedMemory") diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index 5ec60baa55..a7ac0f248d 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -18,8 +18,6 @@ mod instruction_result; pub mod instructions; mod interpreter; -pub(crate) const USE_GAS: bool = !cfg!(feature = "no_gas_measuring"); - // Reexport primary types. pub use gas::Gas; pub use host::{DummyHost, Host}; @@ -27,8 +25,9 @@ pub use inner_models::*; pub use instruction_result::*; pub use instructions::{opcode, Instruction, OpCode, OPCODE_JUMPMAP}; pub use interpreter::{ - analysis, next_multiple_of_32, BytecodeLocked, Contract, Interpreter, SharedMemory, Stack, - MAX_CODE_SIZE, MAX_INITCODE_SIZE, STACK_LIMIT, + analysis, next_multiple_of_32, BytecodeLocked, Contract, Interpreter, InterpreterAction, + InterpreterResult, SharedMemory, Stack, EMPTY_SHARED_MEMORY, MAX_CODE_SIZE, MAX_INITCODE_SIZE, + STACK_LIMIT, }; #[doc(hidden)] pub use revm_primitives as primitives; diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 65d6bcd7cd..ca779c28b5 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -68,7 +68,6 @@ dev = [ "optional_beneficiary_reward", ] memory_limit = [] -no_gas_measuring = [] optional_balance_check = [] optional_block_gas_limit = [] optional_eip3607 = [] diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 03e952474c..f73148c630 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -54,7 +54,6 @@ dev = [ "optional_beneficiary_reward", ] memory_limit = ["revm-interpreter/memory_limit"] -no_gas_measuring = ["revm-interpreter/no_gas_measuring"] optional_balance_check = ["revm-interpreter/optional_balance_check"] optional_block_gas_limit = ["revm-interpreter/optional_block_gas_limit"] optional_eip3607 = ["revm-interpreter/optional_eip3607"] diff --git a/crates/revm/benches/bench.rs b/crates/revm/benches/bench.rs index 9b79237d47..988c2e9b4f 100644 --- a/crates/revm/benches/bench.rs +++ b/crates/revm/benches/bench.rs @@ -8,7 +8,7 @@ use revm::{ address, bytes, hex, BerlinSpec, Bytecode, BytecodeState, Bytes, TransactTo, U256, }, }; -use revm_interpreter::{opcode::make_instruction_table, SharedMemory}; +use revm_interpreter::{opcode::make_instruction_table, SharedMemory, EMPTY_SHARED_MEMORY}; use std::time::Duration; type Evm = revm::EVM; @@ -87,24 +87,22 @@ fn bench_transact(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { } fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { - let mut shared_memory = SharedMemory::new(); - g.bench_function("eval", |b| { let contract = Contract { input: evm.env.tx.data.clone(), bytecode: BytecodeLocked::try_from(evm.db.as_ref().unwrap().0.clone()).unwrap(), ..Default::default() }; + let mut shared_memory = SharedMemory::new(); let mut host = DummyHost::new(evm.env.clone()); let instruction_table = make_instruction_table::(); - b.iter(|| { - let mut interpreter = Interpreter::new( - Box::new(contract.clone()), - u64::MAX, - false, - &mut shared_memory, - ); - let res = interpreter.run(&instruction_table, &mut host); + b.iter(move || { + // replace memory with empty memory to use it inside interpreter. + // Later return memory back. + let temp = core::mem::replace(&mut shared_memory, EMPTY_SHARED_MEMORY); + let mut interpreter = Interpreter::new(Box::new(contract.clone()), u64::MAX, false); + let res = interpreter.run(temp, &instruction_table, &mut host); + shared_memory = interpreter.take_memory(); host.clear(); res }) diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index f423b16d57..be73045315 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,13 +1,9 @@ -use crate::primitives::{specification, EVMError, EVMResult, Env, ExecutionResult}; use crate::{ db::{Database, DatabaseCommit, DatabaseRef}, - evm_impl::{EVMImpl, Transact}, + evm_impl::{new_evm, Transact}, + primitives::{db::WrapDatabaseRef, EVMError, EVMResult, Env, ExecutionResult, ResultAndState}, Inspector, }; -use alloc::boxed::Box; -use revm_interpreter::primitives::db::WrapDatabaseRef; -use revm_interpreter::primitives::ResultAndState; -use revm_precompile::Precompiles; /// Struct that takes Database and enabled transact to update state directly to database. /// additionally it allows user to set all environment parameters. @@ -76,7 +72,7 @@ impl EVM { /// Do checks that could make transaction fail before call/create pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { if let Some(db) = self.db.as_mut() { - evm_inner::(&mut self.env, db, None).preverify_transaction() + new_evm::(&mut self.env, db, None).preverify_transaction() } else { panic!("Database needs to be set"); } @@ -86,7 +82,7 @@ impl EVM { /// state. pub fn transact_preverified(&mut self) -> EVMResult { if let Some(db) = self.db.as_mut() { - evm_inner::(&mut self.env, db, None).transact_preverified() + new_evm::(&mut self.env, db, None).transact_preverified() } else { panic!("Database needs to be set"); } @@ -95,7 +91,7 @@ impl EVM { /// Execute transaction without writing to DB, return change state. pub fn transact(&mut self) -> EVMResult { if let Some(db) = self.db.as_mut() { - evm_inner::(&mut self.env, db, None).transact() + new_evm::(&mut self.env, db, None).transact() } else { panic!("Database needs to be set"); } @@ -104,7 +100,7 @@ impl EVM { /// Execute transaction with given inspector, without wring to DB. Return change state. pub fn inspect>(&mut self, mut inspector: INSP) -> EVMResult { if let Some(db) = self.db.as_mut() { - evm_inner::(&mut self.env, db, Some(&mut inspector)).transact() + new_evm::(&mut self.env, db, Some(&mut inspector)).transact() } else { panic!("Database needs to be set"); } @@ -115,7 +111,7 @@ impl<'a, DB: DatabaseRef> EVM { /// Do checks that could make transaction fail before call/create pub fn preverify_transaction_ref(&self) -> Result<(), EVMError> { if let Some(db) = self.db.as_ref() { - evm_inner::<_>(&mut self.env.clone(), &mut WrapDatabaseRef(db), None) + new_evm::<_>(&mut self.env.clone(), &mut WrapDatabaseRef(db), None) .preverify_transaction() } else { panic!("Database needs to be set"); @@ -126,7 +122,7 @@ impl<'a, DB: DatabaseRef> EVM { /// without writing to DB, return change state. pub fn transact_preverified_ref(&self) -> EVMResult { if let Some(db) = self.db.as_ref() { - evm_inner::<_>(&mut self.env.clone(), &mut WrapDatabaseRef(db), None) + new_evm::<_>(&mut self.env.clone(), &mut WrapDatabaseRef(db), None) .transact_preverified() } else { panic!("Database needs to be set"); @@ -136,7 +132,7 @@ impl<'a, DB: DatabaseRef> EVM { /// Execute transaction without writing to DB, return change state. pub fn transact_ref(&self) -> EVMResult { if let Some(db) = self.db.as_ref() { - evm_inner::<_>(&mut self.env.clone(), &mut WrapDatabaseRef(db), None).transact() + new_evm::<_>(&mut self.env.clone(), &mut WrapDatabaseRef(db), None).transact() } else { panic!("Database needs to be set"); } @@ -148,7 +144,7 @@ impl<'a, DB: DatabaseRef> EVM { mut inspector: I, ) -> EVMResult { if let Some(db) = self.db.as_ref() { - evm_inner::<_>( + new_evm( &mut self.env.clone(), &mut WrapDatabaseRef(db), Some(&mut inspector), @@ -183,43 +179,3 @@ impl EVM { core::mem::take(&mut self.db).unwrap() } } - -pub fn evm_inner<'a, DB: Database>( - env: &'a mut Env, - db: &'a mut DB, - insp: Option<&'a mut dyn Inspector>, -) -> Box + 'a> { - macro_rules! create_evm { - ($spec:ident) => { - Box::new(EVMImpl::<'a, $spec, DB>::new( - db, - env, - insp, - Precompiles::new(revm_precompile::SpecId::from_spec_id($spec::SPEC_ID)).clone(), - )) as Box + 'a> - }; - } - - use specification::*; - match env.cfg.spec_id { - SpecId::FRONTIER | SpecId::FRONTIER_THAWING => create_evm!(FrontierSpec), - SpecId::HOMESTEAD | SpecId::DAO_FORK => create_evm!(HomesteadSpec), - SpecId::TANGERINE => create_evm!(TangerineSpec), - SpecId::SPURIOUS_DRAGON => create_evm!(SpuriousDragonSpec), - SpecId::BYZANTIUM => create_evm!(ByzantiumSpec), - SpecId::PETERSBURG | SpecId::CONSTANTINOPLE => create_evm!(PetersburgSpec), - SpecId::ISTANBUL | SpecId::MUIR_GLACIER => create_evm!(IstanbulSpec), - SpecId::BERLIN => create_evm!(BerlinSpec), - SpecId::LONDON | SpecId::ARROW_GLACIER | SpecId::GRAY_GLACIER => { - create_evm!(LondonSpec) - } - SpecId::MERGE => create_evm!(MergeSpec), - SpecId::SHANGHAI => create_evm!(ShanghaiSpec), - SpecId::CANCUN => create_evm!(CancunSpec), - SpecId::LATEST => create_evm!(LatestSpec), - #[cfg(feature = "optimism")] - SpecId::BEDROCK => create_evm!(BedrockSpec), - #[cfg(feature = "optimism")] - SpecId::REGOLITH => create_evm!(RegolithSpec), - } -} diff --git a/crates/revm/src/evm_context.rs b/crates/revm/src/evm_context.rs index aeb8f15291..d0b47be655 100644 --- a/crates/revm/src/evm_context.rs +++ b/crates/revm/src/evm_context.rs @@ -1,11 +1,23 @@ -use crate::db::Database; -use crate::journaled_state::JournaledState; -use crate::primitives::{Address, Bytecode, EVMError, Env, B256, U256}; -use revm_precompile::Precompiles; +use crate::{ + db::Database, + interpreter::{ + analysis::to_analysed, gas, return_ok, CallInputs, Contract, CreateInputs, Gas, + InstructionResult, Interpreter, InterpreterResult, MAX_CODE_SIZE, + }, + journaled_state::JournaledState, + precompile::{Precompile, Precompiles}, + primitives::{ + keccak256, Address, AnalysisKind, Bytecode, Bytes, EVMError, Env, Spec, SpecId::*, B256, + U256, + }, + CallStackFrame, CALL_STACK_LIMIT, +}; +use alloc::boxed::Box; +use core::ops::Range; /// EVM Data contains all the data that EVM needs to execute. #[derive(Debug)] -pub struct EVMData<'a, DB: Database> { +pub struct EvmContext<'a, DB: Database> { /// EVM Environment contains all the information about config, block and transaction that /// evm needs. pub env: &'a mut Env, @@ -22,8 +34,8 @@ pub struct EVMData<'a, DB: Database> { pub l1_block_info: Option, } -impl<'a, DB: Database> EVMData<'a, DB> { - /// Load access list for berlin hardfork. +impl<'a, DB: Database> EvmContext<'a, DB> { + /// Load access list for berlin hard fork. /// /// Loading of accounts/storages is needed to make them warm. #[inline] @@ -121,4 +133,302 @@ impl<'a, DB: Database> EVMData<'a, DB> { pub fn tstore(&mut self, address: Address, index: U256, value: U256) { self.journaled_state.tstore(address, index, value) } + + /// Make create frame. + pub fn make_create_frame( + &mut self, + inputs: &CreateInputs, + ) -> Result, InterpreterResult> { + // Prepare crate. + let gas = Gas::new(inputs.gas_limit); + + let return_error = |e| { + Err(InterpreterResult { + result: e, + gas, + output: Bytes::new(), + }) + }; + + // Check depth + if self.journaled_state.depth() > CALL_STACK_LIMIT { + return return_error(InstructionResult::CallTooDeep); + } + + // Fetch balance of caller. + let Some((caller_balance, _)) = self.balance(inputs.caller) else { + return return_error(InstructionResult::FatalExternalError); + }; + + // Check if caller has enough balance to send to the created contract. + if caller_balance < inputs.value { + return return_error(InstructionResult::OutOfFund); + } + + // Increase nonce of caller and check if it overflows + let old_nonce; + if let Some(nonce) = self.journaled_state.inc_nonce(inputs.caller) { + old_nonce = nonce - 1; + } else { + return return_error(InstructionResult::Return); + } + + // Create address + let code_hash = keccak256(&inputs.init_code); + let created_address = inputs.created_address_with_hash(old_nonce, &code_hash); + + // Load account so it needs to be marked as warm for access list. + if self + .journaled_state + .load_account(created_address, self.db) + .map_err(|e| self.error = Some(e)) + .is_err() + { + return return_error(InstructionResult::FatalExternalError); + } + + // create account, transfer funds and make the journal checkpoint. + let checkpoint = match self.journaled_state.create_account_checkpoint::( + inputs.caller, + created_address, + inputs.value, + ) { + Ok(checkpoint) => checkpoint, + Err(e) => { + return return_error(e); + } + }; + + let bytecode = Bytecode::new_raw(inputs.init_code.clone()); + + let contract = Box::new(Contract::new( + Bytes::new(), + bytecode, + code_hash, + created_address, + inputs.caller, + inputs.value, + )); + + Ok(Box::new(CallStackFrame { + is_create: true, + checkpoint, + created_address: Some(created_address), + subcall_return_memory_range: 0..0, + interpreter: Interpreter::new(contract, gas.limit(), false), + })) + } + + /// Make call frame + pub fn make_call_frame( + &mut self, + inputs: &CallInputs, + return_memory_offset: Range, + ) -> Result, InterpreterResult> { + let gas = Gas::new(inputs.gas_limit); + + let return_result = |instruction_result: InstructionResult| { + Err(InterpreterResult { + result: instruction_result, + gas, + output: Bytes::new(), + }) + }; + + // Check depth + if self.journaled_state.depth() > CALL_STACK_LIMIT { + return return_result(InstructionResult::CallTooDeep); + } + + let account = match self.journaled_state.load_code(inputs.contract, self.db) { + Ok((account, _)) => account, + Err(e) => { + self.error = Some(e); + return return_result(InstructionResult::FatalExternalError); + } + }; + let code_hash = account.info.code_hash(); + let bytecode = account.info.code.clone().unwrap_or_default(); + + // Create subroutine checkpoint + let checkpoint = self.journaled_state.checkpoint(); + + // Touch address. For "EIP-158 State Clear", this will erase empty accounts. + if inputs.transfer.value == U256::ZERO { + self.load_account(inputs.context.address); + self.journaled_state.touch(&inputs.context.address); + } + + // Transfer value from caller to called account + if let Err(e) = self.journaled_state.transfer( + &inputs.transfer.source, + &inputs.transfer.target, + inputs.transfer.value, + self.db, + ) { + //println!("transfer error"); + self.journaled_state.checkpoint_revert(checkpoint); + return return_result(e); + } + + if let Some(precompile) = self.precompiles.get(&inputs.contract) { + //println!("Call precompile"); + let result = self.call_precompile(precompile, inputs, gas); + if matches!(result.result, return_ok!()) { + self.journaled_state.checkpoint_commit(); + } else { + self.journaled_state.checkpoint_revert(checkpoint); + } + Err(result) + } else if !bytecode.is_empty() { + let contract = Box::new(Contract::new_with_context( + inputs.input.clone(), + bytecode, + code_hash, + &inputs.context, + )); + // Create interpreter and execute subcall and push new CallStackFrame. + Ok(Box::new(CallStackFrame { + is_create: false, + checkpoint, + created_address: None, + subcall_return_memory_range: return_memory_offset, + interpreter: Interpreter::new(contract, gas.limit(), inputs.is_static), + })) + } else { + self.journaled_state.checkpoint_commit(); + return_result(InstructionResult::Stop) + } + } + + /// Call precompile contract + fn call_precompile( + &mut self, + precompile: Precompile, + inputs: &CallInputs, + gas: Gas, + ) -> InterpreterResult { + let input_data = &inputs.input; + + let out = match precompile { + Precompile::Standard(fun) => fun(input_data, gas.limit()), + Precompile::Env(fun) => fun(input_data, gas.limit(), self.env()), + }; + + let mut result = InterpreterResult { + result: InstructionResult::Return, + gas, + output: Bytes::new(), + }; + + match out { + Ok((gas_used, data)) => { + if result.gas.record_cost(gas_used) { + result.result = InstructionResult::Return; + result.output = Bytes::from(data); + } else { + result.result = InstructionResult::PrecompileOOG; + } + } + Err(e) => { + result.result = if crate::precompile::Error::OutOfGas == e { + InstructionResult::PrecompileOOG + } else { + InstructionResult::PrecompileError + }; + } + } + result + } + + /// Handles call return. + #[inline] + pub fn call_return( + &mut self, + interpreter_result: InterpreterResult, + frame: Box, + ) -> InterpreterResult { + // revert changes or not. + if matches!(interpreter_result.result, return_ok!()) { + self.journaled_state.checkpoint_commit(); + } else { + self.journaled_state.checkpoint_revert(frame.checkpoint); + } + interpreter_result + } + + /// Handles create return. + #[inline] + pub fn create_return( + &mut self, + mut interpreter_result: InterpreterResult, + frame: Box, + ) -> (InterpreterResult, Address) { + let address = frame.created_address.unwrap(); + // if return is not ok revert and return. + if !matches!(interpreter_result.result, return_ok!()) { + self.journaled_state.checkpoint_revert(frame.checkpoint); + return (interpreter_result, address); + } + // Host error if present on execution + // if ok, check contract creation limit and calculate gas deduction on output len. + // + // EIP-3541: Reject new contract code starting with the 0xEF byte + if SPEC::enabled(LONDON) + && !interpreter_result.output.is_empty() + && interpreter_result.output.first() == Some(&0xEF) + { + self.journaled_state.checkpoint_revert(frame.checkpoint); + interpreter_result.result = InstructionResult::CreateContractStartingWithEF; + return (interpreter_result, address); + } + + // EIP-170: Contract code size limit + // By default limit is 0x6000 (~25kb) + if SPEC::enabled(SPURIOUS_DRAGON) + && interpreter_result.output.len() + > self + .env + .cfg + .limit_contract_code_size + .unwrap_or(MAX_CODE_SIZE) + { + self.journaled_state.checkpoint_revert(frame.checkpoint); + interpreter_result.result = InstructionResult::CreateContractSizeLimit; + return (interpreter_result, frame.created_address.unwrap()); + } + let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT; + if !interpreter_result.gas.record_cost(gas_for_code) { + // record code deposit gas cost and check if we are out of gas. + // EIP-2 point 3: If contract creation does not have enough gas to pay for the + // final gas fee for adding the contract code to the state, the contract + // creation fails (i.e. goes out-of-gas) rather than leaving an empty contract. + if SPEC::enabled(HOMESTEAD) { + self.journaled_state.checkpoint_revert(frame.checkpoint); + interpreter_result.result = InstructionResult::OutOfGas; + return (interpreter_result, address); + } else { + interpreter_result.output = Bytes::new(); + } + } + // if we have enough gas we can commit changes. + self.journaled_state.checkpoint_commit(); + + // Do analysis of bytecode straight away. + let bytecode = match self.env.cfg.perf_analyse_created_bytecodes { + AnalysisKind::Raw => Bytecode::new_raw(interpreter_result.output.clone()), + AnalysisKind::Check => { + Bytecode::new_raw(interpreter_result.output.clone()).to_checked() + } + AnalysisKind::Analyse => { + to_analysed(Bytecode::new_raw(interpreter_result.output.clone())) + } + }; + + // set code + self.journaled_state.set_code(address, bytecode); + + interpreter_result.result = InstructionResult::Return; + (interpreter_result, address) + } } diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs index dd54c11248..4e95b4f5ea 100644 --- a/crates/revm/src/evm_impl.rs +++ b/crates/revm/src/evm_impl.rs @@ -3,24 +3,22 @@ use crate::{ handler::Handler, inspector_instruction, interpreter::{ - analysis::to_analysed, - gas, gas::initial_tx_gas, opcode::{make_boxed_instruction_table, make_instruction_table, InstructionTables}, - return_ok, CallContext, CallInputs, CallScheme, Contract, CreateInputs, Gas, Host, - InstructionResult, Interpreter, SelfDestructResult, SharedMemory, Transfer, MAX_CODE_SIZE, + CallContext, CallInputs, CallScheme, CreateInputs, Host, Interpreter, InterpreterAction, + InterpreterResult, SelfDestructResult, SharedMemory, Transfer, }, - journaled_state::{JournalCheckpoint, JournaledState}, - precompile::{self, Precompile, Precompiles}, + journaled_state::JournaledState, + precompile::Precompiles, primitives::{ - keccak256, Address, AnalysisKind, Bytecode, Bytes, EVMError, EVMResult, Env, - InvalidTransaction, Log, Output, Spec, SpecId::*, TransactTo, B256, U256, + specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Log, + Output, Spec, SpecId::*, TransactTo, B256, U256, }, - EVMData, Inspector, + CallStackFrame, EvmContext, Inspector, }; use alloc::{boxed::Box, sync::Arc, vec::Vec}; use auto_impl::auto_impl; -use core::{fmt, marker::PhantomData}; +use core::{fmt, marker::PhantomData, ops::Range}; #[cfg(feature = "optimism")] use crate::optimism; @@ -28,68 +26,29 @@ use crate::optimism; /// EVM call stack limit. pub const CALL_STACK_LIMIT: u64 = 1024; -pub struct EVMImpl<'a, GSPEC: Spec, DB: Database> { - pub data: EVMData<'a, DB>, +pub struct EVMImpl<'a, SPEC: Spec, DB: Database> { + pub context: EvmContext<'a, DB>, pub inspector: Option<&'a mut dyn Inspector>, pub instruction_table: InstructionTables<'a, Self>, pub handler: Handler, - _phantomdata: PhantomData, + _phantomdata: PhantomData, } -impl fmt::Debug for EVMImpl<'_, GSPEC, DB> +impl fmt::Debug for EVMImpl<'_, SPEC, DB> where - GSPEC: Spec, + SPEC: Spec, DB: Database + fmt::Debug, DB::Error: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("EVMImpl") - .field("data", &self.data) + .field("data", &self.context) .finish_non_exhaustive() } } -struct PreparedCreate { - gas: Gas, - created_address: Address, - checkpoint: JournalCheckpoint, - contract: Box, -} - -struct CreateResult { - result: InstructionResult, - created_address: Option
, - gas: Gas, - return_value: Bytes, -} - -struct PreparedCall { - gas: Gas, - checkpoint: JournalCheckpoint, - contract: Box, -} - -struct CallResult { - result: InstructionResult, - gas: Gas, - return_value: Bytes, -} - -/// EVM transaction interface. -#[auto_impl(&mut, Box)] -pub trait Transact { - /// Run checks that could make transaction fail before call/create. - fn preverify_transaction(&mut self) -> Result<(), EVMError>; - - /// Skip pre-verification steps and execute the transaction. - fn transact_preverified(&mut self) -> EVMResult; - - /// Execute transaction by running pre-verification steps and then transaction itself. - fn transact(&mut self) -> EVMResult; -} - #[cfg(feature = "optimism")] -impl<'a, GSPEC: Spec, DB: Database> EVMImpl<'a, GSPEC, DB> { +impl<'a, SPEC: Spec, DB: Database> EVMImpl<'a, SPEC, DB> { /// If the transaction is not a deposit transaction, subtract the L1 data fee from the /// caller's balance directly after minting the requested amount of ETH. fn remove_l1_cost( @@ -145,62 +104,47 @@ impl<'a, GSPEC: Spec, DB: Database> EVMImpl<'a, GSPEC, DB> { } } -impl<'a, GSPEC: Spec + 'static, DB: Database> Transact for EVMImpl<'a, GSPEC, DB> { - #[inline] - fn preverify_transaction(&mut self) -> Result<(), EVMError> { - self.preverify_transaction_inner() - } - - #[inline] - fn transact_preverified(&mut self) -> EVMResult { - let output = self.transact_preverified_inner(); - self.handler.end(&mut self.data, output) - } - - #[inline] - fn transact(&mut self) -> EVMResult { - let output = self - .preverify_transaction_inner() - .and_then(|()| self.transact_preverified_inner()); - self.handler.end(&mut self.data, output) - } -} - -impl<'a, GSPEC: Spec + 'static, DB: Database> EVMImpl<'a, GSPEC, DB> { - pub fn new( +impl<'a, SPEC: Spec + 'static, DB: Database> EVMImpl<'a, SPEC, DB> { + pub fn new_with_spec( db: &'a mut DB, env: &'a mut Env, inspector: Option<&'a mut dyn Inspector>, precompiles: Precompiles, ) -> Self { let journaled_state = JournaledState::new( - GSPEC::SPEC_ID, + SPEC::SPEC_ID, precompiles .addresses() .into_iter() .cloned() .collect::>(), ); + // If T is present it should be a generic T that modifies handler. let instruction_table = if inspector.is_some() { - let instruction_table = make_boxed_instruction_table::( - make_instruction_table::(), + let instruction_table = make_boxed_instruction_table::( + make_instruction_table::(), inspector_instruction, ); InstructionTables::Boxed(Arc::new(instruction_table)) } else { - InstructionTables::Plain(Arc::new(make_instruction_table::())) + InstructionTables::Plain(Arc::new(make_instruction_table::())) }; #[cfg(feature = "optimism")] - let handler = if env.cfg.optimism { - Handler::optimism::() + let mut handler = if env.cfg.optimism { + Handler::optimism::() } else { - Handler::mainnet::() + Handler::mainnet::() }; #[cfg(not(feature = "optimism"))] - let handler = Handler::mainnet::(); + let mut handler = Handler::mainnet::(); + + if env.cfg.is_beneficiary_reward_disabled() { + // do nothing + handler.reward_beneficiary = |_, _| Ok(()); + } Self { - data: EVMData { + context: EvmContext { env, journaled_state, db, @@ -216,15 +160,214 @@ impl<'a, GSPEC: Spec + 'static, DB: Database> EVMImpl<'a, GSPEC, DB> { } } + #[inline] + pub fn run( + &mut self, + instruction_table: &[FN; 256], + first_frame: Box, + ) -> InterpreterResult + 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.env.cfg.memory_limit); + #[cfg(not(feature = "memory_limit"))] + let mut shared_memory = SharedMemory::new(); + + shared_memory.new_context(); + + let mut stack_frame = call_stack.first_mut().unwrap(); + + loop { + // run interpreter + let action = stack_frame + .interpreter + .run(shared_memory, instruction_table, self); + // take shared memory back. + shared_memory = stack_frame.interpreter.take_memory(); + + let new_frame = match action { + InterpreterAction::SubCall { + inputs, + return_memory_offset, + } => self.handle_sub_call( + inputs, + stack_frame, + return_memory_offset, + &mut shared_memory, + ), + InterpreterAction::Create { inputs } => self.handle_sub_create(inputs, stack_frame), + InterpreterAction::Return { result } => { + // free memory context. + shared_memory.free_context(); + + let child = call_stack.pop().unwrap(); + let parent = call_stack.last_mut(); + + if let Some(result) = + self.handle_frame_return(child, parent, &mut shared_memory, result) + { + return result; + } + stack_frame = call_stack.last_mut().unwrap(); + continue; + } + }; + if let Some(new_frame) = new_frame { + shared_memory.new_context(); + call_stack.push(new_frame); + } + stack_frame = call_stack.last_mut().unwrap(); + } + } + + fn handle_frame_return( + &mut self, + mut child_stack_frame: Box, + parent_stack_frame: Option<&mut Box>, + shared_memory: &mut SharedMemory, + mut result: InterpreterResult, + ) -> Option { + if let Some(inspector) = self.inspector.as_mut() { + result = if child_stack_frame.is_create { + let (result, address) = inspector.create_end( + &mut self.context, + result, + child_stack_frame.created_address, + ); + child_stack_frame.created_address = address; + result + } else { + inspector.call_end(&mut self.context, result) + }; + } + + // break from loop if this is last CallStackFrame. + let Some(parent_stack_frame) = parent_stack_frame else { + let result = if child_stack_frame.is_create { + self.context + .create_return::(result, child_stack_frame) + .0 + } else { + self.context.call_return(result, child_stack_frame) + }; + + return Some(result); + }; + + if child_stack_frame.is_create { + let (result, address) = self + .context + .create_return::(result, child_stack_frame); + parent_stack_frame + .interpreter + .insert_create_output(result, Some(address)) + } else { + let subcall_memory_return_offset = + child_stack_frame.subcall_return_memory_range.clone(); + let result = self.context.call_return(result, child_stack_frame); + + parent_stack_frame.interpreter.insert_call_output( + shared_memory, + result, + subcall_memory_return_offset, + ) + } + None + } + + /// Handle Action for new sub create call, return None if there is no need + /// to add new stack frame. + #[inline] + fn handle_sub_create( + &mut self, + mut inputs: Box, + curent_stack_frame: &mut CallStackFrame, + ) -> Option> { + // Call inspector if it is some. + if let Some(inspector) = self.inspector.as_mut() { + if let Some((result, address)) = inspector.create(&mut self.context, &mut inputs) { + curent_stack_frame + .interpreter + .insert_create_output(result, address); + return None; + } + } + + match self.context.make_create_frame::(&inputs) { + Ok(new_frame) => Some(new_frame), + Err(mut result) => { + let mut address = None; + if let Some(inspector) = self.inspector.as_mut() { + let ret = inspector.create_end( + &mut self.context, + result, + curent_stack_frame.created_address, + ); + result = ret.0; + address = ret.1; + } + // insert result of the failed creation of create CallStackFrame. + curent_stack_frame + .interpreter + .insert_create_output(result, address); + None + } + } + } + + /// Handles action for new sub call, return None if there is no need to add + /// new stack frame. + #[inline] + fn handle_sub_call( + &mut self, + mut inputs: Box, + curent_stake_frame: &mut CallStackFrame, + return_memory_offset: Range, + shared_memory: &mut SharedMemory, + ) -> Option> { + // Call inspector if it is some. + if let Some(inspector) = self.inspector.as_mut() { + if let Some((result, range)) = inspector.call(&mut self.context, &mut inputs) { + curent_stake_frame + .interpreter + .insert_call_output(shared_memory, result, range); + return None; + } + } + match self + .context + .make_call_frame(&inputs, return_memory_offset.clone()) + { + Ok(new_frame) => Some(new_frame), + Err(mut result) => { + //println!("Result returned right away: {:#?}", result); + if let Some(inspector) = self.inspector.as_mut() { + result = inspector.call_end(&mut self.context, result); + } + curent_stake_frame.interpreter.insert_call_output( + shared_memory, + result, + return_memory_offset, + ); + None + } + } + } + /// Pre verify transaction. pub fn preverify_transaction_inner(&mut self) -> Result<(), EVMError> { let env = self.env(); // Important: validate block before tx. - env.validate_block_env::()?; - env.validate_tx::()?; + env.validate_block_env::()?; + env.validate_tx::()?; - let initial_gas_spend = initial_tx_gas::( + let initial_gas_spend = initial_tx_gas::( &env.tx.data, env.tx.transact_to.is_create(), &env.tx.access_list, @@ -238,12 +381,12 @@ impl<'a, GSPEC: Spec + 'static, DB: Database> EVMImpl<'a, GSPEC, DB> { // load acc let tx_caller = env.tx.caller; let (caller_account, _) = self - .data + .context .journaled_state - .load_account(tx_caller, self.data.db) + .load_account(tx_caller, self.context.db) .map_err(EVMError::Database)?; - self.data + self.context .env .validate_tx_against_state(caller_account) .map_err(Into::into) @@ -251,7 +394,7 @@ impl<'a, GSPEC: Spec + 'static, DB: Database> EVMImpl<'a, GSPEC, DB> { /// Transact preverified transaction. pub fn transact_preverified_inner(&mut self) -> EVMResult { - let env = &self.data.env; + let env = &self.context.env; let tx_caller = env.tx.caller; let tx_value = env.tx.value; let tx_data = env.tx.data.clone(); @@ -261,22 +404,22 @@ impl<'a, GSPEC: Spec + 'static, DB: Database> EVMImpl<'a, GSPEC, DB> { #[cfg(feature = "optimism")] let tx_l1_cost = if env.cfg.optimism && env.tx.optimism.source_hash.is_none() { let l1_block_info = - optimism::L1BlockInfo::try_fetch(self.data.db).map_err(EVMError::Database)?; + optimism::L1BlockInfo::try_fetch(self.context.db).map_err(EVMError::Database)?; let Some(enveloped_tx) = &env.tx.optimism.enveloped_tx else { panic!("[OPTIMISM] Failed to load enveloped transaction."); }; - let tx_l1_cost = l1_block_info.calculate_tx_l1_cost::(enveloped_tx); + let tx_l1_cost = l1_block_info.calculate_tx_l1_cost::(enveloped_tx); // storage l1 block info for later use. - self.data.l1_block_info = Some(l1_block_info); + self.context.l1_block_info = Some(l1_block_info); tx_l1_cost } else { U256::ZERO }; - let initial_gas_spend = initial_tx_gas::( + let initial_gas_spend = initial_tx_gas::( &tx_data, env.tx.transact_to.is_create(), &env.tx.access_list, @@ -284,49 +427,49 @@ impl<'a, GSPEC: Spec + 'static, DB: Database> EVMImpl<'a, GSPEC, DB> { // load coinbase // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm - if GSPEC::enabled(SHANGHAI) { - self.data + if SPEC::enabled(SHANGHAI) { + self.context .journaled_state - .initial_account_load(self.data.env.block.coinbase, &[], self.data.db) + .initial_account_load(self.context.env.block.coinbase, &[], self.context.db) .map_err(EVMError::Database)?; } - self.data.load_access_list()?; + self.context.load_access_list()?; // load acc - let journal = &mut self.data.journaled_state; + let journal = &mut self.context.journaled_state; #[cfg(feature = "optimism")] - if self.data.env.cfg.optimism { - EVMImpl::::commit_mint_value( + if self.context.env.cfg.optimism { + EVMImpl::::commit_mint_value( tx_caller, - self.data.env.tx.optimism.mint, - self.data.db, + self.context.env.tx.optimism.mint, + self.context.db, journal, )?; - let is_deposit = self.data.env.tx.optimism.source_hash.is_some(); - EVMImpl::::remove_l1_cost( + let is_deposit = self.context.env.tx.optimism.source_hash.is_some(); + EVMImpl::::remove_l1_cost( is_deposit, tx_caller, tx_l1_cost, - self.data.db, + self.context.db, journal, )?; } let (caller_account, _) = journal - .load_account(tx_caller, self.data.db) + .load_account(tx_caller, self.context.db) .map_err(EVMError::Database)?; // Subtract gas costs from the caller's account. // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. let mut gas_cost = - U256::from(tx_gas_limit).saturating_mul(self.data.env.effective_gas_price()); + U256::from(tx_gas_limit).saturating_mul(self.context.env.effective_gas_price()); // EIP-4844 - if GSPEC::enabled(CANCUN) { - let data_fee = self.data.env.calc_data_fee().expect("already checked"); + if SPEC::enabled(CANCUN) { + let data_fee = self.context.env.calc_data_fee().expect("already checked"); gas_cost = gas_cost.saturating_add(data_fee); } @@ -337,19 +480,14 @@ impl<'a, GSPEC: Spec + 'static, DB: Database> EVMImpl<'a, GSPEC, DB> { let transact_gas_limit = tx_gas_limit - initial_gas_spend; - #[cfg(feature = "memory_limit")] - let mut shared_memory = SharedMemory::new_with_memory_limit(self.data.env.cfg.memory_limit); - #[cfg(not(feature = "memory_limit"))] - let mut shared_memory = SharedMemory::new(); - // call inner handling of call/create - let (call_result, ret_gas, output) = match self.data.env.tx.transact_to { + let first_stack_frame = match self.context.env.tx.transact_to { TransactTo::Call(address) => { // Nonce is already checked caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); - let (exit, gas, bytes) = self.call( - &mut CallInputs { + self.context.make_call_frame( + &CallInputs { contract: address, transfer: Transfer { source: tx_caller, @@ -367,30 +505,39 @@ impl<'a, GSPEC: Spec + 'static, DB: Database> EVMImpl<'a, GSPEC, DB> { }, is_static: false, }, - &mut shared_memory, - ); - (exit, gas, Output::Call(bytes)) + 0..0, + ) } - TransactTo::Create(scheme) => { - let (exit, address, ret_gas, bytes) = self.create( - &mut CreateInputs { - caller: tx_caller, - scheme, - value: tx_value, - init_code: tx_data, - gas_limit: transact_gas_limit, - }, - &mut shared_memory, - ); - (exit, ret_gas, Output::Create(bytes, address)) + TransactTo::Create(scheme) => self.context.make_create_frame::(&CreateInputs { + caller: tx_caller, + scheme, + value: tx_value, + init_code: tx_data, + gas_limit: transact_gas_limit, + }), + }; + // Some only if it is create. + let mut created_address = None; + + // start main loop if CallStackFrame is created correctly + let interpreter_result = match first_stack_frame { + Ok(first_stack_frame) => { + created_address = first_stack_frame.created_address; + let table = self.instruction_table.clone(); + match table { + InstructionTables::Plain(table) => self.run(&table, first_stack_frame), + InstructionTables::Boxed(table) => self.run(&table, first_stack_frame), + } } + Err(interpreter_result) => interpreter_result, }; let handler = &self.handler; - let data = &mut self.data; + let data = &mut self.context; // handle output of call/create calls. - let mut gas = handler.call_return(data.env, call_result, ret_gas); + let mut gas = + handler.call_return(data.env, interpreter_result.result, interpreter_result.gas); // set refund. Refund amount depends on hardfork. gas.set_refund(handler.calculate_gas_refund(data.env, &gas) as i64); @@ -399,449 +546,81 @@ impl<'a, GSPEC: Spec + 'static, DB: Database> EVMImpl<'a, GSPEC, DB> { handler.reimburse_caller(data, &gas)?; // Reward beneficiary - if !data.env.cfg.is_beneficiary_reward_disabled() { - handler.reward_beneficiary(data, &gas)?; - } - - // main return - handler.main_return(data, call_result, output, &gas) - } - - #[inline(never)] - fn prepare_create(&mut self, inputs: &CreateInputs) -> Result { - let gas = Gas::new(inputs.gas_limit); - - // Check depth of calls - if self.data.journaled_state.depth() > CALL_STACK_LIMIT { - return Err(CreateResult { - result: InstructionResult::CallTooDeep, - created_address: None, - gas, - return_value: Bytes::new(), - }); - } - - // Fetch balance of caller. - let Some((caller_balance, _)) = self.balance(inputs.caller) else { - return Err(CreateResult { - result: InstructionResult::FatalExternalError, - created_address: None, - gas, - return_value: Bytes::new(), - }); - }; - - // Check if caller has enough balance to send to the created contract. - if caller_balance < inputs.value { - return Err(CreateResult { - result: InstructionResult::OutOfFund, - created_address: None, - gas, - return_value: Bytes::new(), - }); - } - - // Increase nonce of caller and check if it overflows - let old_nonce; - if let Some(nonce) = self.data.journaled_state.inc_nonce(inputs.caller) { - old_nonce = nonce - 1; - } else { - return Err(CreateResult { - result: InstructionResult::Return, - created_address: None, - gas, - return_value: Bytes::new(), - }); - } - - // Create address - let code_hash = keccak256(&inputs.init_code); - let created_address = inputs.created_address_with_hash(old_nonce, &code_hash); - - // Load account so it needs to be marked as warm for access list. - if self - .data - .journaled_state - .load_account(created_address, self.data.db) - .map_err(|e| self.data.error = Some(e)) - .is_err() - { - return Err(CreateResult { - result: InstructionResult::FatalExternalError, - created_address: None, - gas, - return_value: Bytes::new(), - }); - } + handler.reward_beneficiary(data, &gas)?; - // create account, transfer funds and make the journal checkpoint. - let checkpoint = match self - .data - .journaled_state - .create_account_checkpoint::(inputs.caller, created_address, inputs.value) - { - Ok(checkpoint) => checkpoint, - Err(e) => { - return Err(CreateResult { - result: e, - created_address: None, - gas, - return_value: Bytes::new(), - }); - } + // output of execution + let output = match data.env.tx.transact_to { + TransactTo::Call(_) => Output::Call(interpreter_result.output), + TransactTo::Create(_) => Output::Create(interpreter_result.output, created_address), }; - let bytecode = Bytecode::new_raw(inputs.init_code.clone()); - - let contract = Box::new(Contract::new( - Bytes::new(), - bytecode, - code_hash, - created_address, - inputs.caller, - inputs.value, - )); - - Ok(PreparedCreate { - gas, - created_address, - checkpoint, - contract, - }) - } - - /// EVM create opcode for both initial crate and CREATE and CREATE2 opcodes. - fn create_inner( - &mut self, - inputs: &CreateInputs, - shared_memory: &mut SharedMemory, - ) -> CreateResult { - // Prepare crate. - let prepared_create = match self.prepare_create(inputs) { - Ok(o) => o, - Err(e) => return e, - }; - - // Create new interpreter and execute initcode - let (exit_reason, mut bytes, mut gas) = self.run_interpreter( - prepared_create.contract, - prepared_create.gas.limit(), - false, - shared_memory, - ); - - // Host error if present on execution - match exit_reason { - return_ok!() => { - // if ok, check contract creation limit and calculate gas deduction on output len. - // - // EIP-3541: Reject new contract code starting with the 0xEF byte - if GSPEC::enabled(LONDON) && !bytes.is_empty() && bytes.first() == Some(&0xEF) { - self.data - .journaled_state - .checkpoint_revert(prepared_create.checkpoint); - return CreateResult { - result: InstructionResult::CreateContractStartingWithEF, - created_address: Some(prepared_create.created_address), - gas, - return_value: bytes, - }; - } - - // EIP-170: Contract code size limit - // By default limit is 0x6000 (~25kb) - if GSPEC::enabled(SPURIOUS_DRAGON) - && bytes.len() - > self - .data - .env - .cfg - .limit_contract_code_size - .unwrap_or(MAX_CODE_SIZE) - { - self.data - .journaled_state - .checkpoint_revert(prepared_create.checkpoint); - return CreateResult { - result: InstructionResult::CreateContractSizeLimit, - created_address: Some(prepared_create.created_address), - gas, - return_value: bytes, - }; - } - if crate::USE_GAS { - let gas_for_code = bytes.len() as u64 * gas::CODEDEPOSIT; - if !gas.record_cost(gas_for_code) { - // record code deposit gas cost and check if we are out of gas. - // EIP-2 point 3: If contract creation does not have enough gas to pay for the - // final gas fee for adding the contract code to the state, the contract - // creation fails (i.e. goes out-of-gas) rather than leaving an empty contract. - if GSPEC::enabled(HOMESTEAD) { - self.data - .journaled_state - .checkpoint_revert(prepared_create.checkpoint); - return CreateResult { - result: InstructionResult::OutOfGas, - created_address: Some(prepared_create.created_address), - gas, - return_value: bytes, - }; - } else { - bytes = Bytes::new(); - } - } - } - // if we have enough gas - self.data.journaled_state.checkpoint_commit(); - // Do analysis of bytecode straight away. - let bytecode = match self.data.env.cfg.perf_analyse_created_bytecodes { - AnalysisKind::Raw => Bytecode::new_raw(bytes.clone()), - AnalysisKind::Check => Bytecode::new_raw(bytes.clone()).to_checked(), - AnalysisKind::Analyse => to_analysed(Bytecode::new_raw(bytes.clone())), - }; - self.data - .journaled_state - .set_code(prepared_create.created_address, bytecode); - CreateResult { - result: InstructionResult::Return, - created_address: Some(prepared_create.created_address), - gas, - return_value: bytes, - } - } - _ => { - self.data - .journaled_state - .checkpoint_revert(prepared_create.checkpoint); - CreateResult { - result: exit_reason, - created_address: Some(prepared_create.created_address), - gas, - return_value: bytes, - } - } - } + // main return + handler.main_return(data, interpreter_result.result, output, &gas) } +} - /// Create a Interpreter and run it. - /// Returns the exit reason, return value and gas from interpreter - pub fn run_interpreter( - &mut self, - contract: Box, - gas_limit: u64, - is_static: bool, - shared_memory: &mut SharedMemory, - ) -> (InstructionResult, Bytes, Gas) { - let mut interpreter = Box::new(Interpreter::new( - contract, - gas_limit, - is_static, - shared_memory, - )); - - interpreter.shared_memory.new_context(); - - if let Some(inspector) = self.inspector.as_mut() { - inspector.initialize_interp(&mut interpreter, &mut self.data); - } - - let exit_reason = match &mut self.instruction_table { - InstructionTables::Plain(table) => interpreter.run::<_, Self>(&table.clone(), self), - InstructionTables::Boxed(table) => interpreter.run::<_, Self>(&table.clone(), self), - }; - - let (return_value, gas) = (interpreter.return_value(), *interpreter.gas()); - - interpreter.shared_memory.free_context(); +/// EVM transaction interface. +#[auto_impl(&mut, Box)] +pub trait Transact { + /// Run checks that could make transaction fail before call/create. + fn preverify_transaction(&mut self) -> Result<(), EVMError>; - (exit_reason, return_value, gas) - } + /// Skip pre-verification steps and execute the transaction. + fn transact_preverified(&mut self) -> EVMResult; - /// Call precompile contract - fn call_precompile( - &mut self, - precompile: Precompile, - inputs: &CallInputs, - mut gas: Gas, - ) -> CallResult { - let input_data = &inputs.input; - - let out = match precompile { - Precompile::Standard(fun) => fun(input_data, gas.limit()), - Precompile::Env(fun) => fun(input_data, gas.limit(), self.env()), - }; + /// Execute transaction by running pre-verification steps and then transaction itself. + fn transact(&mut self) -> EVMResult; +} - match out { - Ok((gas_used, data)) => { - if !crate::USE_GAS || gas.record_cost(gas_used) { - CallResult { - result: InstructionResult::Return, - gas, - return_value: Bytes::from(data), - } - } else { - CallResult { - result: InstructionResult::PrecompileOOG, - gas, - return_value: Bytes::new(), - } - } - } - Err(e) => { - let result = if precompile::Error::OutOfGas == e { - InstructionResult::PrecompileOOG - } else { - InstructionResult::PrecompileError - }; - CallResult { - result, - gas, - return_value: Bytes::new(), - } - } - } +impl<'a, SPEC: Spec + 'static, DB: Database> Transact for EVMImpl<'a, SPEC, DB> { + #[inline] + fn preverify_transaction(&mut self) -> Result<(), EVMError> { + self.preverify_transaction_inner() } - #[inline(never)] - fn prepare_call(&mut self, inputs: &CallInputs) -> Result { - let gas = Gas::new(inputs.gas_limit); - let account = match self - .data - .journaled_state - .load_code(inputs.contract, self.data.db) - { - Ok((account, _)) => account, - Err(e) => { - self.data.error = Some(e); - return Err(CallResult { - result: InstructionResult::FatalExternalError, - gas, - return_value: Bytes::new(), - }); - } - }; - let code_hash = account.info.code_hash(); - let bytecode = account.info.code.clone().unwrap_or_default(); - - // Check depth - if self.data.journaled_state.depth() > CALL_STACK_LIMIT { - return Err(CallResult { - result: InstructionResult::CallTooDeep, - gas, - return_value: Bytes::new(), - }); - } - - // Create subroutine checkpoint - let checkpoint = self.data.journaled_state.checkpoint(); - - // Touch address. For "EIP-158 State Clear", this will erase empty accounts. - if inputs.transfer.value == U256::ZERO { - self.load_account(inputs.context.address); - self.data.journaled_state.touch(&inputs.context.address); - } - - // Transfer value from caller to called account - if let Err(e) = self.data.journaled_state.transfer( - &inputs.transfer.source, - &inputs.transfer.target, - inputs.transfer.value, - self.data.db, - ) { - self.data.journaled_state.checkpoint_revert(checkpoint); - return Err(CallResult { - result: e, - gas, - return_value: Bytes::new(), - }); - } - - let contract = Box::new(Contract::new_with_context( - inputs.input.clone(), - bytecode, - code_hash, - &inputs.context, - )); - - Ok(PreparedCall { - gas, - checkpoint, - contract, - }) + #[inline] + fn transact_preverified(&mut self) -> EVMResult { + let output = self.transact_preverified_inner(); + self.handler.end(&mut self.context, output) } - /// Main contract call of the EVM. - fn call_inner(&mut self, inputs: &CallInputs, shared_memory: &mut SharedMemory) -> CallResult { - // Prepare call - let prepared_call = match self.prepare_call(inputs) { - Ok(o) => o, - Err(e) => return e, - }; - - let ret = if let Some(precompile) = self.data.precompiles.get(&inputs.contract) { - self.call_precompile(precompile, inputs, prepared_call.gas) - } else if !prepared_call.contract.bytecode.is_empty() { - // Create interpreter and execute subcall - let (exit_reason, bytes, gas) = self.run_interpreter( - prepared_call.contract, - prepared_call.gas.limit(), - inputs.is_static, - shared_memory, - ); - CallResult { - result: exit_reason, - gas, - return_value: bytes, - } - } else { - CallResult { - result: InstructionResult::Stop, - gas: prepared_call.gas, - return_value: Bytes::new(), - } - }; - - // revert changes or not. - if matches!(ret.result, return_ok!()) { - self.data.journaled_state.checkpoint_commit(); - } else { - self.data - .journaled_state - .checkpoint_revert(prepared_call.checkpoint); - } - - ret + #[inline] + fn transact(&mut self) -> EVMResult { + let output = self + .preverify_transaction_inner() + .and_then(|()| self.transact_preverified_inner()); + self.handler.end(&mut self.context, output) } } -impl<'a, GSPEC: Spec + 'static, DB: Database> Host for EVMImpl<'a, GSPEC, DB> { +impl<'a, SPEC: Spec + 'static, DB: Database> Host for EVMImpl<'a, SPEC, DB> { fn env(&mut self) -> &mut Env { - self.data.env() + self.context.env() } fn block_hash(&mut self, number: U256) -> Option { - self.data.block_hash(number) + self.context.block_hash(number) } fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { - self.data.load_account(address) + self.context.load_account(address) } fn balance(&mut self, address: Address) -> Option<(U256, bool)> { - self.data.balance(address) + self.context.balance(address) } fn code(&mut self, address: Address) -> Option<(Bytecode, bool)> { - self.data.code(address) + self.context.code(address) } /// Get code hash of address. fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { - self.data.code_hash(address) + self.context.code_hash(address) } fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { - self.data.sload(address, index) + self.context.sload(address, index) } fn sstore( @@ -850,91 +629,80 @@ impl<'a, GSPEC: Spec + 'static, DB: Database> Host for EVMImpl<'a, GSPEC, DB> { index: U256, value: U256, ) -> Option<(U256, U256, U256, bool)> { - self.data.sstore(address, index, value) + self.context.sstore(address, index, value) } fn tload(&mut self, address: Address, index: U256) -> U256 { - self.data.tload(address, index) + self.context.tload(address, index) } fn tstore(&mut self, address: Address, index: U256, value: U256) { - self.data.tstore(address, index, value) + self.context.tstore(address, index, value) } fn log(&mut self, address: Address, topics: Vec, data: Bytes) { if let Some(inspector) = self.inspector.as_mut() { - inspector.log(&mut self.data, &address, &topics, &data); + inspector.log(&mut self.context, &address, &topics, &data); } let log = Log { address, topics, data, }; - self.data.journaled_state.log(log); + self.context.journaled_state.log(log); } fn selfdestruct(&mut self, address: Address, target: Address) -> Option { if let Some(inspector) = self.inspector.as_mut() { - let acc = self.data.journaled_state.state.get(&address).unwrap(); + let acc = self.context.journaled_state.state.get(&address).unwrap(); inspector.selfdestruct(address, target, acc.info.balance); } - self.data + self.context .journaled_state - .selfdestruct(address, target, self.data.db) - .map_err(|e| self.data.error = Some(e)) + .selfdestruct(address, target, self.context.db) + .map_err(|e| self.context.error = Some(e)) .ok() } +} - fn create( - &mut self, - inputs: &mut CreateInputs, - shared_memory: &mut SharedMemory, - ) -> (InstructionResult, Option
, Gas, Bytes) { - // Call inspector - if let Some(inspector) = self.inspector.as_mut() { - let (ret, address, gas, out) = inspector.create(&mut self.data, inputs); - if ret != InstructionResult::Continue { - return inspector.create_end(&mut self.data, inputs, ret, address, gas, out); - } - } - let ret = self.create_inner(inputs, shared_memory); - if let Some(inspector) = self.inspector.as_mut() { - inspector.create_end( - &mut self.data, - inputs, - ret.result, - ret.created_address, - ret.gas, - ret.return_value, - ) - } else { - (ret.result, ret.created_address, ret.gas, ret.return_value) - } +/// Creates new EVM instance with erased types. +pub fn new_evm<'a, DB: Database>( + env: &'a mut Env, + db: &'a mut DB, + insp: Option<&'a mut dyn Inspector>, +) -> Box + 'a> { + macro_rules! create_evm { + ($spec:ident) => { + Box::new(EVMImpl::<'a, $spec, DB>::new_with_spec( + db, + env, + insp, + Precompiles::new(revm_precompile::SpecId::from_spec_id($spec::SPEC_ID)).clone(), + )) + }; } - fn call( - &mut self, - inputs: &mut CallInputs, - shared_memory: &mut SharedMemory, - ) -> (InstructionResult, Gas, Bytes) { - if let Some(inspector) = self.inspector.as_mut() { - let (ret, gas, out) = inspector.call(&mut self.data, inputs); - if ret != InstructionResult::Continue { - return inspector.call_end(&mut self.data, inputs, gas, ret, out); - } - } - let ret = self.call_inner(inputs, shared_memory); - if let Some(inspector) = self.inspector.as_mut() { - inspector.call_end( - &mut self.data, - inputs, - ret.gas, - ret.result, - ret.return_value, - ) - } else { - (ret.result, ret.gas, ret.return_value) + use specification::*; + match env.cfg.spec_id { + SpecId::FRONTIER | SpecId::FRONTIER_THAWING => create_evm!(FrontierSpec), + SpecId::HOMESTEAD | SpecId::DAO_FORK => create_evm!(HomesteadSpec), + SpecId::TANGERINE => create_evm!(TangerineSpec), + SpecId::SPURIOUS_DRAGON => create_evm!(SpuriousDragonSpec), + SpecId::BYZANTIUM => create_evm!(ByzantiumSpec), + SpecId::PETERSBURG | SpecId::CONSTANTINOPLE => create_evm!(PetersburgSpec), + SpecId::ISTANBUL | SpecId::MUIR_GLACIER => create_evm!(IstanbulSpec), + SpecId::BERLIN => create_evm!(BerlinSpec), + SpecId::LONDON | SpecId::ARROW_GLACIER | SpecId::GRAY_GLACIER => { + create_evm!(LondonSpec) } + SpecId::MERGE => create_evm!(MergeSpec), + SpecId::SHANGHAI => create_evm!(ShanghaiSpec), + SpecId::CANCUN => create_evm!(CancunSpec), + SpecId::LATEST => create_evm!(LatestSpec), + #[cfg(feature = "optimism")] + SpecId::BEDROCK => create_evm!(BedrockSpec), + #[cfg(feature = "optimism")] + SpecId::REGOLITH => create_evm!(RegolithSpec), } } diff --git a/crates/revm/src/frame.rs b/crates/revm/src/frame.rs new file mode 100644 index 0000000000..251cd974de --- /dev/null +++ b/crates/revm/src/frame.rs @@ -0,0 +1,19 @@ +use crate::JournalCheckpoint; +use crate::{interpreter::Interpreter, primitives::Address}; +use core::ops::Range; + +/// Call CallStackFrame. +#[derive(Debug)] +pub struct CallStackFrame { + /// True if it is create false if it is call. + /// TODO make a enum for this. + pub is_create: bool, + /// Journal checkpoint + pub checkpoint: JournalCheckpoint, + /// temporary. If it is create it should have address. + pub created_address: Option
, + /// temporary. Call range + pub subcall_return_memory_range: Range, + /// Interpreter + pub interpreter: Interpreter, +} diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index ddbfa989d7..eef328bd41 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -5,7 +5,7 @@ pub mod optimism; use crate::{ interpreter::{Gas, InstructionResult}, primitives::{db::Database, EVMError, EVMResultGeneric, Env, Output, ResultAndState, Spec}, - EVMData, + EvmContext, }; /// Handle call return and return final gas value. @@ -13,7 +13,7 @@ type CallReturnHandle = fn(&Env, InstructionResult, Gas) -> Gas; /// Reimburse the caller with ethereum it didn't spent. type ReimburseCallerHandle = - fn(&mut EVMData<'_, DB>, &Gas) -> EVMResultGeneric<(), ::Error>; + fn(&mut EvmContext<'_, DB>, &Gas) -> EVMResultGeneric<(), ::Error>; /// Reward beneficiary with transaction rewards. type RewardBeneficiaryHandle = ReimburseCallerHandle; @@ -23,7 +23,7 @@ type CalculateGasRefundHandle = fn(&Env, &Gas) -> u64; /// Main return handle, takes state from journal and transforms internal result to external. type MainReturnHandle = fn( - &mut EVMData<'_, DB>, + &mut EvmContext<'_, DB>, InstructionResult, Output, &Gas, @@ -34,7 +34,7 @@ type MainReturnHandle = fn( /// /// It is useful for catching errors and returning them in a different way. type EndHandle = fn( - &mut EVMData<'_, DB>, + &mut EvmContext<'_, DB>, evm_output: Result::Error>>, ) -> Result::Error>>; @@ -95,10 +95,10 @@ impl Handler { /// Reimburse the caller with gas that were not spend. pub fn reimburse_caller( &self, - data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, gas: &Gas, ) -> Result<(), EVMError> { - (self.reimburse_caller)(data, gas) + (self.reimburse_caller)(context, gas) } /// Calculate gas refund for transaction. Some chains have it disabled. @@ -109,29 +109,29 @@ impl Handler { /// Reward beneficiary pub fn reward_beneficiary( &self, - data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, gas: &Gas, ) -> Result<(), EVMError> { - (self.reward_beneficiary)(data, gas) + (self.reward_beneficiary)(context, gas) } /// Main return. pub fn main_return( &self, - data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, call_result: InstructionResult, output: Output, gas: &Gas, ) -> Result> { - (self.main_return)(data, call_result, output, gas) + (self.main_return)(context, call_result, output, gas) } /// End handler. pub fn end( &self, - data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, end_output: Result>, ) -> Result> { - (self.end)(data, end_output) + (self.end)(context, end_output) } } diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index 4e43672a07..c39293391f 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -6,7 +6,7 @@ use crate::{ db::Database, EVMError, Env, ExecutionResult, Output, ResultAndState, Spec, SpecId::LONDON, U256, }, - EVMData, + EvmContext, }; /// Handle output of the transaction @@ -36,17 +36,16 @@ pub fn handle_call_return( #[inline] pub fn handle_reimburse_caller( - data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, gas: &Gas, ) -> Result<(), EVMError> { - let _ = data; - let caller = data.env.tx.caller; - let effective_gas_price = data.env.effective_gas_price(); + let caller = context.env.tx.caller; + let effective_gas_price = context.env.effective_gas_price(); // return balance of not spend gas. - let (caller_account, _) = data + let (caller_account, _) = context .journaled_state - .load_account(caller, data.db) + .load_account(caller, context.db) .map_err(EVMError::Database)?; caller_account.info.balance = caller_account @@ -60,23 +59,23 @@ pub fn handle_reimburse_caller( /// Reward beneficiary with gas fee. #[inline] pub fn reward_beneficiary( - data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, gas: &Gas, ) -> Result<(), EVMError> { - let beneficiary = data.env.block.coinbase; - let effective_gas_price = data.env.effective_gas_price(); + let beneficiary = context.env.block.coinbase; + let effective_gas_price = context.env.effective_gas_price(); // transfer fee to coinbase/beneficiary. // EIP-1559 discard basefee for coinbase transfer. Basefee amount of gas is discarded. let coinbase_gas_price = if SPEC::enabled(LONDON) { - effective_gas_price.saturating_sub(data.env.block.basefee) + effective_gas_price.saturating_sub(context.env.block.basefee) } else { effective_gas_price }; - let (coinbase_account, _) = data + let (coinbase_account, _) = context .journaled_state - .load_account(beneficiary, data.db) + .load_account(beneficiary, context.db) .map_err(EVMError::Database)?; coinbase_account.mark_touch(); @@ -105,10 +104,12 @@ pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { } } +//pub fn main_first_call + /// Main return handle, returns the output of the transaction. #[inline] pub fn main_return( - data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, call_result: InstructionResult, output: Output, gas: &Gas, @@ -118,7 +119,7 @@ pub fn main_return( let final_gas_used = gas.spend() - gas_refunded; // reset journal and return present state. - let (state, logs) = data.journaled_state.finalize(); + let (state, logs) = context.journaled_state.finalize(); let result = match call_result.into() { SuccessOrHalt::Success(reason) => ExecutionResult::Success { @@ -140,9 +141,10 @@ pub fn main_return( gas_used: final_gas_used, }, SuccessOrHalt::FatalExternalError => { - return Err(EVMError::Database(data.error.take().unwrap())); + return Err(EVMError::Database(context.error.take().unwrap())); } - SuccessOrHalt::InternalContinue => { + // Only two internal return flags. + SuccessOrHalt::InternalContinue | SuccessOrHalt::InternalCallOrCreate => { panic!("Internal return flags should remain internal {call_result:?}") } }; @@ -153,7 +155,7 @@ pub fn main_return( /// Mainnet end handle does not change the output. #[inline] pub fn end_handle( - _data: &mut EVMData<'_, DB>, + _context: &mut EvmContext<'_, DB>, evm_output: Result>, ) -> Result> { evm_output diff --git a/crates/revm/src/handler/optimism.rs b/crates/revm/src/handler/optimism.rs index 8fab657d61..b90dc9bad0 100644 --- a/crates/revm/src/handler/optimism.rs +++ b/crates/revm/src/handler/optimism.rs @@ -8,7 +8,7 @@ use crate::{ db::Database, Account, EVMError, Env, ExecutionResult, Halt, HashMap, InvalidTransaction, Output, ResultAndState, Spec, SpecId::REGOLITH, U256, }, - EVMData, + EvmContext, }; use core::ops::Mul; @@ -92,34 +92,34 @@ pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { /// Reward beneficiary with gas fee. #[inline] pub fn reward_beneficiary( - data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, gas: &Gas, ) -> Result<(), EVMError> { - let is_deposit = data.env.cfg.optimism && data.env.tx.optimism.source_hash.is_some(); - let disable_coinbase_tip = data.env.cfg.optimism && is_deposit; + let is_deposit = context.env.cfg.optimism && context.env.tx.optimism.source_hash.is_some(); + let disable_coinbase_tip = context.env.cfg.optimism && is_deposit; // transfer fee to coinbase/beneficiary. if !disable_coinbase_tip { - mainnet::reward_beneficiary::(data, gas)?; + mainnet::reward_beneficiary::(context, gas)?; } - if data.env.cfg.optimism && !is_deposit { + if context.env.cfg.optimism && !is_deposit { // If the transaction is not a deposit transaction, fees are paid out // to both the Base Fee Vault as well as the L1 Fee Vault. - let Some(l1_block_info) = data.l1_block_info.clone() else { + let Some(l1_block_info) = context.l1_block_info.clone() else { panic!("[OPTIMISM] Failed to load L1 block information."); }; - let Some(enveloped_tx) = &data.env.tx.optimism.enveloped_tx else { + let Some(enveloped_tx) = &context.env.tx.optimism.enveloped_tx else { panic!("[OPTIMISM] Failed to load enveloped transaction."); }; let l1_cost = l1_block_info.calculate_tx_l1_cost::(enveloped_tx); // Send the L1 cost of the transaction to the L1 Fee Vault. - let Ok((l1_fee_vault_account, _)) = data + let Ok((l1_fee_vault_account, _)) = context .journaled_state - .load_account(optimism::L1_FEE_RECIPIENT, data.db) + .load_account(optimism::L1_FEE_RECIPIENT, context.db) else { panic!("[OPTIMISM] Failed to load L1 Fee Vault account"); }; @@ -127,14 +127,14 @@ pub fn reward_beneficiary( l1_fee_vault_account.info.balance += l1_cost; // Send the base fee of the transaction to the Base Fee Vault. - let Ok((base_fee_vault_account, _)) = data + let Ok((base_fee_vault_account, _)) = context .journaled_state - .load_account(optimism::BASE_FEE_RECIPIENT, data.db) + .load_account(optimism::BASE_FEE_RECIPIENT, context.db) else { panic!("[OPTIMISM] Failed to load Base Fee Vault account"); }; base_fee_vault_account.mark_touch(); - base_fee_vault_account.info.balance += data + base_fee_vault_account.info.balance += context .env .block .basefee @@ -146,19 +146,19 @@ pub fn reward_beneficiary( /// Main return handle, returns the output of the transaction. #[inline] pub fn main_return( - data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, call_result: InstructionResult, output: Output, gas: &Gas, ) -> Result> { - let result = mainnet::main_return::(data, call_result, output, gas)?; + let result = mainnet::main_return::(context, call_result, output, gas)?; if result.result.is_halt() { // Post-regolith, if the transaction is a deposit transaction and it haults, // we bubble up to the global return handler. The mint value will be persisted // and the caller nonce will be incremented there. - let is_deposit = data.env.tx.optimism.source_hash.is_some(); - let optimism_regolith = data.env.cfg.optimism && SPEC::enabled(REGOLITH); + let is_deposit = context.env.tx.optimism.source_hash.is_some(); + let optimism_regolith = context.env.cfg.optimism && SPEC::enabled(REGOLITH); if is_deposit && optimism_regolith { return Err(EVMError::Transaction( InvalidTransaction::HaltedDepositPostRegolith, @@ -171,13 +171,13 @@ pub fn main_return( /// Deposit transaction can't be reverted and is always successful. #[inline] pub fn end_handle( - data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, evm_output: Result>, ) -> Result> { evm_output.or_else(|err| { if matches!(err, EVMError::Transaction(_)) - && data.env().cfg.optimism - && data.env().tx.optimism.source_hash.is_some() + && context.env().cfg.optimism + && context.env().tx.optimism.source_hash.is_some() { // If the transaction is a deposit transaction and it failed // for any reason, the caller nonce must be bumped, and the @@ -185,13 +185,14 @@ pub fn end_handle( // also returned as a special Halt variant so that consumers can more // easily distinguish between a failed deposit and a failed // normal transaction. - let caller = data.env().tx.caller; + let caller = context.env().tx.caller; // Increment sender nonce and account balance for the mint amount. Deposits // always persist the mint amount, even if the transaction fails. let account = { let mut acc = Account::from( - data.db + context + .db .basic(caller) .unwrap_or_default() .unwrap_or_default(), @@ -200,7 +201,7 @@ pub fn end_handle( acc.info.balance = acc .info .balance - .saturating_add(U256::from(data.env().tx.optimism.mint.unwrap_or(0))); + .saturating_add(U256::from(context.env().tx.optimism.mint.unwrap_or(0))); acc.mark_touch(); acc }; @@ -210,14 +211,14 @@ pub fn end_handle( // limit of the transaction. pre-regolith, it is the gas limit // of the transaction for non system transactions and 0 for system // transactions. - let is_system_tx = data + let is_system_tx = context .env() .tx .optimism .is_system_transaction .unwrap_or(false); let gas_used = if SPEC::enabled(REGOLITH) || !is_system_tx { - data.env().tx.gas_limit + context.env().tx.gas_limit } else { 0 }; diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index bda2159356..0eb953d7c7 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -1,6 +1,10 @@ -use crate::interpreter::{CallInputs, CreateInputs, Gas, InstructionResult, Interpreter}; -use crate::primitives::{db::Database, Address, Bytes, B256, U256}; -use crate::EVMData; +use core::ops::Range; + +use crate::{ + interpreter::{CallInputs, CreateInputs, Interpreter}, + primitives::{db::Database, Address, Bytes, B256, U256}, + EvmContext, +}; use auto_impl::auto_impl; #[cfg(feature = "std")] @@ -12,6 +16,7 @@ mod instruction; mod noop; pub use instruction::inspector_instruction; +use revm_interpreter::InterpreterResult; /// [Inspector] implementations. pub mod inspectors { #[cfg(feature = "std")] @@ -30,9 +35,9 @@ pub trait Inspector { /// If `interp.instruction_result` is set to anything other than [InstructionResult::Continue] then the execution of the interpreter /// is skipped. #[inline] - fn initialize_interp(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { let _ = interp; - let _ = data; + let _ = context; } /// Called on each step of the interpreter. @@ -44,21 +49,21 @@ pub trait Inspector { /// /// To get the current opcode, use `interp.current_opcode()`. #[inline] - fn step(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { let _ = interp; - let _ = data; + let _ = context; } /// Called when a log is emitted. #[inline] fn log( &mut self, - evm_data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, address: &Address, topics: &[B256], data: &Bytes, ) { - let _ = evm_data; + let _ = context; let _ = address; let _ = topics; let _ = data; @@ -69,9 +74,9 @@ pub trait Inspector { /// Setting `interp.instruction_result` to anything other than [InstructionResult::Continue] alters the execution /// of the interpreter. #[inline] - fn step_end(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { let _ = interp; - let _ = data; + let _ = context; } /// Called whenever a call to a contract is about to start. @@ -80,12 +85,12 @@ pub trait Inspector { #[inline] fn call( &mut self, - data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, inputs: &mut CallInputs, - ) -> (InstructionResult, Gas, Bytes) { - let _ = data; + ) -> Option<(InterpreterResult, Range)> { + let _ = context; let _ = inputs; - (InstructionResult::Continue, Gas::new(0), Bytes::new()) + None } /// Called when a call to a contract has concluded. @@ -95,15 +100,11 @@ pub trait Inspector { #[inline] fn call_end( &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CallInputs, - remaining_gas: Gas, - ret: InstructionResult, - out: Bytes, - ) -> (InstructionResult, Gas, Bytes) { - let _ = data; - let _ = inputs; - (ret, remaining_gas, out) + context: &mut EvmContext<'_, DB>, + result: InterpreterResult, + ) -> InterpreterResult { + let _ = context; + result } /// Called when a contract is about to be created. @@ -112,17 +113,12 @@ pub trait Inspector { #[inline] fn create( &mut self, - data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, inputs: &mut CreateInputs, - ) -> (InstructionResult, Option
, Gas, Bytes) { - let _ = data; + ) -> Option<(InterpreterResult, Option
)> { + let _ = context; let _ = inputs; - ( - InstructionResult::Continue, - None, - Gas::new(0), - Bytes::default(), - ) + None } /// Called when a contract has been created. @@ -132,16 +128,12 @@ pub trait Inspector { #[inline] fn create_end( &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CreateInputs, - ret: InstructionResult, + context: &mut EvmContext<'_, DB>, + result: InterpreterResult, address: Option
, - remaining_gas: Gas, - out: Bytes, - ) -> (InstructionResult, Option
, Gas, Bytes) { - let _ = data; - let _ = inputs; - (ret, address, remaining_gas, out) + ) -> (InterpreterResult, Option
) { + let _ = context; + (result, address) } /// Called when a contract has been self-destructed with funds transferred to target. diff --git a/crates/revm/src/inspector/customprinter.rs b/crates/revm/src/inspector/customprinter.rs index a265a381d5..9ea86d5f9b 100644 --- a/crates/revm/src/inspector/customprinter.rs +++ b/crates/revm/src/inspector/customprinter.rs @@ -1,9 +1,14 @@ //! Custom print inspector, it has step level information of execution. //! It is a great tool if some debugging is needed. -use crate::interpreter::{opcode, CallInputs, CreateInputs, Gas, InstructionResult, Interpreter}; -use crate::primitives::{Address, Bytes, U256}; -use crate::{inspectors::GasInspector, Database, EVMData, Inspector}; +use core::ops::Range; + +use crate::{ + inspectors::GasInspector, + interpreter::{opcode, CallInputs, CreateInputs, Interpreter, InterpreterResult}, + primitives::{Address, U256}, + Database, EvmContext, Inspector, +}; /// Custom print [Inspector], it has step level information of execution. /// @@ -14,21 +19,23 @@ pub struct CustomPrintTracer { } impl Inspector for CustomPrintTracer { - fn initialize_interp(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { - self.gas_inspector.initialize_interp(interp, data); + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + self.gas_inspector.initialize_interp(interp, context); } // get opcode by calling `interp.contract.opcode(interp.program_counter())`. // all other information can be obtained from interp. - fn step(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { let opcode = interp.current_opcode(); let opcode_str = opcode::OPCODE_JUMPMAP[opcode as usize]; let gas_remaining = self.gas_inspector.gas_remaining(); + let memory_size = interp.shared_memory.len(); + println!( "depth:{}, PC:{}, gas:{:#x}({}), OPCODE: {:?}({:?}) refund:{:#x}({}) Stack:{:?}, Data size:{}", - data.journaled_state.depth(), + context.journaled_state.depth(), interp.program_counter(), gas_remaining, gas_remaining, @@ -37,48 +44,38 @@ impl Inspector for CustomPrintTracer { interp.gas.refunded(), interp.gas.refunded(), interp.stack.data(), - interp.shared_memory.len(), + memory_size, ); - self.gas_inspector.step(interp, data); + self.gas_inspector.step(interp, context); } - fn step_end(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { - self.gas_inspector.step_end(interp, data); + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + self.gas_inspector.step_end(interp, context); } fn call_end( &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CallInputs, - remaining_gas: Gas, - ret: InstructionResult, - out: Bytes, - ) -> (InstructionResult, Gas, Bytes) { - self.gas_inspector - .call_end(data, inputs, remaining_gas, ret, out.clone()); - (ret, remaining_gas, out) + context: &mut EvmContext<'_, DB>, + result: InterpreterResult, + ) -> InterpreterResult { + self.gas_inspector.call_end(context, result) } fn create_end( &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CreateInputs, - ret: InstructionResult, + context: &mut EvmContext<'_, DB>, + result: InterpreterResult, address: Option
, - remaining_gas: Gas, - out: Bytes, - ) -> (InstructionResult, Option
, Gas, Bytes) { - self.gas_inspector - .create_end(data, inputs, ret, address, remaining_gas, out.clone()); - (ret, address, remaining_gas, out) + ) -> (InterpreterResult, Option
) { + self.gas_inspector.create_end(context, result, address) } fn call( &mut self, - _data: &mut EVMData<'_, DB>, + _context: &mut EvmContext<'_, DB>, inputs: &mut CallInputs, - ) -> (InstructionResult, Gas, Bytes) { + ) -> Option<(InterpreterResult, Range)> { println!( "SM CALL: {:?}, context:{:?}, is_static:{:?}, transfer:{:?}, input_size:{:?}", inputs.contract, @@ -87,19 +84,19 @@ impl Inspector for CustomPrintTracer { inputs.transfer, inputs.input.len(), ); - (InstructionResult::Continue, Gas::new(0), Bytes::new()) + None } fn create( &mut self, - _data: &mut EVMData<'_, DB>, + _context: &mut EvmContext<'_, DB>, inputs: &mut CreateInputs, - ) -> (InstructionResult, Option
, Gas, Bytes) { + ) -> Option<(InterpreterResult, Option
)> { println!( "CREATE CALL: caller:{:?}, scheme:{:?}, value:{:?}, init_code:{:?}, gas:{:?}", inputs.caller, inputs.scheme, inputs.value, inputs.init_code, inputs.gas_limit ); - (InstructionResult::Continue, None, Gas::new(0), Bytes::new()) + None } fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { @@ -114,7 +111,6 @@ impl Inspector for CustomPrintTracer { mod test { #[test] - #[cfg(not(feature = "no_gas_measuring"))] #[cfg(not(feature = "optimism"))] fn gas_calculation_underflow() { use crate::primitives::{address, bytes}; diff --git a/crates/revm/src/inspector/eip3155.rs b/crates/revm/src/inspector/eip3155.rs index 58310216bb..c0803282dc 100644 --- a/crates/revm/src/inspector/eip3155.rs +++ b/crates/revm/src/inspector/eip3155.rs @@ -1,9 +1,12 @@ -use crate::inspectors::GasInspector; -use crate::interpreter::{CallInputs, CreateInputs, Gas, InstructionResult}; -use crate::primitives::{db::Database, hex, Address, Bytes}; -use crate::{EVMData, Inspector}; -use revm_interpreter::primitives::U256; -use revm_interpreter::{opcode, Interpreter, SharedMemory, Stack}; +use crate::{ + inspectors::GasInspector, + interpreter::{ + opcode, CallInputs, CreateInputs, Interpreter, InterpreterResult, SharedMemory, Stack, + }, + primitives::{db::Database, hex, Address, U256}, + EvmContext, Inspector, +}; +use core::ops::Range; use serde_json::json; use std::io::Write; @@ -46,56 +49,49 @@ impl TracerEip3155 { } impl Inspector for TracerEip3155 { - fn initialize_interp(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { - self.gas_inspector.initialize_interp(interp, data); + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + self.gas_inspector.initialize_interp(interp, context); } // get opcode by calling `interp.contract.opcode(interp.program_counter())`. // all other information can be obtained from interp. - fn step(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { - self.gas_inspector.step(interp, data); + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + self.gas_inspector.step(interp, context); self.stack = interp.stack.clone(); self.pc = interp.program_counter(); self.opcode = interp.current_opcode(); self.mem_size = interp.shared_memory.len(); - self.gas = self.gas_inspector.gas_remaining(); + self.gas = interp.gas.remaining(); } - fn step_end(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { - self.gas_inspector.step_end(interp, data); + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + self.gas_inspector.step_end(interp, context); if self.skip { self.skip = false; return; }; - self.print_log_line(data.journaled_state.depth()); + self.print_log_line(context.journaled_state.depth()); } fn call( &mut self, - data: &mut EVMData<'_, DB>, + _context: &mut EvmContext<'_, DB>, _inputs: &mut CallInputs, - ) -> (InstructionResult, Gas, Bytes) { - self.print_log_line(data.journaled_state.depth()); - (InstructionResult::Continue, Gas::new(0), Bytes::new()) + ) -> Option<(InterpreterResult, Range)> { + None } fn call_end( &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CallInputs, - remaining_gas: Gas, - ret: InstructionResult, - out: Bytes, - ) -> (InstructionResult, Gas, Bytes) { - self.gas_inspector - .call_end(data, inputs, remaining_gas, ret, out.clone()); - // self.log_step(interp, data, is_static, eval); - self.skip = true; - if data.journaled_state.depth() == 0 { + context: &mut EvmContext<'_, DB>, + result: InterpreterResult, + ) -> InterpreterResult { + let result = self.gas_inspector.call_end(context, result); + if context.journaled_state.depth() == 0 { let log_line = json!({ //stateroot - "output": format!("0x{}", hex::encode(out.as_ref())), + "output": format!("0x{}", hex::encode(result.output.as_ref())), "gasUsed": format!("0x{:x}", self.gas_inspector.gas_remaining()), //time //fork @@ -104,36 +100,24 @@ impl Inspector for TracerEip3155 { writeln!(self.output, "{}", serde_json::to_string(&log_line).unwrap()) .expect("If output fails we can ignore the logging"); } - (ret, remaining_gas, out) + result } fn create( &mut self, - data: &mut EVMData<'_, DB>, + _context: &mut EvmContext<'_, DB>, _inputs: &mut CreateInputs, - ) -> (InstructionResult, Option
, Gas, Bytes) { - self.print_log_line(data.journaled_state.depth()); - ( - InstructionResult::Continue, - None, - Gas::new(0), - Bytes::default(), - ) + ) -> Option<(InterpreterResult, Option
)> { + None } fn create_end( &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CreateInputs, - ret: InstructionResult, + context: &mut EvmContext<'_, DB>, + result: InterpreterResult, address: Option
, - remaining_gas: Gas, - out: Bytes, - ) -> (InstructionResult, Option
, Gas, Bytes) { - self.gas_inspector - .create_end(data, inputs, ret, address, remaining_gas, out.clone()); - self.skip = true; - (ret, address, remaining_gas, out) + ) -> (InterpreterResult, Option
) { + self.gas_inspector.create_end(context, result, address) } } @@ -141,17 +125,17 @@ impl TracerEip3155 { fn print_log_line(&mut self, depth: u64) { let short_stack: Vec = self.stack.data().iter().map(|&b| short_hex(b)).collect(); let log_line = json!({ + "depth": depth, "pc": self.pc, + "opName": opcode::OPCODE_JUMPMAP[self.opcode as usize], "op": self.opcode, "gas": format!("0x{:x}", self.gas), "gasCost": format!("0x{:x}", self.gas_inspector.last_gas_cost()), //memory? "memSize": self.mem_size, "stack": short_stack, - "depth": depth, //returnData //refund - "opName": opcode::OPCODE_JUMPMAP[self.opcode as usize], //error //storage //returnStack diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 35efaef4d1..965d87e009 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -1,8 +1,10 @@ //! GasIspector. Helper Inspector to calculate gas for others. -use crate::interpreter::{CallInputs, CreateInputs, Gas, InstructionResult}; -use crate::primitives::{db::Database, Address, Bytes}; -use crate::{EVMData, Inspector}; +use crate::{ + interpreter::InterpreterResult, + primitives::{db::Database, Address}, + EvmContext, Inspector, +}; /// Helper [Inspector] that keeps track of gas. #[allow(dead_code)] @@ -23,20 +25,18 @@ impl GasInspector { } impl Inspector for GasInspector { - #[cfg(not(feature = "no_gas_measuring"))] fn initialize_interp( &mut self, - interp: &mut crate::interpreter::Interpreter<'_>, - _data: &mut EVMData<'_, DB>, + interp: &mut crate::interpreter::Interpreter, + _context: &mut EvmContext<'_, DB>, ) { self.gas_remaining = interp.gas.limit(); } - #[cfg(not(feature = "no_gas_measuring"))] fn step_end( &mut self, - interp: &mut crate::interpreter::Interpreter<'_>, - _data: &mut EVMData<'_, DB>, + interp: &mut crate::interpreter::Interpreter, + _context: &mut EvmContext<'_, DB>, ) { let last_gas = core::mem::replace(&mut self.gas_remaining, interp.gas.remaining()); self.last_gas_cost = last_gas.saturating_sub(self.last_gas_cost); @@ -44,39 +44,36 @@ impl Inspector for GasInspector { fn call_end( &mut self, - _data: &mut EVMData<'_, DB>, - _inputs: &CallInputs, - mut remaining_gas: Gas, - ret: InstructionResult, - out: Bytes, - ) -> (InstructionResult, Gas, Bytes) { - if ret.is_error() { - remaining_gas.record_cost(remaining_gas.remaining()); + _context: &mut EvmContext<'_, DB>, + mut result: InterpreterResult, + ) -> InterpreterResult { + if result.result.is_error() { + result.gas.record_cost(result.gas.remaining()); self.gas_remaining = 0; - (ret, remaining_gas, out) - } else { - (ret, remaining_gas, out) } + result } fn create_end( &mut self, - _data: &mut EVMData<'_, DB>, - _inputs: &CreateInputs, - ret: InstructionResult, + _context: &mut EvmContext<'_, DB>, + result: InterpreterResult, address: Option
, - remaining_gas: Gas, - out: Bytes, - ) -> (InstructionResult, Option
, Gas, Bytes) { - (ret, address, remaining_gas, out) + ) -> (InterpreterResult, Option
) { + (result, address) } } #[cfg(test)] mod tests { - use crate::interpreter::{CallInputs, CreateInputs, Gas, InstructionResult, Interpreter}; - use crate::primitives::{Address, Bytes, B256}; - use crate::{inspectors::GasInspector, Database, EVMData, Inspector}; + use core::ops::Range; + + use crate::{ + inspectors::GasInspector, + interpreter::{CallInputs, CreateInputs, Interpreter, InterpreterResult}, + primitives::{Address, Bytes, B256}, + Database, EvmContext, Inspector, + }; #[derive(Default, Debug)] struct StackInspector { @@ -86,85 +83,67 @@ mod tests { } impl Inspector for StackInspector { - fn initialize_interp(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { - self.gas_inspector.initialize_interp(interp, data); + fn initialize_interp( + &mut self, + interp: &mut Interpreter, + context: &mut EvmContext<'_, DB>, + ) { + self.gas_inspector.initialize_interp(interp, context); } - fn step(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { self.pc = interp.program_counter(); - self.gas_inspector.step(interp, data); + self.gas_inspector.step(interp, context); } fn log( &mut self, - evm_data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, address: &Address, topics: &[B256], data: &Bytes, ) { - self.gas_inspector.log(evm_data, address, topics, data); + self.gas_inspector.log(context, address, topics, data); } - fn step_end(&mut self, interp: &mut Interpreter<'_>, data: &mut EVMData<'_, DB>) { - self.gas_inspector.step_end(interp, data); + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + self.gas_inspector.step_end(interp, context); self.gas_remaining_steps .push((self.pc, self.gas_inspector.gas_remaining())); } fn call( &mut self, - data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, call: &mut CallInputs, - ) -> (InstructionResult, Gas, Bytes) { - self.gas_inspector.call(data, call); - - ( - InstructionResult::Continue, - Gas::new(call.gas_limit), - Bytes::new(), - ) + ) -> Option<(InterpreterResult, Range)> { + self.gas_inspector.call(context, call) } fn call_end( &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CallInputs, - remaining_gas: Gas, - ret: InstructionResult, - out: Bytes, - ) -> (InstructionResult, Gas, Bytes) { - self.gas_inspector - .call_end(data, inputs, remaining_gas, ret, out.clone()); - (ret, remaining_gas, out) + context: &mut EvmContext<'_, DB>, + result: InterpreterResult, + ) -> InterpreterResult { + self.gas_inspector.call_end(context, result) } fn create( &mut self, - data: &mut EVMData<'_, DB>, + context: &mut EvmContext<'_, DB>, call: &mut CreateInputs, - ) -> (InstructionResult, Option
, Gas, Bytes) { - self.gas_inspector.create(data, call); - - ( - InstructionResult::Continue, - None, - Gas::new(call.gas_limit), - Bytes::new(), - ) + ) -> Option<(InterpreterResult, Option
)> { + self.gas_inspector.create(context, call); + None } fn create_end( &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CreateInputs, - status: InstructionResult, + context: &mut EvmContext<'_, DB>, + result: InterpreterResult, address: Option
, - gas: Gas, - retdata: Bytes, - ) -> (InstructionResult, Option
, Gas, Bytes) { - self.gas_inspector - .create_end(data, inputs, status, address, gas, retdata.clone()); - (status, address, gas, retdata) + ) -> (InterpreterResult, Option
) { + self.gas_inspector.create_end(context, result, address) } } diff --git a/crates/revm/src/inspector/instruction.rs b/crates/revm/src/inspector/instruction.rs index fb9eec5d1c..1566415811 100644 --- a/crates/revm/src/inspector/instruction.rs +++ b/crates/revm/src/inspector/instruction.rs @@ -12,16 +12,16 @@ pub fn inspector_instruction<'a, SPEC: Spec + 'static, DB: Database>( instruction: Instruction>, ) -> BoxedInstruction<'a, EVMImpl<'a, SPEC, DB>> { let inspector_instruction = Box::new( - move |interpreter: &mut Interpreter<'_>, host: &mut EVMImpl<'a, SPEC, DB>| { + move |interpreter: &mut Interpreter, host: &mut EVMImpl<'a, SPEC, DB>| { // step - let data = &mut host.data; + let context = &mut host.context; // Safety: as the PC was already incremented we need to subtract 1 to preserve the // old Inspector behavior. unsafe { interpreter.instruction_pointer = interpreter.instruction_pointer.sub(1); } if let Some(inspector) = host.inspector.as_mut() { - inspector.step(interpreter, data); + inspector.step(interpreter, context); if interpreter.instruction_result != InstructionResult::Continue { return; } @@ -36,9 +36,9 @@ pub fn inspector_instruction<'a, SPEC: Spec + 'static, DB: Database>( instruction(interpreter, host); // step ends - let data = &mut host.data; + let context = &mut host.context; if let Some(inspector) = host.inspector.as_mut() { - inspector.step_end(interpreter, data); + inspector.step_end(interpreter, context); } }, ); diff --git a/crates/revm/src/journaled_state.rs b/crates/revm/src/journaled_state.rs index 29459c78b1..a0595eb998 100644 --- a/crates/revm/src/journaled_state.rs +++ b/crates/revm/src/journaled_state.rs @@ -28,7 +28,7 @@ pub struct JournaledState { pub spec: SpecId, /// Precompiles addresses are used to check if loaded address /// should be considered cold or hot loaded. It is cloned from - /// EVMData to be directly accessed from JournaledState. + /// EvmContext to be directly accessed from JournaledState. /// /// Note that addresses are sorted. pub precompile_addresses: Vec
, @@ -791,7 +791,7 @@ pub enum JournalEntry { } /// SubRoutine checkpoint that will help us to go back from this -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct JournalCheckpoint { log_i: usize, journal_i: usize, diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 9cf765a570..93f7c63086 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -4,6 +4,9 @@ #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(all(feature = "with-serde", not(feature = "serde")))] +compile_error!("`with-serde` feature has been renamed to `serde`."); + #[macro_use] extern crate alloc; @@ -11,6 +14,7 @@ pub mod db; mod evm; mod evm_context; mod evm_impl; +mod frame; pub mod handler; mod inspector; mod journaled_state; @@ -18,20 +22,16 @@ mod journaled_state; #[cfg(feature = "optimism")] pub mod optimism; -#[cfg(all(feature = "with-serde", not(feature = "serde")))] -compile_error!("`with-serde` feature has been renamed to `serde`."); - -pub(crate) const USE_GAS: bool = !cfg!(feature = "no_gas_measuring"); - pub type DummyStateDB = InMemoryDB; #[cfg(feature = "std")] pub use db::{ CacheState, DBBox, State, StateBuilder, StateDBBox, TransitionAccount, TransitionState, }; pub use db::{Database, DatabaseCommit, DatabaseRef, InMemoryDB}; -pub use evm::{evm_inner, new, EVM}; -pub use evm_context::EVMData; -pub use evm_impl::{EVMImpl, Transact, CALL_STACK_LIMIT}; +pub use evm::{new, EVM}; +pub use evm_context::EvmContext; +pub use evm_impl::{new_evm, EVMImpl, Transact, CALL_STACK_LIMIT}; +pub use frame::CallStackFrame; pub use journaled_state::{JournalCheckpoint, JournalEntry, JournaledState}; // reexport `revm_precompiles` diff --git a/documentation/src/crates/interpreter.md b/documentation/src/crates/interpreter.md index 6c91bf1cce..1f728084af 100644 --- a/documentation/src/crates/interpreter.md +++ b/documentation/src/crates/interpreter.md @@ -15,10 +15,6 @@ External Crates: - [alloc](https://doc.rust-lang.org/alloc/): The alloc crate is used to provide the ability to allocate memory on the heap. It's a part of Rust's standard library that can be used in environments without a full host OS. - [core](https://doc.rust-lang.org/core/): The core crate is the dependency-free foundation of the Rust standard library. It includes fundamental types, macros, and traits. -Constants: - -- `USE_GAS`: This constant determines whether gas measurement should be enabled. It's set to false if the `no_gas_measuring` feature is enabled. - Re-exports: - Several types and functions are re-exported for easier access by users of this library, such as `Gas`, `Host`, `InstructionResult`, `OpCode`, `Interpreter`, `Memory`, `Stack`, and others. This allows users to import these items directly from the library root instead of from their individual modules. - `revm_primitives`: This crate is re-exported, providing primitive types or functionality used in the EVM implementation. diff --git a/documentation/src/crates/revm.md b/documentation/src/crates/revm.md index d85be6329b..adee43a5ea 100644 --- a/documentation/src/crates/revm.md +++ b/documentation/src/crates/revm.md @@ -14,10 +14,6 @@ External Crates: - `alloc`: The alloc crate is used to provide the ability to allocate memory on the heap. It's a part of Rust's standard library that can be used in environments without a full host OS. -Constants: - -- `USE_GAS`: This constant determines whether gas measurement should be used. It's set to false if the no_gas_measuring feature is enabled. - Re-exported Crates: - `revm_precompile`: This crate is re-exported, providing the precompiled contracts used in the EVM implementation. @@ -28,6 +24,6 @@ Re-exported Types: - `Database`, `DatabaseCommit`, `InMemoryDB`: These types from the `db` module are re-exported for handling the database operations. - `EVM`: The `EVM` struct from the `evm` module is re-exported, serving as the main interface to the EVM implementation. -- `EVMData`: The `EVMData` struct from the `evm_impl` module is re-exported, likely providing data structures to encapsulate EVM execution data. +- `EvmContext`: The `EvmContext` struct from the `evm_impl` module is re-exported, likely providing data structures to encapsulate EVM execution data. - `JournalEntry`, `JournaledState`: These types from the `journaled_state` module are re-exported, providing the journaling system for the EVM state. - `inspectors`, `Inspector`: The `Inspector` trait and its implementations from the `inspector` module are re-exported for observing the EVM execution.