Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Shanghai): All EIPs: push0, warm coinbase, limit/measure initcode #376

Merged
merged 3 commits into from
Feb 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
11 changes: 2 additions & 9 deletions bins/revme/src/statetest/models/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand All @@ -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")
}
Expand Down
7 changes: 4 additions & 3 deletions bins/revme/src/statetest/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ pub fn execute_test_suit(path: &Path, elapsed: &Arc<Mutex<Duration>>) -> 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)?;

Expand Down Expand Up @@ -191,9 +195,6 @@ pub fn execute_test_suit(path: &Path, elapsed: &Arc<Mutex<Duration>>) -> Result<
spec_name,
SpecName::ByzantiumToConstantinopleAt5
| SpecName::Constantinople
| SpecName::MergeEOF
| SpecName::MergeMeterInitCode
| SpecName::MergePush0
| SpecName::Unknown
) {
continue;
Expand Down
9 changes: 9 additions & 0 deletions crates/interpreter/src/gas/calc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,15 @@ pub fn sha3_cost(len: u64) -> Option<u64> {
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<SPEC: Spec>(is_cold: bool) -> u64 {
if SPEC::enabled(BERLIN) {
if is_cold {
Expand Down
3 changes: 3 additions & 0 deletions crates/interpreter/src/gas/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
3 changes: 3 additions & 0 deletions crates/interpreter/src/instruction_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -94,6 +96,7 @@ impl From<InstructionResult> for SuccessOrHalt {
InstructionResult::CreateContractStartingWithEF => {
Self::Halt(Halt::CreateContractSizeLimit)
}
InstructionResult::CreateInitcodeSizeLimit => Self::Internal,
InstructionResult::FatalExternalError => Self::FatalExternalError,
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/interpreter/src/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ pub fn eval<H: Host, S: Spec>(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::<S>(interp, host),
opcode::PUSH1 => stack::push::<1>(interp, host),
opcode::PUSH2 => stack::push::<2>(interp, host),
opcode::PUSH3 => stack::push::<3>(interp, host),
Expand Down
9 changes: 9 additions & 0 deletions crates/interpreter/src/instructions/host.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -240,6 +241,14 @@ pub fn create<const IS_CREATE2: bool, SPEC: Spec>(
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))
};
Expand Down
15 changes: 10 additions & 5 deletions crates/interpreter/src/instructions/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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"),
Expand Down
14 changes: 14 additions & 0 deletions crates/interpreter/src/instructions/stack.rs
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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<SPEC: Spec>(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<const N: usize>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
// gas!(interp, gas::VERYLOW);
let start = interpreter.instruction_pointer;
Expand Down
6 changes: 6 additions & 0 deletions crates/interpreter/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion crates/primitives/src/bytecode.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down
2 changes: 1 addition & 1 deletion crates/primitives/src/bytecode/jump_table.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use alloc::sync::Arc;
use alloc::{sync::Arc, vec::Vec};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Analysis {
Expand Down
3 changes: 3 additions & 0 deletions crates/primitives/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions crates/primitives/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![cfg_attr(not(feature = "std"), no_std)]
pub mod bits;
pub mod bytecode;
pub mod db;
Expand Down
1 change: 1 addition & 0 deletions crates/primitives/src/log.rs
Original file line number Diff line number Diff line change
@@ -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))]
Expand Down
2 changes: 2 additions & 0 deletions crates/primitives/src/precompile.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use alloc::vec::Vec;

/// A precompile operation result.
pub type PrecompileResult = Result<(u64, Vec<u8>), PrecompileError>;

Expand Down
3 changes: 3 additions & 0 deletions crates/primitives/src/result.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{Log, State, B160};
use alloc::vec::Vec;
use bytes::Bytes;
use ruint::aliases::U256;

Expand Down Expand Up @@ -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.
Expand Down
8 changes: 5 additions & 3 deletions crates/primitives/src/specification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
}
}
Expand Down Expand Up @@ -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);
3 changes: 2 additions & 1 deletion crates/primitives/src/utilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,15 @@ 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<S, T>(x: T, s: S) -> Result<S::Ok, S::Error>
where
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<bytes::Bytes, D::Error>
Expand Down
6 changes: 4 additions & 2 deletions crates/revm/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
Expand All @@ -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),
}
}
34 changes: 33 additions & 1 deletion crates/revm/src/evm_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -146,6 +147,15 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact<DB::Error>
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 {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down
Loading