diff --git a/README.md b/README.md index 76f5b0f1e3..aea98ec899 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ go to `cd bins/revme/` Download eth tests from (this will take some time): `git clone https://github.com/ethereum/tests` -run tests with command: `cargo run --release -- statetest tests/GeneralStateTests/` +run tests with command: `cargo run --release -- statetest tests/GeneralStateTests/ tests/LegacyTests/Constantinople/GeneralStateTests` `GeneralStateTests` contains all tests related to EVM. diff --git a/bins/revme/src/statetest/models/spec.rs b/bins/revme/src/statetest/models/spec.rs index 0d91e6ea06..45d7567e2a 100644 --- a/bins/revme/src/statetest/models/spec.rs +++ b/bins/revme/src/statetest/models/spec.rs @@ -21,12 +21,7 @@ pub enum SpecName { BerlinToLondonAt5, London, Merge, - #[serde(alias = "Merge+3540+3670")] - MergeEOF, - #[serde(alias = "Merge+3860")] - MergeMeterInitCode, - #[serde(alias = "Merge+3855")] - MergePush0, + Shanghai, #[serde(other)] Unknown, } @@ -46,9 +41,7 @@ impl SpecName { Self::Berlin => SpecId::BERLIN, Self::London | Self::BerlinToLondonAt5 => SpecId::LONDON, Self::Merge => SpecId::MERGE, - Self::MergeEOF => SpecId::MERGE_EOF, - Self::MergeMeterInitCode => SpecId::MERGE_EOF, - Self::MergePush0 => SpecId::MERGE_EOF, + Self::Shanghai => SpecId::CANCUN, Self::ByzantiumToConstantinopleAt5 | Self::Constantinople => { panic!("Overriden with PETERSBURG") } diff --git a/bins/revme/src/statetest/runner.rs b/bins/revme/src/statetest/runner.rs index 8eb55c910f..aafb1434b3 100644 --- a/bins/revme/src/statetest/runner.rs +++ b/bins/revme/src/statetest/runner.rs @@ -97,6 +97,10 @@ pub fn execute_test_suit(path: &Path, elapsed: &Arc>) -> Result< return Ok(()); } + if path.to_str().unwrap().contains("stEOF") { + return Ok(()); + } + let json_reader = std::fs::read(path).unwrap(); let suit: TestSuit = serde_json::from_reader(&*json_reader)?; @@ -191,9 +195,6 @@ pub fn execute_test_suit(path: &Path, elapsed: &Arc>) -> Result< spec_name, SpecName::ByzantiumToConstantinopleAt5 | SpecName::Constantinople - | SpecName::MergeEOF - | SpecName::MergeMeterInitCode - | SpecName::MergePush0 | SpecName::Unknown ) { continue; diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index 08b0320d86..4697281193 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -145,6 +145,15 @@ pub fn sha3_cost(len: u64) -> Option { SHA3.checked_add(SHA3WORD.checked_mul(if wordr == 0 { wordd } else { wordd + 1 })?) } +/// EIP-3860: Limit and meter initcode +/// apply extra gas cost of 2 for every 32-byte chunk of initcode +/// Can't overflow as initcode length is assumed to be checked +pub fn initcode_cost(len: u64) -> u64 { + let wordd = len / 32; + let wordr = len % 32; + INITCODE_WORD_COST * if wordr == 0 { wordd } else { wordd + 1 } +} + pub fn sload_cost(is_cold: bool) -> u64 { if SPEC::enabled(BERLIN) { if is_cold { diff --git a/crates/interpreter/src/gas/constants.rs b/crates/interpreter/src/gas/constants.rs index b49b42e496..6cca72ad22 100644 --- a/crates/interpreter/src/gas/constants.rs +++ b/crates/interpreter/src/gas/constants.rs @@ -35,4 +35,7 @@ pub const COLD_SLOAD_COST: u64 = 2100; pub const COLD_ACCOUNT_ACCESS_COST: u64 = 2600; pub const WARM_STORAGE_READ_COST: u64 = 100; +/// EIP-3860 : Limit and meter initcode +pub const INITCODE_WORD_COST: u64 = 2; + pub const CALL_STIPEND: u64 = 2300; diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index 2b476b909b..7facd864bf 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -38,6 +38,8 @@ pub enum InstructionResult { CreateContractSizeLimit, /// Error on created contract that begins with EF CreateContractStartingWithEF, + /// EIP-3860: Limit and meter initcode. Initcode size limit exceeded. + CreateInitcodeSizeLimit, // Fatal external error. Returned by database. FatalExternalError, @@ -94,6 +96,7 @@ impl From for SuccessOrHalt { InstructionResult::CreateContractStartingWithEF => { Self::Halt(Halt::CreateContractSizeLimit) } + InstructionResult::CreateInitcodeSizeLimit => Self::Internal, InstructionResult::FatalExternalError => Self::FatalExternalError, } } diff --git a/crates/interpreter/src/instructions.rs b/crates/interpreter/src/instructions.rs index dc37062f62..6a9e094999 100644 --- a/crates/interpreter/src/instructions.rs +++ b/crates/interpreter/src/instructions.rs @@ -73,6 +73,7 @@ pub fn eval(opcode: u8, interp: &mut Interpreter, host: &mut H opcode::PC => control::pc(interp, host), opcode::MSIZE => memory::msize(interp, host), opcode::JUMPDEST => control::jumpdest(interp, host), + opcode::PUSH0 => stack::push0::(interp, host), opcode::PUSH1 => stack::push::<1>(interp, host), opcode::PUSH2 => stack::push::<2>(interp, host), opcode::PUSH3 => stack::push::<3>(interp, host), diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 7ed0e081f2..55febadac9 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -1,4 +1,5 @@ use crate::primitives::{Bytes, Spec, SpecId::*, B160, B256, U256}; +use crate::MAX_INITCODE_SIZE; use crate::{ alloc::vec::Vec, gas::{self, COLD_ACCOUNT_ACCESS_COST, WARM_STORAGE_READ_COST}, @@ -240,6 +241,14 @@ pub fn create( code_offset, InstructionResult::InvalidOperandOOG ); + // EIP-3860: Limit and meter initcode + if SPEC::enabled(SHANGHAI) { + if len > MAX_INITCODE_SIZE { + interpreter.instruction_result = InstructionResult::CreateInitcodeSizeLimit; + return; + } + gas!(interpreter, gas::initcode_cost(len as u64)); + } memory_resize!(interpreter, code_offset, len); Bytes::copy_from_slice(interpreter.memory.get_slice(code_offset, len)) }; diff --git a/crates/interpreter/src/instructions/opcode.rs b/crates/interpreter/src/instructions/opcode.rs index 320f1bca14..74dbf019ed 100644 --- a/crates/interpreter/src/instructions/opcode.rs +++ b/crates/interpreter/src/instructions/opcode.rs @@ -48,6 +48,7 @@ pub const JUMPI: u8 = 0x57; pub const PC: u8 = 0x58; pub const MSIZE: u8 = 0x59; pub const JUMPDEST: u8 = 0x5b; +pub const PUSH0: u8 = 0x5f; pub const PUSH1: u8 = 0x60; pub const PUSH2: u8 = 0x61; pub const PUSH3: u8 = 0x62; @@ -389,7 +390,7 @@ macro_rules! gas_opcodee { /* 0x5c */ OpInfo::none(), /* 0x5d */ OpInfo::none(), /* 0x5e */ OpInfo::none(), - /* 0x5f */ OpInfo::none(), + /* 0x5f PUSH0 */ OpInfo::gas(gas::BASE), /* 0x60 PUSH1 */ OpInfo::push_opcode(), /* 0x61 PUSH2 */ OpInfo::push_opcode(), /* 0x62 PUSH3 */ OpInfo::push_opcode(), @@ -620,9 +621,13 @@ pub const fn spec_opcode_gas(spec_id: SpecId) -> &'static [OpInfo; 256] { gas_opcodee!(MERGE, SpecId::MERGE); MERGE } - SpecId::MERGE_EOF => { - gas_opcodee!(MERGE_EOF, SpecId::MERGE_EOF); - MERGE_EOF + SpecId::SHANGHAI => { + gas_opcodee!(SHANGHAI, SpecId::SHANGHAI); + SHANGHAI + } + SpecId::CANCUN => { + gas_opcodee!(CANCUN, SpecId::CANCUN); + CANCUN } SpecId::LATEST => { gas_opcodee!(LATEST, SpecId::LATEST); @@ -727,7 +732,7 @@ pub const OPCODE_JUMPMAP: [Option<&'static str>; 256] = [ /* 0x5c */ None, /* 0x5d */ None, /* 0x5e */ None, - /* 0x5f */ None, + /* 0x5f */ Some("PUSH0"), /* 0x60 */ Some("PUSH1"), /* 0x61 */ Some("PUSH2"), /* 0x62 */ Some("PUSH3"), diff --git a/crates/interpreter/src/instructions/stack.rs b/crates/interpreter/src/instructions/stack.rs index 228e822c9d..fee9dc2231 100644 --- a/crates/interpreter/src/instructions/stack.rs +++ b/crates/interpreter/src/instructions/stack.rs @@ -1,3 +1,6 @@ +use crate::InstructionResult; +use revm_primitives::{Spec, SpecId::SHANGHAI, U256}; + use crate::{interpreter::Interpreter, Host}; pub fn pop(interpreter: &mut Interpreter, _host: &mut dyn Host) { @@ -7,6 +10,17 @@ pub fn pop(interpreter: &mut Interpreter, _host: &mut dyn Host) { } } +/// 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 dyn Host) { + // gas!(interp, gas::BASE); + // EIP-3855: PUSH0 instruction + check!(interpreter, SPEC::enabled(SHANGHAI)); + if let Err(result) = interpreter.stack.push(U256::ZERO) { + interpreter.instruction_result = result; + } +} + pub fn push(interpreter: &mut Interpreter, _host: &mut dyn Host) { // gas!(interp, gas::VERYLOW); let start = interpreter.instruction_pointer; diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index a8491e9fd7..ddf7cf78df 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -18,6 +18,12 @@ use core::ops::Range; pub const STACK_LIMIT: u64 = 1024; pub const CALL_STACK_LIMIT: u64 = 1024; +/// EIP-170: Contract code size limit +/// By default limit is 0x6000 (~25kb) +pub const MAX_CODE_SIZE: usize = 0x6000; +/// EIP-3860: Limit and meter initcode +pub const MAX_INITCODE_SIZE: usize = 2 * MAX_CODE_SIZE; + pub struct Interpreter { /// Instruction pointer. pub instruction_pointer: *const u8, diff --git a/crates/primitives/src/bytecode.rs b/crates/primitives/src/bytecode.rs index 8a99670747..7c4d2500a8 100644 --- a/crates/primitives/src/bytecode.rs +++ b/crates/primitives/src/bytecode.rs @@ -1,7 +1,7 @@ mod jump_table; use crate::{keccak256, B256, KECCAK_EMPTY}; -use alloc::sync::Arc; +use alloc::{sync::Arc, vec, vec::Vec}; use bytes::Bytes; pub use jump_table::{Analysis, AnalysisData, ValidJumpAddress}; diff --git a/crates/primitives/src/bytecode/jump_table.rs b/crates/primitives/src/bytecode/jump_table.rs index 3372927796..89782c36e0 100644 --- a/crates/primitives/src/bytecode/jump_table.rs +++ b/crates/primitives/src/bytecode/jump_table.rs @@ -1,4 +1,4 @@ -use alloc::sync::Arc; +use alloc::{sync::Arc, vec::Vec}; #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Analysis { diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index 5ffc615d15..742398b64b 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -55,6 +55,9 @@ impl TransactTo { pub fn create() -> Self { Self::Create(CreateScheme::Create) } + pub fn is_create(&self) -> bool { + matches!(self, Self::Create(_)) + } } /// Create scheme. diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 8140210461..cd5941a0b1 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), no_std)] pub mod bits; pub mod bytecode; pub mod db; diff --git a/crates/primitives/src/log.rs b/crates/primitives/src/log.rs index 1d672e544c..3c16ed035a 100644 --- a/crates/primitives/src/log.rs +++ b/crates/primitives/src/log.rs @@ -1,4 +1,5 @@ use crate::{bytes::Bytes, B160, B256}; +use alloc::vec::Vec; #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/crates/primitives/src/precompile.rs b/crates/primitives/src/precompile.rs index 8a9fa1dbd9..009680c6ff 100644 --- a/crates/primitives/src/precompile.rs +++ b/crates/primitives/src/precompile.rs @@ -1,3 +1,5 @@ +use alloc::vec::Vec; + /// A precompile operation result. pub type PrecompileResult = Result<(u64, Vec), PrecompileError>; diff --git a/crates/primitives/src/result.rs b/crates/primitives/src/result.rs index c3226a1422..a1775089f2 100644 --- a/crates/primitives/src/result.rs +++ b/crates/primitives/src/result.rs @@ -1,4 +1,5 @@ use crate::{Log, State, B160}; +use alloc::vec::Vec; use bytes::Bytes; use ruint::aliases::U256; @@ -103,6 +104,8 @@ pub enum InvalidTransaction { OverflowPaymentInTransaction, /// Nonce overflows in transaction, NonceOverflowInTransaction, + /// EIP-3860: Limit and meter initcode + CreateInitcodeSizeLimit, } /// When transaction return successfully without halts. diff --git a/crates/primitives/src/specification.rs b/crates/primitives/src/specification.rs index 2aefdb8fe0..3d3a1d9375 100644 --- a/crates/primitives/src/specification.rs +++ b/crates/primitives/src/specification.rs @@ -21,8 +21,9 @@ pub enum SpecId { ARROW_GLACIER = 13, // Arrow Glacier 13773000 GRAY_GLACIER = 14, // Gray Glacier 15050000 MERGE = 15, // Paris/Merge TBD (Depends on difficulty) - MERGE_EOF = 16, // Merge+EOF TBD - LATEST = 17, + SHANGHAI = 16, + CANCUN = 17, + LATEST = 18, } impl SpecId { @@ -48,7 +49,7 @@ impl From<&str> for SpecId { "Berlin" => SpecId::BERLIN, "London" => SpecId::LONDON, "Merge" => SpecId::MERGE, - "MergeEOF" => SpecId::MERGE_EOF, + "Shanghai" => SpecId::SHANGHAI, _ => SpecId::LATEST, } } @@ -97,4 +98,5 @@ spec!(LONDON, LondonSpec); // GRAY_GLACIER no EVM spec change spec!(MERGE, MergeSpec); // MERGE_EOF is pending EVM change +spec!(SHANGHAI, ShanghaiSpec); spec!(LATEST, LatestSpec); diff --git a/crates/primitives/src/utilities.rs b/crates/primitives/src/utilities.rs index 09cb3d3dbd..4726093d20 100644 --- a/crates/primitives/src/utilities.rs +++ b/crates/primitives/src/utilities.rs @@ -34,6 +34,7 @@ pub fn create2_address(caller: B160, code_hash: B256, salt: U256) -> B160 { /// Serde functions to serde as [bytes::Bytes] hex string #[cfg(feature = "serde")] pub mod serde_hex_bytes { + use alloc::string::String; use serde::{Deserialize, Deserializer, Serializer}; pub fn serialize(x: T, s: S) -> Result @@ -41,7 +42,7 @@ pub mod serde_hex_bytes { S: Serializer, T: AsRef<[u8]>, { - s.serialize_str(&format!("0x{}", hex::encode(x.as_ref()))) + s.serialize_str(&alloc::format!("0x{}", hex::encode(x.as_ref()))) } pub fn deserialize<'de, D>(d: D) -> Result diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index d140c822a4..3aaa6df71e 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -167,7 +167,8 @@ pub fn to_precompile_id(spec_id: SpecId) -> revm_precompile::SpecId { | SpecId::ARROW_GLACIER | SpecId::GRAY_GLACIER | SpecId::MERGE - | SpecId::MERGE_EOF + | SpecId::SHANGHAI + | SpecId::CANCUN | SpecId::LATEST => revm_precompile::SpecId::BERLIN, } } @@ -191,7 +192,8 @@ pub fn evm_inner<'a, DB: Database, const INSPECT: bool>( create_evm!(LondonSpec, db, env, insp) } SpecId::MERGE => create_evm!(MergeSpec, db, env, insp), - SpecId::MERGE_EOF => create_evm!(MergeSpec, db, env, insp), + SpecId::SHANGHAI => create_evm!(ShanghaiSpec, db, env, insp), + SpecId::CANCUN => create_evm!(LatestSpec, db, env, insp), SpecId::LATEST => create_evm!(LatestSpec, db, env, insp), } } diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs index 099314d4b7..d1a1a346f2 100644 --- a/crates/revm/src/evm_impl.rs +++ b/crates/revm/src/evm_impl.rs @@ -13,6 +13,7 @@ use crate::primitives::{ use crate::{db::Database, journaled_state::JournaledState, precompile, Inspector}; use alloc::vec::Vec; use core::{cmp::min, marker::PhantomData}; +use revm_interpreter::{MAX_CODE_SIZE, MAX_INITCODE_SIZE}; use revm_precompile::{Precompile, Precompiles}; pub struct EVMData<'a, DB: Database> { @@ -146,6 +147,15 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact gas.record_cost(gas_limit); } + // load coinbase + // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm + if GSPEC::enabled(SHANGHAI) { + self.data + .journaled_state + .load_account(self.data.env.block.coinbase, self.data.db) + .map_err(EVMError::Database)?; + } + // call inner handling of call/create // TODO can probably be refactored to look nicer. let (exit_reason, ret_gas, output) = match self.data.env.tx.transact_to { @@ -351,6 +361,21 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, let is_create = matches!(self.data.env.tx.transact_to, TransactTo::Create(_)); let input = &self.data.env.tx.data; + // EIP-3860: Limit and meter initcode + let initcode_cost = if SPEC::enabled(SHANGHAI) && self.data.env.tx.transact_to.is_create() { + let initcode_len = self.data.env.tx.data.len(); + if initcode_len > MAX_INITCODE_SIZE { + return Err(InvalidTransaction::CreateInitcodeSizeLimit.into()); + } + if crate::USE_GAS { + gas::initcode_cost(initcode_len as u64) + } else { + 0 + } + } else { + 0 + }; + if crate::USE_GAS { let zero_data_len = input.iter().filter(|v| **v == 0).count() as u64; let non_zero_data_len = input.len() as u64 - zero_data_len; @@ -393,6 +418,7 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, let gas_transaction_non_zero_data = if SPEC::enabled(ISTANBUL) { 16 } else { 68 }; Ok(transact + + initcode_cost + zero_data_len * gas::TRANSACTION_ZERO_DATA + non_zero_data_len * gas_transaction_non_zero_data + accessed_accounts * gas::ACCESS_LIST_ADDRESS @@ -577,7 +603,13 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, // 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(0x6000) + && bytes.len() + > self + .data + .env + .cfg + .limit_contract_code_size + .unwrap_or(MAX_CODE_SIZE) { self.data.journaled_state.checkpoint_revert(checkpoint); return self.create_end( diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 5e7a89f417..a92b9bdd19 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -4,6 +4,7 @@ use crate::interpreter::{CallInputs, CreateInputs, Gas, InstructionResult}; use crate::primitives::{db::Database, Bytes, B160}; use crate::{evm_impl::EVMData, Inspector}; +#[allow(dead_code)] #[derive(Clone, Copy, Debug, Default)] pub struct GasInspector { /// We now batch continual gas_block in one go, that means we need to reduce it if we want diff --git a/crates/revm/src/journaled_state.rs b/crates/revm/src/journaled_state.rs index 4fbcafa291..e715524724 100644 --- a/crates/revm/src/journaled_state.rs +++ b/crates/revm/src/journaled_state.rs @@ -1,6 +1,6 @@ use crate::interpreter::{inner_models::SelfDestructResult, InstructionResult}; use crate::primitives::{ - db::Database, hash_map::Entry, Account, Bytecode, HashMap, Log, StorageSlot, B160, + db::Database, hash_map::Entry, Account, Bytecode, HashMap, Log, State, StorageSlot, B160, KECCAK_EMPTY, U256, }; use alloc::{vec, vec::Vec}; @@ -26,9 +26,6 @@ pub struct JournaledState { pub num_of_precompiles: usize, } -pub type State = HashMap; -pub type Storage = HashMap; - #[derive(Debug, Clone, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum JournalEntry { diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index c338bd251c..c6e974c188 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] -//#![no_std] pub mod db; mod evm; mod evm_impl;