Skip to content

Commit

Permalink
feat(Shanghai): All EIPs: push0, warm coinbase, limit/measure initcode (
Browse files Browse the repository at this point in the history
#376)

* chore: no_std to primitives

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

* Update README.md
  • Loading branch information
rakita committed Feb 13, 2023
1 parent 6710511 commit 08ce847
Show file tree
Hide file tree
Showing 16 changed files with 109 additions and 24 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -34,7 +34,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
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
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
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
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
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
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
@@ -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
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
@@ -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
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
3 changes: 3 additions & 0 deletions crates/primitives/src/env.rs
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
2 changes: 2 additions & 0 deletions crates/primitives/src/result.rs
Expand Up @@ -104,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
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);
6 changes: 4 additions & 2 deletions crates/revm/src/evm.rs
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
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

0 comments on commit 08ce847

Please sign in to comment.