diff --git a/Cargo.toml b/Cargo.toml index 2a2134771..7b0a5fea0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,15 @@ +[workspace] +members = [ + "interpreter", + "jsontests", + "precompile", + "tracer", +] +resolver = "2" + [workspace.package] +edition = "2021" +rust-version = "1.60.0" license = "Apache-2.0" authors = ["rust-evm Developers "] repository = "https://github.com/rust-ethereum/evm" @@ -7,7 +18,8 @@ keywords = ["no_std", "ethereum", "evm"] [package] name = "evm" version = "1.0.0-dev" -edition = "2021" +edition = { workspace = true } +rust-version = { workspace = true } license = { workspace = true } authors = { workspace = true } repository = { workspace = true } @@ -27,20 +39,12 @@ std = [ "sha3/std", "evm-interpreter/std", ] -with-codec = [ +scale = [ "primitive-types/codec", "primitive-types/scale-info", - "evm-interpreter/with-codec", + "evm-interpreter/scale", ] -with-serde = [ +serde = [ "primitive-types/impl-serde", - "evm-interpreter/with-serde", -] - -[workspace] -members = [ - "interpreter", - "jsontests", - "precompile", - "tracer", + "evm-interpreter/serde", ] diff --git a/README.md b/README.md index ee443cc5a..1d7c7f8ae 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ interpreter that can be easily customized. ## Status The Rust EVM project has a long history dating back to the initial -implementation in 2017 (when it was called SputnikVM). It has went through -multiple rewrites over the years to accomodate for different requirements, when -we successfully tested one integrating Geth to sync the mainnet. +implementation in 2017 (when it was called SputnikVM). It has gone through +multiple rewrites over the years to accommodate for different requirements, +when we successfully tested one integrating Geth to sync the mainnet. The current rewrite is used in production for the Frontier project (the Ethereum-compatibility layer for Polkadot). However, we have not yet fully diff --git a/interpreter/Cargo.toml b/interpreter/Cargo.toml index 199483d04..e23509501 100644 --- a/interpreter/Cargo.toml +++ b/interpreter/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "evm-interpreter" version = "1.0.0-dev" -edition = "2021" +edition = { workspace = true } +rust-version = { workspace = true } license = { workspace = true } authors = { workspace = true } repository = { workspace = true } @@ -9,13 +10,14 @@ keywords = { workspace = true } description = "The interpreter part of Ethereum Virtual Machine" [dependencies] +auto_impl = "1.2" +paste = "1.0" primitive-types = { version = "0.12", default-features = false, features = ["rlp"] } rlp = { version = "0.5", default-features = false } scale-codec = { package = "parity-scale-codec", version = "3.2", default-features = false, features = ["derive", "full"], optional = true } scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true } serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } sha3 = { version = "0.10", default-features = false } -auto_impl = "1.1" [dev-dependencies] hex = "0.4" @@ -25,17 +27,17 @@ default = ["std"] std = [ "primitive-types/std", "rlp/std", - "serde/std", "scale-codec/std", "scale-info/std", + "serde/std", "sha3/std", ] -with-codec = [ +scale = [ "scale-codec", "scale-info", "primitive-types/impl-codec", ] -with-serde = [ - "serde", +serde = [ + "dep:serde", "primitive-types/impl-serde", ] diff --git a/interpreter/src/error.rs b/interpreter/src/error/exit.rs similarity index 60% rename from interpreter/src/error.rs rename to interpreter/src/error/exit.rs index 69c1d5ff7..dcf797168 100644 --- a/interpreter/src/error.rs +++ b/interpreter/src/error/exit.rs @@ -1,31 +1,7 @@ -use crate::Opcode; use alloc::borrow::Cow; +use core::fmt; -/// Capture represents the result of execution. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum Capture { - /// The machine has exited. It cannot be executed again. - Exit(E), - /// The machine has trapped. It is waiting for external information, and can - /// be executed again. - Trap(T), -} - -impl Capture { - pub fn exit(self) -> Option { - match self { - Self::Exit(e) => Some(e), - Self::Trap(_) => None, - } - } - - pub fn trap(self) -> Option { - match self { - Self::Exit(_) => None, - Self::Trap(t) => Some(t), - } - } -} +use crate::opcode::Opcode; /// Exit result. pub type ExitResult = Result; @@ -33,10 +9,10 @@ pub type ExitResult = Result; /// Exit reason. #[derive(Clone, Debug, Eq, PartialEq)] #[cfg_attr( - feature = "with-codec", + feature = "scale", derive(scale_codec::Encode, scale_codec::Decode, scale_info::TypeInfo) )] -#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum ExitError { /// Machine returns a normal EVM error. Exception(ExitException), @@ -54,30 +30,25 @@ impl From for ExitResult { } #[cfg(feature = "std")] -impl std::error::Error for ExitError { - fn description(&self) -> &str { +impl std::error::Error for ExitError {} + +impl fmt::Display for ExitError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Exception(_) => "EVM exit exception", - Self::Reverted => "EVM internal revert", - Self::Fatal(_) => "EVM fatal error", + Self::Exception(_) => f.write_str("EVM exit exception"), + Self::Reverted => f.write_str("EVM internal revert"), + Self::Fatal(_) => f.write_str("EVM fatal error"), } } } -#[cfg(feature = "std")] -impl std::fmt::Display for ExitError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:?}") - } -} - /// Exit succeed reason. #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[cfg_attr( - feature = "with-codec", + feature = "scale", derive(scale_codec::Encode, scale_codec::Decode, scale_info::TypeInfo) )] -#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum ExitSucceed { /// Machine encountered an explicit stop. Stopped, @@ -96,67 +67,67 @@ impl From for ExitResult { /// Exit error reason. #[derive(Clone, Debug, Eq, PartialEq)] #[cfg_attr( - feature = "with-codec", + feature = "scale", derive(scale_codec::Encode, scale_codec::Decode, scale_info::TypeInfo) )] -#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum ExitException { /// Trying to pop from an empty stack. - #[cfg_attr(feature = "with-codec", codec(index = 0))] + #[cfg_attr(feature = "scale", codec(index = 0))] StackUnderflow, /// Trying to push into a stack over stack limit. - #[cfg_attr(feature = "with-codec", codec(index = 1))] + #[cfg_attr(feature = "scale", codec(index = 1))] StackOverflow, /// Jump destination is invalid. - #[cfg_attr(feature = "with-codec", codec(index = 2))] + #[cfg_attr(feature = "scale", codec(index = 2))] InvalidJump, /// An opcode accesses memory region, but the region is invalid. - #[cfg_attr(feature = "with-codec", codec(index = 3))] + #[cfg_attr(feature = "scale", codec(index = 3))] InvalidRange, /// Encountered the designated invalid opcode. - #[cfg_attr(feature = "with-codec", codec(index = 4))] + #[cfg_attr(feature = "scale", codec(index = 4))] DesignatedInvalid, /// Call stack is too deep (runtime). - #[cfg_attr(feature = "with-codec", codec(index = 5))] + #[cfg_attr(feature = "scale", codec(index = 5))] CallTooDeep, /// Create opcode encountered collision (runtime). - #[cfg_attr(feature = "with-codec", codec(index = 6))] + #[cfg_attr(feature = "scale", codec(index = 6))] CreateCollision, /// Create init code exceeds limit (runtime). - #[cfg_attr(feature = "with-codec", codec(index = 7))] + #[cfg_attr(feature = "scale", codec(index = 7))] CreateContractLimit, /// Invalid opcode during execution or starting byte is 0xef ([EIP-3541](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3541.md)). - #[cfg_attr(feature = "with-codec", codec(index = 15))] + #[cfg_attr(feature = "scale", codec(index = 15))] InvalidOpcode(Opcode), /// An opcode accesses external information, but the request is off offset /// limit (runtime). - #[cfg_attr(feature = "with-codec", codec(index = 8))] + #[cfg_attr(feature = "scale", codec(index = 8))] OutOfOffset, /// Execution runs out of gas (runtime). - #[cfg_attr(feature = "with-codec", codec(index = 9))] + #[cfg_attr(feature = "scale", codec(index = 9))] OutOfGas, /// Not enough fund to start the execution (runtime). - #[cfg_attr(feature = "with-codec", codec(index = 10))] + #[cfg_attr(feature = "scale", codec(index = 10))] OutOfFund, /// PC underflowed (unused). #[allow(clippy::upper_case_acronyms)] - #[cfg_attr(feature = "with-codec", codec(index = 11))] + #[cfg_attr(feature = "scale", codec(index = 11))] PCUnderflow, /// Attempt to create an empty account (runtime, unused). - #[cfg_attr(feature = "with-codec", codec(index = 12))] + #[cfg_attr(feature = "scale", codec(index = 12))] CreateEmpty, /// Nonce reached maximum value of 2^64-1 /// https://eips.ethereum.org/EIPS/eip-2681 - #[cfg_attr(feature = "with-codec", codec(index = 14))] + #[cfg_attr(feature = "scale", codec(index = 14))] MaxNonce, /// Other normal errors. - #[cfg_attr(feature = "with-codec", codec(index = 13))] + #[cfg_attr(feature = "scale", codec(index = 13))] Other(Cow<'static, str>), } @@ -175,10 +146,10 @@ impl From for ExitError { /// Exit fatal reason. #[derive(Clone, Debug, Eq, PartialEq)] #[cfg_attr( - feature = "with-codec", + feature = "scale", derive(scale_codec::Encode, scale_codec::Decode, scale_info::TypeInfo) )] -#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum ExitFatal { /// The operation is not supported. NotSupported, diff --git a/interpreter/src/error/mod.rs b/interpreter/src/error/mod.rs new file mode 100644 index 000000000..2473c07a0 --- /dev/null +++ b/interpreter/src/error/mod.rs @@ -0,0 +1,30 @@ +mod exit; +mod trap; + +pub use self::{exit::*, trap::*}; + +/// Capture represents the result of execution. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Capture { + /// The machine has exited. It cannot be executed again. + Exit(E), + /// The machine has trapped. It is waiting for external information, and can + /// be executed again. + Trap(T), +} + +impl Capture { + pub fn exit(self) -> Option { + match self { + Self::Exit(e) => Some(e), + Self::Trap(_) => None, + } + } + + pub fn trap(self) -> Option { + match self { + Self::Exit(_) => None, + Self::Trap(t) => Some(t), + } + } +} diff --git a/interpreter/src/trap.rs b/interpreter/src/error/trap.rs similarity index 96% rename from interpreter/src/trap.rs rename to interpreter/src/error/trap.rs index 8f2e43a65..66764b3fb 100644 --- a/interpreter/src/trap.rs +++ b/interpreter/src/error/trap.rs @@ -1,16 +1,22 @@ //! Call and create trap handler. -use crate::utils::{h256_to_u256, u256_to_usize}; -use crate::{ - interpreter::Interpreter, Context, ExitError, ExitException, ExitResult, Machine, Memory, - RuntimeBackend, RuntimeState, Transfer, -}; use alloc::vec::Vec; -use core::cmp::{max, min}; -use core::convert::Infallible; +use core::{ + cmp::{max, min}, + convert::Infallible, +}; + use primitive_types::{H160, H256, U256}; use sha3::{Digest, Keccak256}; +use crate::{ + error::{ExitError, ExitException, ExitResult}, + interpreter::Interpreter, + machine::{Machine, Memory}, + runtime::{Context, RuntimeBackend, RuntimeState, Transfer}, + utils::{h256_to_u256, u256_to_usize}, +}; + pub trait TrapConstruct { fn construct(v: T) -> Self; } @@ -21,72 +27,6 @@ pub trait TrapConsume { fn consume(self) -> Result; } -/// Create scheme. -#[derive(Clone, Copy, Eq, PartialEq, Debug)] -pub enum CreateScheme { - /// Legacy create scheme of `CREATE`. - Legacy { - /// Caller of the create. - caller: H160, - }, - /// Create scheme of `CREATE2`. - Create2 { - /// Caller of the create. - caller: H160, - /// Code hash. - code_hash: H256, - /// Salt. - salt: H256, - }, -} - -impl CreateScheme { - pub fn address(&self, handler: &H) -> H160 { - match self { - Self::Create2 { - caller, - code_hash, - salt, - } => { - let mut hasher = Keccak256::new(); - hasher.update([0xff]); - hasher.update(&caller[..]); - hasher.update(&salt[..]); - hasher.update(&code_hash[..]); - H256::from_slice(hasher.finalize().as_slice()).into() - } - Self::Legacy { caller } => { - let nonce = handler.nonce(*caller); - let mut stream = rlp::RlpStream::new_list(2); - stream.append(caller); - stream.append(&nonce); - H256::from_slice(Keccak256::digest(&stream.out()).as_slice()).into() - } - } - } - - #[must_use] - pub const fn caller(&self) -> H160 { - match self { - Self::Create2 { caller, .. } => *caller, - Self::Legacy { caller } => *caller, - } - } -} - -/// Call scheme. -#[derive(Clone, Copy, Eq, PartialEq, Debug)] -pub enum CallScheme { - /// `CALL` - Call, - /// `CALLCODE` - CallCode, - /// `DELEGATECALL` - DelegateCall, - /// `STATICCALL` - StaticCall, -} - pub enum CallCreateTrap { Create, Create2, @@ -161,6 +101,20 @@ impl CallCreateTrapData { } } +/// Call scheme. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub enum CallScheme { + /// `CALL` + Call, + /// `CALLCODE` + CallCode, + /// `DELEGATECALL` + DelegateCall, + /// `STATICCALL` + StaticCall, +} + +#[derive(Clone, Debug)] pub struct CallTrapData { pub target: H160, pub transfer: Option, @@ -376,6 +330,59 @@ impl CallTrapData { } } +/// Create scheme. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub enum CreateScheme { + /// Legacy create scheme of `CREATE`. + Legacy { + /// Caller of the create call. + caller: H160, + }, + /// Create scheme of `CREATE2`. + Create2 { + /// Caller of the create call. + caller: H160, + /// Code hash. + code_hash: H256, + /// Salt. + salt: H256, + }, +} + +impl CreateScheme { + pub fn address(&self, handler: &H) -> H160 { + match self { + Self::Create2 { + caller, + code_hash, + salt, + } => { + let mut hasher = Keccak256::new(); + hasher.update([0xff]); + hasher.update(&caller[..]); + hasher.update(&salt[..]); + hasher.update(&code_hash[..]); + H256::from_slice(hasher.finalize().as_slice()).into() + } + Self::Legacy { caller } => { + let nonce = handler.nonce(*caller); + let mut stream = rlp::RlpStream::new_list(2); + stream.append(caller); + stream.append(&nonce); + H256::from_slice(Keccak256::digest(&stream.out()).as_slice()).into() + } + } + } + + #[must_use] + pub const fn caller(&self) -> H160 { + match self { + Self::Create2 { caller, .. } => *caller, + Self::Legacy { caller } => *caller, + } + } +} + #[derive(Clone, Debug)] pub struct CreateTrapData { pub scheme: CreateScheme, diff --git a/interpreter/src/etable.rs b/interpreter/src/etable.rs index 45aa98917..1e7d3d82a 100644 --- a/interpreter/src/etable.rs +++ b/interpreter/src/etable.rs @@ -1,9 +1,15 @@ +use core::{ + marker::PhantomData, + ops::{Deref, DerefMut}, +}; + use crate::{ - eval::*, trap::CallCreateTrap, ExitResult, GasState, Machine, Opcode, RuntimeBackend, - RuntimeEnvironment, RuntimeState, TrapConstruct, + error::{CallCreateTrap, ExitResult, TrapConstruct}, + eval::*, + machine::Machine, + opcode::Opcode, + runtime::{GasState, RuntimeBackend, RuntimeEnvironment, RuntimeState}, }; -use core::marker::PhantomData; -use core::ops::{Deref, DerefMut}; pub trait EtableSet { type State; @@ -146,6 +152,7 @@ impl Etable { table[Opcode::MULMOD.as_usize()] = eval_mulmod as _; table[Opcode::EXP.as_usize()] = eval_exp as _; table[Opcode::SIGNEXTEND.as_usize()] = eval_signextend as _; + table[Opcode::LT.as_usize()] = eval_lt as _; table[Opcode::GT.as_usize()] = eval_gt as _; table[Opcode::SLT.as_usize()] = eval_slt as _; @@ -157,22 +164,27 @@ impl Etable { table[Opcode::XOR.as_usize()] = eval_xor as _; table[Opcode::NOT.as_usize()] = eval_not as _; table[Opcode::BYTE.as_usize()] = eval_byte as _; + table[Opcode::SHL.as_usize()] = eval_shl as _; table[Opcode::SHR.as_usize()] = eval_shr as _; table[Opcode::SAR.as_usize()] = eval_sar as _; - table[Opcode::CODESIZE.as_usize()] = eval_codesize as _; - table[Opcode::CODECOPY.as_usize()] = eval_codecopy as _; + table[Opcode::CALLDATALOAD.as_usize()] = eval_calldataload as _; table[Opcode::CALLDATASIZE.as_usize()] = eval_calldatasize as _; table[Opcode::CALLDATACOPY.as_usize()] = eval_calldatacopy as _; + table[Opcode::CODESIZE.as_usize()] = eval_codesize as _; + table[Opcode::CODECOPY.as_usize()] = eval_codecopy as _; + table[Opcode::POP.as_usize()] = eval_pop as _; table[Opcode::MLOAD.as_usize()] = eval_mload as _; table[Opcode::MSTORE.as_usize()] = eval_mstore as _; table[Opcode::MSTORE8.as_usize()] = eval_mstore8 as _; + table[Opcode::JUMP.as_usize()] = eval_jump as _; table[Opcode::JUMPI.as_usize()] = eval_jumpi as _; table[Opcode::PC.as_usize()] = eval_pc as _; table[Opcode::MSIZE.as_usize()] = eval_msize as _; + table[Opcode::JUMPDEST.as_usize()] = eval_jumpdest as _; table[Opcode::PUSH0.as_usize()] = eval_push0 as _; @@ -244,7 +256,9 @@ impl Etable { table[Opcode::SWAP16.as_usize()] = eval_swap16 as _; table[Opcode::RETURN.as_usize()] = eval_return as _; + table[Opcode::REVERT.as_usize()] = eval_revert as _; + table[Opcode::INVALID.as_usize()] = eval_invalid as _; Self(table, PhantomData) @@ -261,43 +275,55 @@ where let mut table = Self::core(); table.0[Opcode::SHA3.as_usize()] = eval_sha3 as _; + table.0[Opcode::ADDRESS.as_usize()] = eval_address as _; table.0[Opcode::BALANCE.as_usize()] = eval_balance as _; - table.0[Opcode::SELFBALANCE.as_usize()] = eval_selfbalance as _; table.0[Opcode::ORIGIN.as_usize()] = eval_origin as _; table.0[Opcode::CALLER.as_usize()] = eval_caller as _; table.0[Opcode::CALLVALUE.as_usize()] = eval_callvalue as _; + table.0[Opcode::GASPRICE.as_usize()] = eval_gasprice as _; table.0[Opcode::EXTCODESIZE.as_usize()] = eval_extcodesize as _; - table.0[Opcode::EXTCODEHASH.as_usize()] = eval_extcodehash as _; table.0[Opcode::EXTCODECOPY.as_usize()] = eval_extcodecopy as _; table.0[Opcode::RETURNDATASIZE.as_usize()] = eval_returndatasize as _; table.0[Opcode::RETURNDATACOPY.as_usize()] = eval_returndatacopy as _; + table.0[Opcode::EXTCODEHASH.as_usize()] = eval_extcodehash as _; + table.0[Opcode::BLOCKHASH.as_usize()] = eval_blockhash as _; table.0[Opcode::COINBASE.as_usize()] = eval_coinbase as _; table.0[Opcode::TIMESTAMP.as_usize()] = eval_timestamp as _; table.0[Opcode::NUMBER.as_usize()] = eval_number as _; table.0[Opcode::DIFFICULTY.as_usize()] = eval_difficulty as _; table.0[Opcode::GASLIMIT.as_usize()] = eval_gaslimit as _; + table.0[Opcode::CHAINID.as_usize()] = eval_chainid as _; + table.0[Opcode::SELFBALANCE.as_usize()] = eval_selfbalance as _; + table.0[Opcode::BASEFEE.as_usize()] = eval_basefee as _; + table.0[Opcode::SLOAD.as_usize()] = eval_sload as _; table.0[Opcode::SSTORE.as_usize()] = eval_sstore as _; + table.0[Opcode::GAS.as_usize()] = eval_gas as _; + + table.0[Opcode::TLOAD.as_usize()] = eval_tload as _; + table.0[Opcode::TSTORE.as_usize()] = eval_tstore as _; + table.0[Opcode::LOG0.as_usize()] = eval_log0 as _; table.0[Opcode::LOG1.as_usize()] = eval_log1 as _; table.0[Opcode::LOG2.as_usize()] = eval_log2 as _; table.0[Opcode::LOG3.as_usize()] = eval_log3 as _; table.0[Opcode::LOG4.as_usize()] = eval_log4 as _; - table.0[Opcode::SUICIDE.as_usize()] = eval_suicide as _; - table.0[Opcode::CHAINID.as_usize()] = eval_chainid as _; - table.0[Opcode::BASEFEE.as_usize()] = eval_basefee as _; table.0[Opcode::CREATE.as_usize()] = eval_call_create_trap as _; - table.0[Opcode::CREATE2.as_usize()] = eval_call_create_trap as _; table.0[Opcode::CALL.as_usize()] = eval_call_create_trap as _; table.0[Opcode::CALLCODE.as_usize()] = eval_call_create_trap as _; + table.0[Opcode::DELEGATECALL.as_usize()] = eval_call_create_trap as _; + table.0[Opcode::CREATE2.as_usize()] = eval_call_create_trap as _; + table.0[Opcode::STATICCALL.as_usize()] = eval_call_create_trap as _; + table.0[Opcode::SUICIDE.as_usize()] = eval_suicide as _; + table } } diff --git a/interpreter/src/eval/arithmetic.rs b/interpreter/src/eval/arithmetic.rs index ae6e9a15c..07045d003 100644 --- a/interpreter/src/eval/arithmetic.rs +++ b/interpreter/src/eval/arithmetic.rs @@ -1,8 +1,9 @@ -use crate::utils::I256; -use core::convert::TryInto; use core::ops::Rem; + use primitive_types::{U256, U512}; +use crate::utils::I256; + #[inline] pub fn div(op1: U256, op2: U256) -> U256 { if op2 == U256::zero() { diff --git a/interpreter/src/eval/bitwise.rs b/interpreter/src/eval/bitwise.rs index 49883fd1c..23bb5dd3d 100644 --- a/interpreter/src/eval/bitwise.rs +++ b/interpreter/src/eval/bitwise.rs @@ -1,6 +1,7 @@ -use crate::utils::{Sign, I256}; use primitive_types::U256; +use crate::utils::{Sign, I256}; + #[inline] pub fn slt(op1: U256, op2: U256) -> U256 { let op1: I256 = op1.into(); diff --git a/interpreter/src/eval/misc.rs b/interpreter/src/eval/misc.rs index 0a9b9c5a3..657751fa0 100644 --- a/interpreter/src/eval/misc.rs +++ b/interpreter/src/eval/misc.rs @@ -1,9 +1,14 @@ -use super::Control; -use crate::utils::u256_to_h256; -use crate::{ExitError, ExitException, ExitFatal, ExitSucceed, Machine}; use core::cmp::min; + use primitive_types::{H256, U256}; +use crate::{ + error::{ExitError, ExitException, ExitFatal, ExitSucceed}, + etable::Control, + machine::Machine, + utils::u256_to_h256, +}; + #[inline] pub fn codesize(state: &mut Machine) -> Control { let stack = &mut state.stack; diff --git a/interpreter/src/eval/mod.rs b/interpreter/src/eval/mod.rs index ffd7aa296..c3554053b 100644 --- a/interpreter/src/eval/mod.rs +++ b/interpreter/src/eval/mod.rs @@ -5,13 +5,18 @@ mod bitwise; mod misc; mod system; -use crate::{ - trap::CallCreateTrap, Control, ExitException, ExitSucceed, GasState, Machine, Opcode, - RuntimeBackend, RuntimeEnvironment, RuntimeState, TrapConstruct, -}; use core::ops::{BitAnd, BitOr, BitXor}; + use primitive_types::{H256, U256}; +use crate::{ + error::{CallCreateTrap, ExitException, ExitSucceed, TrapConstruct}, + etable::Control, + machine::Machine, + opcode::Opcode, + runtime::{GasState, RuntimeBackend, RuntimeEnvironment, RuntimeState}, +}; + pub fn eval_pass( _machine: &mut Machine, _handle: &mut H, @@ -381,590 +386,59 @@ pub fn eval_jumpdest( Control::Continue } -pub fn eval_push0( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 0, position) -} - -pub fn eval_push1( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 1, position) -} - -pub fn eval_push2( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 2, position) -} - -pub fn eval_push3( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 3, position) -} - -pub fn eval_push4( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 4, position) -} - -pub fn eval_push5( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 5, position) -} - -pub fn eval_push6( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 6, position) -} - -pub fn eval_push7( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 7, position) -} - -pub fn eval_push8( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 8, position) -} - -pub fn eval_push9( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 9, position) -} - -pub fn eval_push10( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 10, position) -} - -pub fn eval_push11( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 11, position) -} - -pub fn eval_push12( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 12, position) -} - -pub fn eval_push13( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 13, position) -} - -pub fn eval_push14( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 14, position) -} - -pub fn eval_push15( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 15, position) -} - -pub fn eval_push16( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 16, position) -} - -pub fn eval_push17( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 17, position) -} - -pub fn eval_push18( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 18, position) -} - -pub fn eval_push19( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 19, position) -} - -pub fn eval_push20( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 20, position) -} - -pub fn eval_push21( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 21, position) -} - -pub fn eval_push22( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 22, position) -} - -pub fn eval_push23( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 23, position) -} - -pub fn eval_push24( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 24, position) -} - -pub fn eval_push25( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 25, position) -} - -pub fn eval_push26( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 26, position) -} - -pub fn eval_push27( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 27, position) -} - -pub fn eval_push28( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 28, position) -} - -pub fn eval_push29( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 29, position) -} - -pub fn eval_push30( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 30, position) -} - -pub fn eval_push31( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 31, position) -} - -pub fn eval_push32( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - position: usize, -) -> Control { - self::misc::push(machine, 32, position) -} - -pub fn eval_dup1( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 1) -} - -pub fn eval_dup2( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 2) -} - -pub fn eval_dup3( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 3) -} - -pub fn eval_dup4( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 4) -} - -pub fn eval_dup5( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 5) -} - -pub fn eval_dup6( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 6) -} - -pub fn eval_dup7( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 7) -} - -pub fn eval_dup8( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 8) -} - -pub fn eval_dup9( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 9) -} - -pub fn eval_dup10( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 10) -} - -pub fn eval_dup11( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 11) -} - -pub fn eval_dup12( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 12) -} - -pub fn eval_dup13( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 13) -} - -pub fn eval_dup14( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 14) -} - -pub fn eval_dup15( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 15) -} - -pub fn eval_dup16( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::dup(machine, 16) -} - -pub fn eval_swap1( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 1) -} - -pub fn eval_swap2( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 2) -} - -pub fn eval_swap3( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 3) -} - -pub fn eval_swap4( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 4) -} - -pub fn eval_swap5( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 5) -} - -pub fn eval_swap6( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 6) -} - -pub fn eval_swap7( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 7) -} - -pub fn eval_swap8( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 8) -} - -pub fn eval_swap9( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 9) -} - -pub fn eval_swap10( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 10) -} - -pub fn eval_swap11( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 11) -} - -pub fn eval_swap12( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 12) +macro_rules! eval_push { + ($($num:expr),*) => { + $(paste::paste! { + pub fn []( + machine: &mut Machine, + _handle: &mut H, + _opcode: Opcode, + position: usize, + ) -> Control { + self::misc::push(machine, $num, position) + } + })* + }; } -pub fn eval_swap13( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 13) +eval_push! { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 } -pub fn eval_swap14( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 14) +macro_rules! eval_dup { + ($($num:expr),*) => { + $(paste::paste! { + pub fn []( + machine: &mut Machine, + _handle: &mut H, + _opcode: Opcode, + _position: usize, + ) -> Control { + self::misc::dup(machine, $num) + } + })* + }; } -pub fn eval_swap15( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 15) +eval_dup! { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 } + +macro_rules! eval_swap { + ($($num:expr),*) => { + $(paste::paste! { + pub fn []( + machine: &mut Machine, + _handle: &mut H, + _opcode: Opcode, + _position: usize, + ) -> Control { + self::misc::swap(machine, $num) + } + })* + }; } -pub fn eval_swap16( - machine: &mut Machine, - _handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::misc::swap(machine, 16) -} +eval_swap! { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 } pub fn eval_return( machine: &mut Machine, @@ -1200,50 +674,40 @@ pub fn eval_gas( self::system::gas(machine, handle) } -pub fn eval_log0, H: RuntimeEnvironment + RuntimeBackend, Tr>( +pub fn eval_tload, H: RuntimeEnvironment + RuntimeBackend, Tr>( machine: &mut Machine, handle: &mut H, _opcode: Opcode, _position: usize, ) -> Control { - self::system::log(machine, 0, handle) + self::system::tload(machine, handle) } -pub fn eval_log1, H: RuntimeEnvironment + RuntimeBackend, Tr>( +pub fn eval_tstore, H: RuntimeEnvironment + RuntimeBackend, Tr>( machine: &mut Machine, handle: &mut H, _opcode: Opcode, _position: usize, ) -> Control { - self::system::log(machine, 1, handle) + self::system::tstore(machine, handle) } -pub fn eval_log2, H: RuntimeEnvironment + RuntimeBackend, Tr>( - machine: &mut Machine, - handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::system::log(machine, 2, handle) -} - -pub fn eval_log3, H: RuntimeEnvironment + RuntimeBackend, Tr>( - machine: &mut Machine, - handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::system::log(machine, 3, handle) +macro_rules! eval_log { + ($($num:expr),*) => { + $(paste::paste! { + pub fn [], H: RuntimeEnvironment + RuntimeBackend, Tr>( + machine: &mut Machine, + handle: &mut H, + _opcode: Opcode, + _position: usize, + ) -> Control { + self::system::log(machine, $num, handle) + } + })* + }; } -pub fn eval_log4, H: RuntimeEnvironment + RuntimeBackend, Tr>( - machine: &mut Machine, - handle: &mut H, - _opcode: Opcode, - _position: usize, -) -> Control { - self::system::log(machine, 4, handle) -} +eval_log! { 0, 1, 2, 3, 4 } pub fn eval_suicide, H: RuntimeEnvironment + RuntimeBackend, Tr>( machine: &mut Machine, diff --git a/interpreter/src/eval/system.rs b/interpreter/src/eval/system.rs index bd97880d5..6d2e07459 100644 --- a/interpreter/src/eval/system.rs +++ b/interpreter/src/eval/system.rs @@ -1,12 +1,15 @@ -use super::Control; -use crate::{ - ExitException, ExitFatal, ExitSucceed, GasState, Log, Machine, RuntimeBackend, - RuntimeEnvironment, RuntimeState, Transfer, -}; use alloc::vec::Vec; + use primitive_types::{H256, U256}; use sha3::{Digest, Keccak256}; +use crate::{ + error::{ExitException, ExitFatal, ExitSucceed}, + etable::Control, + machine::Machine, + runtime::{GasState, Log, RuntimeBackend, RuntimeEnvironment, RuntimeState, Transfer}, +}; + pub fn sha3, Tr>(machine: &mut Machine) -> Control { pop_u256!(machine, from, len); @@ -285,6 +288,28 @@ pub fn gas( Control::Continue } +pub fn tload, H: RuntimeEnvironment + RuntimeBackend, Tr>( + machine: &mut Machine, + handler: &mut H, +) -> Control { + pop!(machine, index); + let value = handler.transient_storage(machine.state.as_ref().context.address, index); + push!(machine, value); + + Control::Continue +} + +pub fn tstore, H: RuntimeEnvironment + RuntimeBackend, Tr>( + machine: &mut Machine, + handler: &mut H, +) -> Control { + pop!(machine, index, value); + match handler.set_transient_storage(machine.state.as_ref().context.address, index, value) { + Ok(()) => Control::Continue, + Err(e) => Control::Exit(e.into()), + } +} + pub fn log, H: RuntimeEnvironment + RuntimeBackend, Tr>( machine: &mut Machine, n: u8, diff --git a/interpreter/src/interpreter/etable.rs b/interpreter/src/interpreter/etable.rs index ea0df5833..83ecf1577 100644 --- a/interpreter/src/interpreter/etable.rs +++ b/interpreter/src/interpreter/etable.rs @@ -1,11 +1,14 @@ -use crate::interpreter::{Interpreter, RunInterpreter, StepInterpreter}; -use crate::{ - Capture, Control, EtableSet, ExitError, ExitException, ExitFatal, ExitResult, ExitSucceed, - Machine, Opcode, Stack, Valids, -}; use alloc::vec::Vec; use core::ops::{Deref, DerefMut}; +use crate::{ + error::{Capture, ExitError, ExitException, ExitFatal, ExitResult, ExitSucceed}, + etable::{Control, EtableSet}, + interpreter::{valids::Valids, Interpreter, RunInterpreter, StepInterpreter}, + machine::{Machine, Stack}, + opcode::Opcode, +}; + pub struct EtableInterpreter<'etable, ES: EtableSet> { valids: Valids, position: usize, diff --git a/interpreter/src/interpreter/mod.rs b/interpreter/src/interpreter/mod.rs index 37f66a83a..4d0788869 100644 --- a/interpreter/src/interpreter/mod.rs +++ b/interpreter/src/interpreter/mod.rs @@ -1,10 +1,14 @@ mod etable; +mod valids; -pub use self::etable::EtableInterpreter; - -use crate::{Capture, ExitResult, Machine}; use alloc::vec::Vec; +pub use self::etable::EtableInterpreter; +use crate::{ + error::{Capture, ExitResult}, + machine::Machine, +}; + pub trait Interpreter { type State; diff --git a/interpreter/src/valids.rs b/interpreter/src/interpreter/valids.rs similarity index 66% rename from interpreter/src/valids.rs rename to interpreter/src/interpreter/valids.rs index e9bc3613a..f9bba082d 100644 --- a/interpreter/src/valids.rs +++ b/interpreter/src/interpreter/valids.rs @@ -1,6 +1,7 @@ -use crate::Opcode; use alloc::vec::Vec; +use crate::opcode::Opcode; + /// Mapping of valid jump destination from code. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Valids(Vec); @@ -25,36 +26,17 @@ impl Valids { } } - Valids(valids) - } - - /// Get the length of the valid mapping. This is the same as the - /// code bytes. - #[inline] - #[must_use] - pub fn len(&self) -> usize { - self.0.len() - } - - /// Returns true if the valids list is empty - #[inline] - #[must_use] - pub fn is_empty(&self) -> bool { - self.len() == 0 + Self(valids) } - /// Returns `true` if the position is a valid jump destination. If - /// not, returns `false`. + /// Returns `true` if the position is a valid jump destination. + /// If not, returns `false`. #[must_use] pub fn is_valid(&self, position: usize) -> bool { if position >= self.0.len() { return false; } - if !self.0[position] { - return false; - } - - true + self.0[position] } } diff --git a/interpreter/src/lib.rs b/interpreter/src/lib.rs index 0e6693bef..f29887abb 100644 --- a/interpreter/src/lib.rs +++ b/interpreter/src/lib.rs @@ -6,78 +6,13 @@ extern crate alloc; -mod error; -mod etable; +pub mod error; +pub mod etable; pub mod eval; -pub mod interpreter; -mod memory; -mod opcode; -mod runtime; -mod stack; -pub mod trap; +mod interpreter; +pub mod machine; +pub mod opcode; +pub mod runtime; pub mod utils; -mod valids; -pub use crate::error::{Capture, ExitError, ExitException, ExitFatal, ExitResult, ExitSucceed}; -pub use crate::etable::{Control, Efn, Etable, EtableSet}; -pub use crate::memory::Memory; -pub use crate::opcode::Opcode; -pub use crate::runtime::{ - Context, GasState, Log, RuntimeBackend, RuntimeBaseBackend, RuntimeEnvironment, RuntimeState, - TransactionContext, Transfer, -}; -pub use crate::stack::Stack; -pub use crate::trap::{TrapConstruct, TrapConsume}; -pub use crate::valids::Valids; - -use alloc::rc::Rc; -use alloc::vec::Vec; - -/// Core execution layer for EVM. -pub struct Machine { - /// Program data. - data: Rc>, - /// Program code. - code: Rc>, - /// Return value. Note the difference between `retbuf`. - /// A `retval` holds what's returned by the current machine, with `RETURN` or `REVERT` opcode. - /// A `retbuf` holds the buffer of returned value by sub-calls. - pub retval: Vec, - /// Memory. - pub memory: Memory, - /// Stack. - pub stack: Stack, - /// Extra state, - pub state: S, -} - -impl Machine { - /// Machine code. - pub fn code(&self) -> &[u8] { - &self.code - } - - /// Create a new machine with given code and data. - pub fn new( - code: Rc>, - data: Rc>, - stack_limit: usize, - memory_limit: usize, - state: S, - ) -> Self { - Self { - data, - code, - retval: Vec::new(), - memory: Memory::new(memory_limit), - stack: Stack::new(stack_limit), - state, - } - } - - /// Whether the machine has empty code. - #[must_use] - pub fn is_empty(&self) -> bool { - self.code.is_empty() - } -} +pub use self::interpreter::{EtableInterpreter, Interpreter, RunInterpreter, StepInterpreter}; diff --git a/interpreter/src/memory.rs b/interpreter/src/machine/memory.rs similarity index 97% rename from interpreter/src/memory.rs rename to interpreter/src/machine/memory.rs index 0c6c92c5a..98abcc707 100644 --- a/interpreter/src/memory.rs +++ b/interpreter/src/machine/memory.rs @@ -1,10 +1,14 @@ -use crate::{ExitException, ExitFatal}; -use alloc::vec; -use alloc::vec::Vec; -use core::ops::{BitAnd, Not, Range}; -use core::{cmp::min, mem}; +use alloc::{vec, vec::Vec}; +use core::{ + cmp::min, + mem, + ops::{BitAnd, Not, Range}, +}; + use primitive_types::U256; +use crate::error::{ExitException, ExitFatal}; + /// A sequencial memory. It uses Rust's `Vec` for internal /// representation. #[derive(Clone, Debug)] diff --git a/interpreter/src/machine/mod.rs b/interpreter/src/machine/mod.rs new file mode 100644 index 000000000..94f224f67 --- /dev/null +++ b/interpreter/src/machine/mod.rs @@ -0,0 +1,55 @@ +mod memory; +mod stack; + +use alloc::{rc::Rc, vec::Vec}; + +pub use self::{memory::Memory, stack::Stack}; + +/// Core execution layer for EVM. +pub struct Machine { + /// Program data. + pub(crate) data: Rc>, + /// Program code. + pub(crate) code: Rc>, + /// Return value. Note the difference between `retbuf`. + /// A `retval` holds what's returned by the current machine, with `RETURN` or `REVERT` opcode. + /// A `retbuf` holds the buffer of returned value by sub-calls. + pub retval: Vec, + /// Memory. + pub memory: Memory, + /// Stack. + pub stack: Stack, + /// Extra state, + pub state: S, +} + +impl Machine { + /// Create a new machine with given code and data. + pub fn new( + code: Rc>, + data: Rc>, + stack_limit: usize, + memory_limit: usize, + state: S, + ) -> Self { + Self { + data, + code, + retval: Vec::new(), + memory: Memory::new(memory_limit), + stack: Stack::new(stack_limit), + state, + } + } + + /// Machine code. + pub fn code(&self) -> &[u8] { + &self.code + } + + /// Whether the machine has empty code. + #[must_use] + pub fn is_empty(&self) -> bool { + self.code.is_empty() + } +} diff --git a/interpreter/src/stack.rs b/interpreter/src/machine/stack.rs similarity index 93% rename from interpreter/src/stack.rs rename to interpreter/src/machine/stack.rs index 68b8c3fe7..8798d5897 100644 --- a/interpreter/src/stack.rs +++ b/interpreter/src/machine/stack.rs @@ -1,7 +1,9 @@ -use crate::{ExitError, ExitException}; use alloc::vec::Vec; + use primitive_types::H256; +use crate::error::{ExitError, ExitException}; + /// EVM stack. #[derive(Clone, Debug)] pub struct Stack { @@ -56,15 +58,15 @@ impl Stack { } } - #[inline] /// Stack limit. + #[inline] #[must_use] pub const fn limit(&self) -> usize { self.limit } - #[inline] /// Stack length. + #[inline] #[must_use] pub fn len(&self) -> usize { self.data.len() @@ -77,8 +79,8 @@ impl Stack { self.data.is_empty() } - #[inline] /// Stack data. + #[inline] #[must_use] pub const fn data(&self) -> &Vec { &self.data @@ -89,16 +91,17 @@ impl Stack { self.data.clear(); } + /// Pop a value from the stack. + /// If the stack is already empty, returns the `StackUnderflow` error. #[inline] - /// Pop a value from the stack. If the stack is already empty, returns the - /// `StackUnderflow` error. pub fn pop(&mut self) -> Result { self.data.pop().ok_or(ExitException::StackUnderflow) } + /// Push a new value into the stack. + /// If it exceeds the stack limit, returns `StackOverflow` error and + /// leaves the stack unchanged. #[inline] - /// Push a new value into the stack. If it will exceed the stack limit, - /// returns `StackOverflow` error and leaves the stack unchanged. pub fn push(&mut self, value: H256) -> Result<(), ExitException> { if self.data.len() + 1 > self.limit { return Err(ExitException::StackOverflow); @@ -135,10 +138,10 @@ impl Stack { } } - #[inline] /// Peek a value at given index for the stack, where the top of /// the stack is at index `0`. If the index is too large, /// `StackError::Underflow` is returned. + #[inline] pub fn peek(&self, no_from_top: usize) -> Result { if self.data.len() > no_from_top { Ok(self.data[self.data.len() - no_from_top - 1]) @@ -147,10 +150,10 @@ impl Stack { } } - #[inline] /// Set a value at given index for the stack, where the top of the /// stack is at index `0`. If the index is too large, /// `StackError::Underflow` is returned. + #[inline] pub fn set(&mut self, no_from_top: usize, val: H256) -> Result<(), ExitException> { if self.data.len() > no_from_top { let len = self.data.len(); diff --git a/interpreter/src/opcode.rs b/interpreter/src/opcode.rs index b4d0a4746..337cec153 100644 --- a/interpreter/src/opcode.rs +++ b/interpreter/src/opcode.rs @@ -1,10 +1,10 @@ /// Opcode enum. One-to-one corresponding to an `u8` value. #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[cfg_attr( - feature = "with-codec", + feature = "scale", derive(scale_codec::Encode, scale_codec::Decode, scale_info::TypeInfo) )] -#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Opcode(pub u8); // Core opcodes. @@ -57,6 +57,13 @@ impl Opcode { /// `BYTE` pub const BYTE: Opcode = Opcode(0x1a); + /// `SHL` + pub const SHL: Opcode = Opcode(0x1b); + /// `SHR` + pub const SHR: Opcode = Opcode(0x1c); + /// `SAR` + pub const SAR: Opcode = Opcode(0x1d); + /// `CALLDATALOAD` pub const CALLDATALOAD: Opcode = Opcode(0x35); /// `CALLDATASIZE` @@ -68,13 +75,6 @@ impl Opcode { /// `CODECOPY` pub const CODECOPY: Opcode = Opcode(0x39); - /// `SHL` - pub const SHL: Opcode = Opcode(0x1b); - /// `SHR` - pub const SHR: Opcode = Opcode(0x1c); - /// `SAR` - pub const SAR: Opcode = Opcode(0x1d); - /// `POP` pub const POP: Opcode = Opcode(0x50); /// `MLOAD` @@ -83,6 +83,7 @@ impl Opcode { pub const MSTORE: Opcode = Opcode(0x52); /// `MSTORE8` pub const MSTORE8: Opcode = Opcode(0x53); + /// `JUMP` pub const JUMP: Opcode = Opcode(0x56); /// `JUMPI` @@ -91,6 +92,7 @@ impl Opcode { pub const PC: Opcode = Opcode(0x58); /// `MSIZE` pub const MSIZE: Opcode = Opcode(0x59); + /// `JUMPDEST` pub const JUMPDEST: Opcode = Opcode(0x5b); @@ -165,48 +167,48 @@ impl Opcode { pub const SWAP15: Opcode = Opcode(0x9e); pub const SWAP16: Opcode = Opcode(0x9f); + /// See [EIP-3541](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3541.md) + pub const EOFMAGIC: Opcode = Opcode(0xef); + /// `RETURN` pub const RETURN: Opcode = Opcode(0xf3); + /// `REVERT` pub const REVERT: Opcode = Opcode(0xfd); /// `INVALID` pub const INVALID: Opcode = Opcode(0xfe); - - /// See [EIP-3541](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3541.md) - pub const EOFMAGIC: Opcode = Opcode(0xef); } // External opcodes impl Opcode { /// `SHA3` pub const SHA3: Opcode = Opcode(0x20); + /// `ADDRESS` pub const ADDRESS: Opcode = Opcode(0x30); /// `BALANCE` pub const BALANCE: Opcode = Opcode(0x31); - /// `SELFBALANCE` - pub const SELFBALANCE: Opcode = Opcode(0x47); - /// `BASEFEE` - pub const BASEFEE: Opcode = Opcode(0x48); /// `ORIGIN` pub const ORIGIN: Opcode = Opcode(0x32); /// `CALLER` pub const CALLER: Opcode = Opcode(0x33); /// `CALLVALUE` pub const CALLVALUE: Opcode = Opcode(0x34); + /// `GASPRICE` pub const GASPRICE: Opcode = Opcode(0x3a); /// `EXTCODESIZE` pub const EXTCODESIZE: Opcode = Opcode(0x3b); /// `EXTCODECOPY` pub const EXTCODECOPY: Opcode = Opcode(0x3c); - /// `EXTCODEHASH` - pub const EXTCODEHASH: Opcode = Opcode(0x3f); /// `RETURNDATASIZE` pub const RETURNDATASIZE: Opcode = Opcode(0x3d); /// `RETURNDATACOPY` pub const RETURNDATACOPY: Opcode = Opcode(0x3e); + /// `EXTCODEHASH` + pub const EXTCODEHASH: Opcode = Opcode(0x3f); + /// `BLOCKHASH` pub const BLOCKHASH: Opcode = Opcode(0x40); /// `COINBASE` @@ -219,34 +221,50 @@ impl Opcode { pub const DIFFICULTY: Opcode = Opcode(0x44); /// `GASLIMIT` pub const GASLIMIT: Opcode = Opcode(0x45); + /// `CHAINID` + pub const CHAINID: Opcode = Opcode(0x46); + /// `SELFBALANCE` + pub const SELFBALANCE: Opcode = Opcode(0x47); + /// `BASEFEE` + pub const BASEFEE: Opcode = Opcode(0x48); + /// `SLOAD` pub const SLOAD: Opcode = Opcode(0x54); /// `SSTORE` pub const SSTORE: Opcode = Opcode(0x55); + /// `GAS` pub const GAS: Opcode = Opcode(0x5a); + + /// `TLOAD` + pub const TLOAD: Opcode = Opcode(0x5c); + /// `TSTORE` + pub const TSTORE: Opcode = Opcode(0x5d); + /// `LOGn` pub const LOG0: Opcode = Opcode(0xa0); pub const LOG1: Opcode = Opcode(0xa1); pub const LOG2: Opcode = Opcode(0xa2); pub const LOG3: Opcode = Opcode(0xa3); pub const LOG4: Opcode = Opcode(0xa4); + /// `CREATE` pub const CREATE: Opcode = Opcode(0xf0); - /// `CREATE2` - pub const CREATE2: Opcode = Opcode(0xf5); /// `CALL` pub const CALL: Opcode = Opcode(0xf1); /// `CALLCODE` pub const CALLCODE: Opcode = Opcode(0xf2); + /// `DELEGATECALL` pub const DELEGATECALL: Opcode = Opcode(0xf4); + /// `CREATE2` + pub const CREATE2: Opcode = Opcode(0xf5); + /// `STATICCALL` pub const STATICCALL: Opcode = Opcode(0xfa); + /// `SUICIDE` pub const SUICIDE: Opcode = Opcode(0xff); - /// `CHAINID` - pub const CHAINID: Opcode = Opcode(0x46); } impl Opcode { diff --git a/interpreter/src/runtime.rs b/interpreter/src/runtime.rs index 7dbce8a49..992099e04 100644 --- a/interpreter/src/runtime.rs +++ b/interpreter/src/runtime.rs @@ -1,9 +1,10 @@ -use crate::ExitError; -use alloc::rc::Rc; -use alloc::vec::Vec; +use alloc::{rc::Rc, vec::Vec}; + use primitive_types::{H160, H256, U256}; use sha3::{Digest, Keccak256}; +use crate::error::ExitError; + /// Gas state. pub trait GasState { fn gas(&self) -> U256; @@ -14,6 +15,7 @@ pub trait GasState { pub struct RuntimeState { /// Runtime context. pub context: Context, + /// Transaction context. pub transaction_context: Rc, /// Return data buffer. pub retbuf: Vec, @@ -113,6 +115,8 @@ pub trait RuntimeBaseBackend { fn code(&self, address: H160) -> Vec; /// Get storage value of address at index. fn storage(&self, address: H160, index: H256) -> H256; + /// Get transient storage value of address at index. + fn transient_storage(&self, address: H160, index: H256) -> H256; /// Check whether an address exists. fn exists(&self, address: H160) -> bool; @@ -138,6 +142,13 @@ pub trait RuntimeBackend: RuntimeBaseBackend { fn mark_hot(&mut self, address: H160, index: Option); /// Set storage value of address at index. fn set_storage(&mut self, address: H160, index: H256, value: H256) -> Result<(), ExitError>; + /// Set transient storage value of address at index, transient storage gets discarded after every transaction. (see EIP-1153) + fn set_transient_storage( + &mut self, + address: H160, + index: H256, + value: H256, + ) -> Result<(), ExitError>; /// Create a log owned by address with given topics and data. fn log(&mut self, log: Log) -> Result<(), ExitError>; /// Mark an address to be deleted. diff --git a/interpreter/src/utils.rs b/interpreter/src/utils.rs index a32cc07eb..9809a2774 100644 --- a/interpreter/src/utils.rs +++ b/interpreter/src/utils.rs @@ -1,10 +1,14 @@ //! Small utilities. -use crate::{ExitError, ExitFatal}; -use core::cmp::Ordering; -use core::ops::{Div, Rem}; +use core::{ + cmp::Ordering, + ops::{Div, Rem}, +}; + use primitive_types::{H256, U256}; +use crate::error::{ExitError, ExitFatal}; + /// Convert [U256] into [H256]. #[must_use] pub fn u256_to_h256(v: U256) -> H256 { @@ -160,10 +164,10 @@ impl Rem for I256 { #[cfg(test)] mod tests { - use crate::utils::{Sign, I256}; - use primitive_types::U256; use std::num::Wrapping; + use super::*; + #[test] fn div_i256() { // Sanity checks based on i8. Notice that we need to use `Wrapping` here because diff --git a/interpreter/tests/performance.rs b/interpreter/tests/performance.rs index 1e9d255f2..65d0ce64b 100644 --- a/interpreter/tests/performance.rs +++ b/interpreter/tests/performance.rs @@ -1,7 +1,12 @@ -use evm_interpreter::interpreter::{EtableInterpreter, RunInterpreter}; -use evm_interpreter::{Capture, Etable, ExitSucceed, Machine}; use std::rc::Rc; +use evm_interpreter::{ + error::{Capture, ExitSucceed}, + etable::Etable, + machine::Machine, + EtableInterpreter, RunInterpreter, +}; + static ETABLE: Etable<(), (), ()> = Etable::core(); macro_rules! ret_test { diff --git a/interpreter/tests/usability.rs b/interpreter/tests/usability.rs index 02421a987..d174e82a1 100644 --- a/interpreter/tests/usability.rs +++ b/interpreter/tests/usability.rs @@ -1,11 +1,17 @@ -use evm_interpreter::interpreter::{EtableInterpreter, RunInterpreter}; +use std::rc::Rc; + use evm_interpreter::{ - trap::CallCreateTrap, Capture, Context, Control, Etable, ExitError, ExitSucceed, Log, Machine, - Opcode, RuntimeBackend, RuntimeBaseBackend, RuntimeEnvironment, RuntimeState, - TransactionContext, + error::{CallCreateTrap, Capture, ExitError, ExitSucceed}, + etable::{Control, Etable}, + machine::Machine, + opcode::Opcode, + runtime::{ + Context, Log, RuntimeBackend, RuntimeBaseBackend, RuntimeEnvironment, RuntimeState, + TransactionContext, + }, + EtableInterpreter, RunInterpreter, }; use primitive_types::{H160, H256, U256}; -use std::rc::Rc; const CODE1: &str = "60e060020a6000350480632839e92814601e57806361047ff414603457005b602a6004356024356047565b8060005260206000f35b603d6004356099565b8060005260206000f35b600082600014605457605e565b8160010190506093565b81600014606957607b565b60756001840360016047565b90506093565b609060018403608c85600186036047565b6047565b90505b92915050565b6000816000148060a95750816001145b60b05760b7565b81905060cf565b60c1600283036099565b60cb600184036099565b0190505b91905056"; const DATA1: &str = "2839e92800000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001"; @@ -108,6 +114,9 @@ impl RuntimeBaseBackend for UnimplementedHandler { fn storage(&self, _address: H160, _index: H256) -> H256 { unimplemented!() } + fn transient_storage(&self, _address: H160, _index: H256) -> H256 { + unimplemented!() + } fn exists(&self, _address: H160) -> bool { unimplemented!() @@ -137,6 +146,14 @@ impl RuntimeBackend for UnimplementedHandler { fn set_storage(&mut self, _address: H160, _index: H256, _value: H256) -> Result<(), ExitError> { unimplemented!() } + fn set_transient_storage( + &mut self, + _address: H160, + _index: H256, + _value: H256, + ) -> Result<(), ExitError> { + unimplemented!() + } fn log(&mut self, _log: Log) -> Result<(), ExitError> { unimplemented!() } diff --git a/jsontests/Cargo.toml b/jsontests/Cargo.toml index 95c902f90..7cd6f5aa8 100644 --- a/jsontests/Cargo.toml +++ b/jsontests/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "jsontests" version = "0.0.0-dev" -edition = "2021" +edition = { workspace = true } +rust-version = { workspace = true } license = { workspace = true } authors = { workspace = true } repository = { workspace = true } diff --git a/jsontests/src/error.rs b/jsontests/src/error.rs index b492a1e04..75df2b289 100644 --- a/jsontests/src/error.rs +++ b/jsontests/src/error.rs @@ -1,7 +1,4 @@ -#![allow(clippy::upper_case_acronyms)] -use thiserror::Error; - -#[derive(Error, Debug)] +#[derive(Debug, thiserror::Error)] #[allow(dead_code)] pub enum TestError { #[error("state root is different")] @@ -10,14 +7,15 @@ pub enum TestError { ExpectException, } -#[derive(Error, Debug)] +#[allow(clippy::upper_case_acronyms)] +#[derive(Debug, thiserror::Error)] pub enum Error { #[error("io error")] IO(#[from] std::io::Error), #[error("json error")] JSON(#[from] serde_json::Error), #[error("evm error")] - EVM(#[from] evm::ExitError), + EVM(#[from] evm::interpreter::error::ExitError), #[error("unsupported fork")] UnsupportedFork, #[error("non-utf8 filename")] diff --git a/jsontests/src/hash.rs b/jsontests/src/hash.rs index 7a7e91e52..a804ce9c0 100644 --- a/jsontests/src/hash.rs +++ b/jsontests/src/hash.rs @@ -1,8 +1,9 @@ -use crate::in_memory::InMemoryBackend; -use evm::utils::h256_to_u256; +use evm::interpreter::utils::h256_to_u256; use primitive_types::{H256, U256}; use sha3::{Digest, Keccak256}; +use crate::in_memory::InMemoryBackend; + /// Basic account type. #[derive(Debug, Clone, PartialEq, Eq)] pub struct TrieAccount { diff --git a/jsontests/src/in_memory.rs b/jsontests/src/in_memory.rs index 05fc87e69..63c84ef87 100644 --- a/jsontests/src/in_memory.rs +++ b/jsontests/src/in_memory.rs @@ -1,7 +1,11 @@ -use evm::{backend::OverlayedChangeSet, RuntimeBaseBackend, RuntimeEnvironment}; -use primitive_types::{H160, H256, U256}; use std::collections::BTreeMap; +use evm::{ + backend::OverlayedChangeSet, + interpreter::runtime::{RuntimeBaseBackend, RuntimeEnvironment}, +}; +use primitive_types::{H160, H256, U256}; + #[derive(Clone, Debug)] pub struct InMemoryEnvironment { pub block_hashes: BTreeMap, @@ -21,6 +25,7 @@ pub struct InMemoryAccount { pub code: Vec, pub nonce: U256, pub storage: BTreeMap, + pub transient_storage: BTreeMap, } #[derive(Clone, Debug)] @@ -62,6 +67,16 @@ impl InMemoryBackend { } } + for ((address, key), value) in changeset.transient_storage.clone() { + let account = self.state.entry(address).or_default(); + + if value == H256::default() { + account.transient_storage.remove(&key); + } else { + account.transient_storage.insert(key, value); + } + } + for address in changeset.deletes.clone() { self.state.remove(&address); } @@ -142,6 +157,17 @@ impl RuntimeBaseBackend for InMemoryBackend { .unwrap_or(H256::default()) } + fn transient_storage(&self, address: H160, index: H256) -> H256 { + self.state + .get(&address) + .cloned() + .unwrap_or(Default::default()) + .transient_storage + .get(&index) + .cloned() + .unwrap_or(H256::default()) + } + fn nonce(&self, address: H160) -> U256 { self.state .get(&address) diff --git a/jsontests/src/main.rs b/jsontests/src/main.rs index 390571365..c21ee0a62 100644 --- a/jsontests/src/main.rs +++ b/jsontests/src/main.rs @@ -4,10 +4,10 @@ mod in_memory; mod run; mod types; -use crate::error::Error; -use crate::types::*; use clap::Parser; +use crate::{error::Error, types::*}; + #[derive(Parser)] #[command(author, version, about, long_about = None)] struct Cli { diff --git a/jsontests/src/run.rs b/jsontests/src/run.rs index 9ee310990..3f113652a 100644 --- a/jsontests/src/run.rs +++ b/jsontests/src/run.rs @@ -1,16 +1,22 @@ -use crate::error::{Error, TestError}; -use crate::in_memory::{InMemoryAccount, InMemoryBackend, InMemoryEnvironment}; -use crate::types::{Fork, TestCompletionStatus, TestData, TestExpectException, TestMulti}; -use evm::backend::OverlayedBackend; -use evm::standard::{Config, Etable, EtableResolver, Invoker, TransactArgs}; -use evm::utils::u256_to_h256; -use evm::Capture; -use evm::{interpreter::Interpreter, GasState}; +use std::{ + collections::{BTreeMap, BTreeSet}, + fs::{self, File}, + io::BufReader, +}; + +use evm::{ + backend::OverlayedBackend, + interpreter::{error::Capture, runtime::GasState, utils::u256_to_h256, Interpreter}, + standard::{Config, Etable, EtableResolver, Invoker, TransactArgs}, +}; use evm_precompile::StandardPrecompileSet; use primitive_types::U256; -use std::collections::{BTreeMap, BTreeSet}; -use std::fs::{self, File}; -use std::io::BufReader; + +use crate::{ + error::{Error, TestError}, + in_memory::{InMemoryAccount, InMemoryBackend, InMemoryEnvironment}, + types::{Fork, TestCompletionStatus, TestData, TestExpectException, TestMulti}, +}; const BASIC_FILE_PATH_TO_TRIM: [&str; 2] = [ "jsontests/res/ethtests/GeneralStateTests/", @@ -138,6 +144,7 @@ pub fn run_test( code: account.code.0, nonce: account.nonce, storage, + transient_storage: Default::default(), }, ) }) diff --git a/jsontests/src/types.rs b/jsontests/src/types.rs index 33b1fb41d..28179d328 100644 --- a/jsontests/src/types.rs +++ b/jsontests/src/types.rs @@ -1,11 +1,11 @@ +use std::{collections::BTreeMap, fmt}; + use hex::FromHex; use primitive_types::{H160, H256, U256}; use serde::{ de::{Error, Visitor}, Deserialize, Deserializer, }; -use std::collections::BTreeMap; -use std::fmt; /// Statistic type to gather tests pass completion status #[derive(Default, Clone, Debug, Eq, PartialEq)] diff --git a/precompile/Cargo.toml b/precompile/Cargo.toml index f412a35b1..eac21091e 100644 --- a/precompile/Cargo.toml +++ b/precompile/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "evm-precompile" version = "0.0.0-dev" -edition = "2021" -license = { workspace = true } +edition = { workspace = true } +rust-version = "1.65.0" authors = { workspace = true } repository = { workspace = true } keywords = { workspace = true } diff --git a/precompile/src/blake2/mod.rs b/precompile/src/blake2/mod.rs index 81144c5f4..f94a64b00 100644 --- a/precompile/src/blake2/mod.rs +++ b/precompile/src/blake2/mod.rs @@ -1,7 +1,11 @@ mod eip152; +use evm::{ + interpreter::error::{ExitException, ExitResult, ExitSucceed}, + GasMutState, +}; + use crate::PurePrecompile; -use evm::{ExitException, ExitResult, ExitSucceed, GasMutState}; pub struct Blake2F; diff --git a/precompile/src/bn128.rs b/precompile/src/bn128.rs index 5b8c5793b..44d90597a 100644 --- a/precompile/src/bn128.rs +++ b/precompile/src/bn128.rs @@ -1,8 +1,13 @@ -use crate::PurePrecompile; use alloc::vec::Vec; -use evm::{ExitError, ExitException, ExitResult, ExitSucceed, GasMutState}; + +use evm::{ + interpreter::error::{ExitError, ExitException, ExitResult, ExitSucceed}, + GasMutState, +}; use primitive_types::U256; +use crate::PurePrecompile; + /// Copy bytes from input to target. fn read_input(source: &[u8], target: &mut [u8], offset: usize) { // Out of bounds, nothing to copy. diff --git a/precompile/src/lib.rs b/precompile/src/lib.rs index 6042bdb52..00cc02829 100644 --- a/precompile/src/lib.rs +++ b/precompile/src/lib.rs @@ -20,17 +20,25 @@ mod bn128; mod modexp; mod simple; -pub use crate::blake2::Blake2F; -pub use crate::bn128::{Bn128Add, Bn128Mul, Bn128Pairing}; -pub use crate::modexp::Modexp; -pub use crate::simple::{ECRecover, Identity, Ripemd160, Sha256}; - use alloc::vec::Vec; -use evm::standard::{Config, PrecompileSet}; -use evm::{ExitError, ExitException, ExitResult, GasMutState, RuntimeState}; +use evm::{ + interpreter::{ + error::{ExitError, ExitException, ExitResult}, + runtime::RuntimeState, + }, + standard::{Config, PrecompileSet}, + GasMutState, +}; use primitive_types::H160; +pub use crate::{ + blake2::Blake2F, + bn128::{Bn128Add, Bn128Mul, Bn128Pairing}, + modexp::Modexp, + simple::{ECRecover, Identity, Ripemd160, Sha256}, +}; + pub trait PurePrecompile { fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec); } diff --git a/precompile/src/modexp.rs b/precompile/src/modexp.rs index 39d86d5fa..a67676114 100644 --- a/precompile/src/modexp.rs +++ b/precompile/src/modexp.rs @@ -1,9 +1,14 @@ -use crate::PurePrecompile; use alloc::{vec, vec::Vec}; use core::cmp::max; -use evm::{ExitException, ExitResult, ExitSucceed, GasMutState}; + +use evm::{ + interpreter::error::{ExitException, ExitResult, ExitSucceed}, + GasMutState, +}; use num::{BigUint, FromPrimitive, Integer, One, ToPrimitive, Zero}; +use crate::PurePrecompile; + pub struct Modexp; const MIN_GAS_COST: u64 = 200; diff --git a/precompile/src/simple.rs b/precompile/src/simple.rs index 58a2912be..c14043abe 100644 --- a/precompile/src/simple.rs +++ b/precompile/src/simple.rs @@ -1,10 +1,15 @@ -use crate::{linear_cost, PurePrecompile}; use core::cmp::min; -use evm::{ExitException, ExitResult, ExitSucceed, GasMutState}; + +use evm::{ + interpreter::error::{ExitException, ExitResult, ExitSucceed}, + GasMutState, +}; use k256::ecdsa::{RecoveryId, Signature, VerifyingKey}; use primitive_types::{H256, U256}; use sha3::{Digest, Keccak256}; +use crate::{linear_cost, PurePrecompile}; + pub struct ECRecover; impl PurePrecompile for ECRecover { diff --git a/rustfmt.toml b/rustfmt.toml index 218e20321..eb74f05bb 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1,3 @@ hard_tabs = true +imports_granularity="Crate" +group_imports = "StdExternalCrate" diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 96436d450..e68e3b5ff 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -13,8 +13,9 @@ mod overlayed; +pub use evm_interpreter::runtime::{RuntimeBackend, RuntimeBaseBackend, RuntimeEnvironment}; + pub use self::overlayed::{OverlayedBackend, OverlayedChangeSet}; -pub use evm_interpreter::{RuntimeBackend, RuntimeBaseBackend, RuntimeEnvironment}; /// Backend with layers that can transactionally be committed or discarded. pub trait TransactionalBackend { diff --git a/src/backend/overlayed.rs b/src/backend/overlayed.rs index 7ffd43df9..c8fe2296e 100644 --- a/src/backend/overlayed.rs +++ b/src/backend/overlayed.rs @@ -1,15 +1,18 @@ -use crate::{ - ExitError, ExitException, Log, MergeStrategy, RuntimeBackend, RuntimeBaseBackend, - RuntimeEnvironment, TransactionalBackend, -}; use alloc::{ boxed::Box, collections::{BTreeMap, BTreeSet}, vec::Vec, }; use core::mem; + +use evm_interpreter::{ + error::{ExitError, ExitException}, + runtime::{Log, RuntimeBackend, RuntimeBaseBackend, RuntimeEnvironment}, +}; use primitive_types::{H160, H256, U256}; +use crate::{backend::TransactionalBackend, MergeStrategy}; + #[derive(Clone, Debug)] pub struct OverlayedChangeSet { pub logs: Vec, @@ -18,6 +21,7 @@ pub struct OverlayedChangeSet { pub nonces: BTreeMap, pub storage_resets: BTreeSet, pub storages: BTreeMap<(H160, H256), H256>, + pub transient_storage: BTreeMap<(H160, H256), H256>, pub deletes: BTreeSet, } @@ -46,6 +50,7 @@ impl OverlayedBackend { nonces: self.substate.nonces, storage_resets: self.substate.storage_resets, storages: self.substate.storages, + transient_storage: self.substate.transient_storage, deletes: self.substate.deletes, }, ) @@ -115,6 +120,14 @@ impl RuntimeBaseBackend for OverlayedBackend { } } + fn transient_storage(&self, address: H160, index: H256) -> H256 { + if let Some(value) = self.substate.known_transient_storage(address, index) { + value + } else { + self.backend.transient_storage(address, index) + } + } + fn exists(&self, address: H160) -> bool { if let Some(exists) = self.substate.known_exists(address) { exists @@ -154,6 +167,18 @@ impl RuntimeBackend for OverlayedBackend { Ok(()) } + fn set_transient_storage( + &mut self, + address: H160, + index: H256, + value: H256, + ) -> Result<(), ExitError> { + self.substate + .transient_storage + .insert((address, index), value); + Ok(()) + } + fn log(&mut self, log: Log) -> Result<(), ExitError> { self.substate.logs.push(log); Ok(()) @@ -240,6 +265,11 @@ impl TransactionalBackend for OverlayedBackend { for ((address, key), value) in child.storages { self.substate.storages.insert((address, key), value); } + for ((address, key), value) in child.transient_storage { + self.substate + .transient_storage + .insert((address, key), value); + } for address in child.deletes { self.substate.deletes.insert(address); } @@ -257,6 +287,7 @@ struct Substate { nonces: BTreeMap, storage_resets: BTreeSet, storages: BTreeMap<(H160, H256), H256>, + transient_storage: BTreeMap<(H160, H256), H256>, deletes: BTreeSet, } @@ -270,6 +301,7 @@ impl Substate { nonces: Default::default(), storage_resets: Default::default(), storages: Default::default(), + transient_storage: Default::default(), deletes: Default::default(), } } @@ -316,6 +348,16 @@ impl Substate { } } + pub fn known_transient_storage(&self, address: H160, key: H256) -> Option { + if let Some(value) = self.transient_storage.get(&(address, key)) { + Some(*value) + } else if let Some(parent) = self.parent.as_ref() { + parent.known_transient_storage(address, key) + } else { + None + } + } + pub fn known_exists(&self, address: H160) -> Option { if self.balances.contains_key(&address) || self.nonces.contains_key(&address) diff --git a/src/call_stack.rs b/src/call_stack.rs index a615acb0e..09a28f847 100644 --- a/src/call_stack.rs +++ b/src/call_stack.rs @@ -1,8 +1,13 @@ -use crate::interpreter::{Interpreter, RunInterpreter, StepInterpreter}; -use crate::{Capture, ExitError, ExitFatal, ExitResult, Invoker, InvokerControl}; use alloc::vec::Vec; use core::convert::Infallible; +use evm_interpreter::{ + error::{Capture, ExitError, ExitFatal, ExitResult}, + Interpreter, RunInterpreter, StepInterpreter, +}; + +use crate::invoker::{Invoker, InvokerControl}; + struct Substack { invoke: TrD, machine: M, diff --git a/src/gasometer.rs b/src/gasometer.rs index 7114c4369..ce3b75ae8 100644 --- a/src/gasometer.rs +++ b/src/gasometer.rs @@ -1,6 +1,6 @@ //! EVM gasometer. -use crate::{ExitError, GasState}; +use evm_interpreter::{error::ExitError, runtime::GasState}; use primitive_types::U256; pub trait GasMutState: GasState { diff --git a/src/invoker.rs b/src/invoker.rs index 755834763..9d8b76b61 100644 --- a/src/invoker.rs +++ b/src/invoker.rs @@ -1,6 +1,10 @@ -use crate::{interpreter::Interpreter, Capture, ExitError, ExitResult}; use alloc::vec::Vec; +use evm_interpreter::{ + error::{Capture, ExitError, ExitResult}, + Interpreter, +}; + /// Control for an invoker. pub enum InvokerControl { /// Pushing the call stack. diff --git a/src/lib.rs b/src/lib.rs index 12857e99f..70fd5c25e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,12 +72,14 @@ mod call_stack; mod gasometer; mod invoker; -pub use evm_interpreter::*; +pub use evm_interpreter as interpreter; -pub use crate::backend::TransactionalBackend; -pub use crate::call_stack::{transact, HeapTransact}; -pub use crate::gasometer::GasMutState; -pub use crate::invoker::{Invoker, InvokerControl}; +pub use crate::{ + backend::TransactionalBackend, + call_stack::{transact, HeapTransact}, + gasometer::GasMutState, + invoker::{Invoker, InvokerControl}, +}; /// Merge strategy of a backend substate layer or a call stack gasometer layer. #[derive(Clone, Debug, Copy)] diff --git a/src/standard/config.rs b/src/standard/config.rs index 75c72428f..cbeea9ec7 100644 --- a/src/standard/config.rs +++ b/src/standard/config.rs @@ -99,6 +99,8 @@ pub struct Config { pub has_push0: bool, /// Uses EIP-1559 (Base fee is burned when this flag is enabled) [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) pub eip_1559: bool, + /// Enables transient storage. See [EIP-1153](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1153.md) + pub eip_1153_enabled: bool, } impl Config { @@ -153,6 +155,7 @@ impl Config { has_base_fee: false, has_push0: false, eip_1559: false, + eip_1153_enabled: false, } } @@ -207,6 +210,7 @@ impl Config { has_base_fee: false, has_push0: false, eip_1559: false, + eip_1153_enabled: false, } } @@ -247,6 +251,7 @@ impl Config { warm_coinbase_address, max_initcode_size, eip_1559, + eip_1153_enabled, } = inputs; // See https://eips.ethereum.org/EIPS/eip-2929 @@ -310,6 +315,7 @@ impl Config { has_base_fee, has_push0, eip_1559, + eip_1153_enabled, } } } @@ -330,6 +336,7 @@ struct DerivedConfigInputs { warm_coinbase_address: bool, max_initcode_size: Option, eip_1559: bool, + eip_1153_enabled: bool, } impl DerivedConfigInputs { @@ -345,6 +352,7 @@ impl DerivedConfigInputs { warm_coinbase_address: false, max_initcode_size: None, eip_1559: false, + eip_1153_enabled: false, } } @@ -360,6 +368,7 @@ impl DerivedConfigInputs { warm_coinbase_address: false, max_initcode_size: None, eip_1559: true, + eip_1153_enabled: false, } } @@ -375,6 +384,7 @@ impl DerivedConfigInputs { warm_coinbase_address: false, max_initcode_size: None, eip_1559: true, + eip_1153_enabled: false, } } @@ -391,6 +401,7 @@ impl DerivedConfigInputs { // 2 * 24576 as per EIP-3860 max_initcode_size: Some(0xC000), eip_1559: true, + eip_1153_enabled: false, } } @@ -407,6 +418,7 @@ impl DerivedConfigInputs { // 2 * (MAX_CODE_SIZE = `24576`) = (0xC000 = 49152) as per EIP-3860 max_initcode_size: Some(0xC000), eip_1559: true, + eip_1153_enabled: true, } } } diff --git a/src/standard/gasometer/consts.rs b/src/standard/gasometer/consts.rs index f51085577..115fe5c19 100644 --- a/src/standard/gasometer/consts.rs +++ b/src/standard/gasometer/consts.rs @@ -1,4 +1,4 @@ -use evm_interpreter::Opcode; +use evm_interpreter::opcode::Opcode; pub const G_ZERO: u64 = 0; pub const G_BASE: u64 = 2; diff --git a/src/standard/gasometer/costs.rs b/src/standard/gasometer/costs.rs index dda497e66..92586e4df 100644 --- a/src/standard/gasometer/costs.rs +++ b/src/standard/gasometer/costs.rs @@ -1,9 +1,9 @@ -use super::consts::*; -use super::utils::log2floor; -use crate::standard::Config; -use evm_interpreter::ExitException; +use evm_interpreter::error::ExitException; use primitive_types::{H256, U256}; +use super::{consts::*, utils::log2floor}; +use crate::standard::Config; + pub fn call_extra_check(gas: U256, after_gas: u64, config: &Config) -> Result<(), ExitException> { if config.err_on_call_with_more_gas && U256::from(after_gas) < gas { Err(ExitException::OutOfGas) @@ -241,6 +241,13 @@ pub fn sstore_cost( }, ) } +pub fn tload_cost(config: &Config) -> Result { + Ok(config.gas_storage_read_warm) +} + +pub fn tstore_cost(config: &Config) -> Result { + Ok(config.gas_storage_read_warm) +} pub fn suicide_cost(value: U256, is_cold: bool, target_exists: bool, config: &Config) -> u64 { let eip161 = !config.empty_considered_exists; diff --git a/src/standard/gasometer/mod.rs b/src/standard/gasometer/mod.rs index 4e55d7db3..060f43de8 100644 --- a/src/standard/gasometer/mod.rs +++ b/src/standard/gasometer/mod.rs @@ -2,15 +2,20 @@ mod consts; mod costs; mod utils; -use crate::standard::Config; -use crate::{ - Control, ExitError, ExitException, Machine, MergeStrategy, Opcode, RuntimeBackend, - RuntimeState, Stack, -}; use alloc::vec::Vec; use core::cmp::{max, min}; + +use evm_interpreter::{ + error::{ExitError, ExitException}, + etable::Control, + machine::{Machine, Stack}, + opcode::Opcode, + runtime::{RuntimeBackend, RuntimeState}, +}; use primitive_types::{H160, H256, U256}; +use crate::{standard::Config, MergeStrategy}; + pub struct GasometerState<'config> { gas_limit: u64, memory_gas: u64, @@ -388,6 +393,7 @@ fn dynamic_opcode_cost( GasCost::SLoad { target_is_cold } } + Opcode::TLOAD if config.eip_1153_enabled => GasCost::TLoad, Opcode::DELEGATECALL if config.has_delegate_call => { let target = stack.peek(1)?.into(); @@ -425,6 +431,7 @@ fn dynamic_opcode_cost( target_is_cold, } } + Opcode::TSTORE if !is_static && config.eip_1153_enabled => GasCost::TStore, Opcode::LOG0 if !is_static => GasCost::Log { n: 0, len: U256::from_big_endian(&stack.peek(1)?[..]), @@ -646,6 +653,10 @@ enum GasCost { /// True if target has not been previously accessed in this transaction target_is_cold: bool, }, + /// Gas cost for `TLOAD`. + TLoad, + /// Gas cost for `TSTORE`. + TStore, /// Gas cost for `SHA3`. Sha3 { /// Length of the data. @@ -742,6 +753,8 @@ impl GasCost { new, target_is_cold, } => costs::sstore_cost(original, current, new, gas, target_is_cold, config)?, + GasCost::TLoad => costs::tload_cost(config)?, + GasCost::TStore => costs::tstore_cost(config)?, GasCost::Sha3 { len } => costs::sha3_cost(len)?, GasCost::Log { n, len } => costs::log_cost(n, len)?, GasCost::VeryLowCopy { len } => costs::verylowcopy_cost(len)?, diff --git a/src/standard/gasometer/utils.rs b/src/standard/gasometer/utils.rs index 9630b9ab9..b2d7880f1 100644 --- a/src/standard/gasometer/utils.rs +++ b/src/standard/gasometer/utils.rs @@ -1,7 +1,7 @@ use primitive_types::U256; pub fn log2floor(value: U256) -> u64 { - assert!(value != U256::zero()); + assert_ne!(value, U256::zero()); let mut l: u64 = 256; for i in 0..4 { let i = 3 - i; diff --git a/src/standard/invoker/mod.rs b/src/standard/invoker/mod.rs index e1fcb1d89..25f688d1d 100644 --- a/src/standard/invoker/mod.rs +++ b/src/standard/invoker/mod.rs @@ -2,24 +2,35 @@ mod resolver; pub mod routines; mod state; -pub use self::resolver::{EtableResolver, PrecompileSet, Resolver}; -pub use self::state::InvokerState; +use alloc::{rc::Rc, vec::Vec}; +use core::{cmp::min, convert::Infallible}; -use super::Config; -use crate::trap::{CallCreateTrap, CallCreateTrapData, CallTrapData, CreateScheme, CreateTrapData}; -use crate::{ - interpreter::Interpreter, Capture, Context, ExitError, ExitException, ExitResult, ExitSucceed, - GasState, Invoker as InvokerT, InvokerControl, MergeStrategy, Opcode, RuntimeBackend, - RuntimeEnvironment, RuntimeState, TransactionContext, TransactionalBackend, Transfer, - TrapConsume, +use evm_interpreter::{ + error::{ + CallCreateTrap, CallCreateTrapData, CallTrapData, Capture, CreateScheme, CreateTrapData, + ExitError, ExitException, ExitResult, ExitSucceed, TrapConsume, + }, + opcode::Opcode, + runtime::{ + Context, GasState, RuntimeBackend, RuntimeEnvironment, RuntimeState, TransactionContext, + Transfer, + }, + Interpreter, }; -use alloc::rc::Rc; -use alloc::vec::Vec; -use core::cmp::min; -use core::convert::Infallible; use primitive_types::{H160, H256, U256}; use sha3::{Digest, Keccak256}; +pub use self::{ + resolver::{EtableResolver, PrecompileSet, Resolver}, + state::InvokerState, +}; +use crate::{ + backend::TransactionalBackend, + invoker::{Invoker as InvokerT, InvokerControl}, + standard::Config, + MergeStrategy, +}; + /// A trap that can be turned into either a call/create trap (where we push new /// call stack), or an interrupt (an external signal). pub trait IntoCallCreateTrap { diff --git a/src/standard/invoker/resolver.rs b/src/standard/invoker/resolver.rs index 4c46534c0..348afd958 100644 --- a/src/standard/invoker/resolver.rs +++ b/src/standard/invoker/resolver.rs @@ -1,11 +1,16 @@ -use crate::interpreter::{EtableInterpreter, Interpreter}; -use crate::{ - standard::Config, EtableSet, ExitError, ExitResult, InvokerControl, Machine, RuntimeBackend, - RuntimeState, -}; use alloc::{rc::Rc, vec::Vec}; + +use evm_interpreter::{ + error::{ExitError, ExitResult}, + etable::EtableSet, + machine::Machine, + runtime::{RuntimeBackend, RuntimeState}, + EtableInterpreter, Interpreter, +}; use primitive_types::H160; +use crate::{invoker::InvokerControl, standard::Config}; + /// A code resolver. /// /// The resolver handles how a call (with the target code address) or create diff --git a/src/standard/invoker/routines.rs b/src/standard/invoker/routines.rs index fbc0ff8a2..9bd3d3fce 100644 --- a/src/standard/invoker/routines.rs +++ b/src/standard/invoker/routines.rs @@ -1,12 +1,19 @@ -use super::{CallTrapData, CreateTrapData, InvokerState, Resolver, SubstackInvoke}; -use crate::standard::Config; -use crate::{ - ExitError, ExitException, ExitResult, InvokerControl, MergeStrategy, Opcode, RuntimeBackend, - RuntimeEnvironment, RuntimeState, TransactionalBackend, Transfer, -}; use alloc::vec::Vec; + +use evm_interpreter::{ + error::{CallTrapData, CreateTrapData, ExitError, ExitException, ExitResult}, + opcode::Opcode, + runtime::{RuntimeBackend, RuntimeEnvironment, RuntimeState, Transfer}, +}; use primitive_types::{H160, U256}; +use crate::{ + backend::TransactionalBackend, + invoker::InvokerControl, + standard::{Config, InvokerState, Resolver, SubstackInvoke}, + MergeStrategy, +}; + #[allow(clippy::too_many_arguments, clippy::type_complexity)] pub fn make_enter_call_machine( _config: &Config, diff --git a/src/standard/invoker/state.rs b/src/standard/invoker/state.rs index eef276a28..3957329f2 100644 --- a/src/standard/invoker/state.rs +++ b/src/standard/invoker/state.rs @@ -1,7 +1,13 @@ -use crate::{standard::Config, ExitError, GasState, MergeStrategy, RuntimeState}; use alloc::vec::Vec; + +use evm_interpreter::{ + error::ExitError, + runtime::{GasState, RuntimeState}, +}; use primitive_types::{H160, H256, U256}; +use crate::{standard::Config, MergeStrategy}; + pub trait InvokerState<'config>: GasState + Sized { fn new_transact_call( runtime: RuntimeState, diff --git a/src/standard/mod.rs b/src/standard/mod.rs index e364d723d..7b9b1a9f3 100644 --- a/src/standard/mod.rs +++ b/src/standard/mod.rs @@ -8,26 +8,34 @@ mod config; mod gasometer; mod invoker; -pub use self::config::Config; -pub use self::gasometer::{eval as eval_gasometer, GasometerState}; -pub use self::invoker::{ - routines, EtableResolver, Invoker, InvokerState, PrecompileSet, Resolver, SubstackInvoke, - TransactArgs, TransactInvoke, TransactValue, -}; - -use crate::{ExitError, GasMutState, GasState, MergeStrategy, RuntimeState}; use alloc::vec::Vec; + +use evm_interpreter::{ + error::{CallCreateTrap, ExitError}, + etable, machine, + runtime::{GasState, RuntimeState}, +}; use primitive_types::{H160, H256, U256}; +pub use self::{ + config::Config, + gasometer::{eval as eval_gasometer, GasometerState}, + invoker::{ + routines, EtableResolver, Invoker, InvokerState, PrecompileSet, Resolver, SubstackInvoke, + TransactArgs, TransactInvoke, TransactValue, + }, +}; +use crate::{gasometer::GasMutState, MergeStrategy}; + /// Standard machine. -pub type Machine<'config> = crate::Machine>; +pub type Machine<'config> = machine::Machine>; /// Standard Etable opcode handle function. -pub type Efn<'config, H> = crate::Efn, H, crate::trap::CallCreateTrap>; +pub type Efn<'config, H> = etable::Efn, H, CallCreateTrap>; /// Standard Etable. pub type Etable<'config, H, F = Efn<'config, H>> = - crate::Etable, H, crate::trap::CallCreateTrap, F>; + etable::Etable, H, CallCreateTrap, F>; pub struct State<'config> { pub runtime: RuntimeState, diff --git a/tracer/Cargo.toml b/tracer/Cargo.toml index a5c1ddb87..c56c01150 100644 --- a/tracer/Cargo.toml +++ b/tracer/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "evm-tracer" version = "0.0.0-dev" -edition = "2021" +edition = { workspace = true } +rust-version = { workspace = true } license = { workspace = true } authors = { workspace = true } repository = { workspace = true } diff --git a/tracer/src/lib.rs b/tracer/src/lib.rs index 7b0b01f81..fb8daf698 100644 --- a/tracer/src/lib.rs +++ b/tracer/src/lib.rs @@ -1,6 +1,6 @@ mod standard; -use evm::{Machine, Opcode}; +use evm::interpreter::{machine::Machine, opcode::Opcode}; pub trait EvalTracer { fn on_eval(&mut self, machine: &Machine, handle: &H, opcode: Opcode, position: usize); diff --git a/tracer/src/standard.rs b/tracer/src/standard.rs index 1177cf0f7..73f9fabef 100644 --- a/tracer/src/standard.rs +++ b/tracer/src/standard.rs @@ -1,5 +1,7 @@ -use evm::standard::{Machine, State}; -use evm::Opcode; +use evm::{ + interpreter::opcode::Opcode, + standard::{Machine, State}, +}; pub trait EvalTracer { fn on_eval(&mut self, machine: &Machine, handle: &H, opcode: Opcode, position: usize);