Skip to content

Commit

Permalink
Cancun support (#3)
Browse files Browse the repository at this point in the history
* feat: support transient storage opcodes (EIP-1153)

* feat: support memory copying instruction (EIP-5656)

* feat: Support cancun selfdestruct changes (EIP-6780)

---------

Co-authored-by: Agusrodri <agusrodriguez2456@gmail.com>
Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com>
  • Loading branch information
3 people authored and gonzamontiel committed Aug 16, 2024
1 parent 879ffe2 commit edba510
Show file tree
Hide file tree
Showing 15 changed files with 278 additions and 12 deletions.
19 changes: 18 additions & 1 deletion core/src/eval/misc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::Control;
use crate::{ExitError, ExitFatal, ExitRevert, ExitSucceed, Machine};
use core::cmp::min;
use core::cmp::{max, min};
use primitive_types::{H256, U256};

#[inline]
Expand Down Expand Up @@ -92,6 +92,23 @@ pub fn mload(state: &mut Machine) -> Control {
Control::Continue(1)
}

/// Support for EIP-5656: MCOPY instruction.
#[inline]
pub fn mcopy(state: &mut Machine) -> Control {
pop_u256!(state, dst, src, len);
try_or_fail!(state.memory.resize_offset(max(dst, src), len));

if len.is_zero() {
return Control::Continue(1);
}

let dst = as_usize_or_fail!(dst);
let src = as_usize_or_fail!(src);
let len = as_usize_or_fail!(len);
state.memory.copy(dst, src, len);
Control::Continue(1)
}

#[inline]
pub fn mstore(state: &mut Machine) -> Control {
pop_u256!(state, index);
Expand Down
5 changes: 5 additions & 0 deletions core/src/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ fn eval_jumpdest(_state: &mut Machine, _opcode: Opcode, _position: usize) -> Con
Control::Continue(1)
}

fn eval_mcopy(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control {
self::misc::mcopy(state)
}

fn eval_push0(state: &mut Machine, _opcode: Opcode, position: usize) -> Control {
self::misc::push(state, 0, position)
}
Expand Down Expand Up @@ -497,6 +501,7 @@ pub fn eval(state: &mut Machine, opcode: Opcode, position: usize) -> Control {
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::MCOPY.as_usize()] = eval_mcopy as _;

table[Opcode::PUSH0.as_usize()] = eval_push0 as _;
table[Opcode::PUSH1.as_usize()] = eval_push1 as _;
Expand Down
58 changes: 56 additions & 2 deletions core/src/memory.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{ExitError, ExitFatal};
use alloc::vec::Vec;
use core::cmp::min;
use core::cmp::{max, min};
use core::ops::{BitAnd, Not};
use primitive_types::U256;

Expand Down Expand Up @@ -181,6 +181,15 @@ impl Memory {

self.set(memory_offset, data, Some(ulen))
}

/// Copies part of the memory inside another part of itself.
pub fn copy(&mut self, dst: usize, src: usize, len: usize) {
let resize_offset = max(dst, src);
if self.data.len() < resize_offset + len {
self.data.resize(resize_offset + len, 0);
}
self.data.copy_within(src..src + len, dst);
}
}

/// Rounds up `x` to the closest multiple of 32. If `x % 32 == 0` then `x` is returned.
Expand All @@ -192,7 +201,7 @@ fn next_multiple_of_32(x: U256) -> Option<U256> {

#[cfg(test)]
mod tests {
use super::{next_multiple_of_32, U256};
use super::{next_multiple_of_32, Memory, U256};

#[test]
fn test_next_multiple_of_32() {
Expand Down Expand Up @@ -225,4 +234,49 @@ mod tests {
}
}
}

#[test]
fn test_memory_copy_works() {
// Create a new instance of memory
let mut memory = Memory::new(100usize);

// Set the [0,0,0,1,2,3,4] array as memory data.
//
// We insert the [1,2,3,4] array on index 3,
// that's why we have the zero padding at the beginning.
memory.set(3usize, &[1u8, 2u8, 3u8, 4u8], None).unwrap();
assert_eq!(memory.data(), &[0u8, 0u8, 0u8, 1u8, 2u8, 3u8, 4u8].to_vec());

// Copy 1 byte into index 0.
// As the length is 1, we only copy the byte present on index 3.
memory.copy(0usize, 3usize, 1usize);

// Now the new memory data results in [1,0,0,1,2,3,4]
assert_eq!(memory.data(), &[1u8, 0u8, 0u8, 1u8, 2u8, 3u8, 4u8].to_vec());
}

#[test]
fn test_memory_copy_resize() {
// Create a new instance of memory
let mut memory = Memory::new(100usize);

// Set the [0,0,0,1,2,3,4] array as memory data.
//
// We insert the [1,2,3,4] array on index 3,
// that's why we have the zero padding at the beginning.
memory.set(3usize, &[1u8, 2u8, 3u8, 4u8], None).unwrap();
assert_eq!(memory.data(), &[0u8, 0u8, 0u8, 1u8, 2u8, 3u8, 4u8].to_vec());

// Copy 2 bytes into index 3.
// As the length is 2, we copy the bytes present on indexes 6 and 7,
// which are [4,0].
memory.copy(3usize, 6usize, 2usize);

// Now the new memory data results in [0, 0, 0, 4, 0, 3, 4, 0].
// An extra element is added due to rezising.
assert_eq!(
memory.data(),
&[0u8, 0u8, 0u8, 4u8, 0u8, 3u8, 4u8, 0u8].to_vec()
);
}
}
6 changes: 6 additions & 0 deletions core/src/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ impl Opcode {
pub const MSIZE: Opcode = Opcode(0x59);
/// `JUMPDEST`
pub const JUMPDEST: Opcode = Opcode(0x5b);
/// `MCOPY`
pub const MCOPY: Opcode = Opcode(0x5e);

/// `PUSHn`
pub const PUSH0: Opcode = Opcode(0x5f);
Expand Down Expand Up @@ -225,6 +227,10 @@ impl Opcode {
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);
Expand Down
8 changes: 8 additions & 0 deletions gasometer/src/costs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,14 @@ pub fn sstore_cost(
)
}

pub fn tload_cost(config: &Config) -> Result<u64, ExitError> {
Ok(config.gas_storage_read_warm)
}

pub fn tstore_cost(config: &Config) -> Result<u64, ExitError> {
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;
let should_charge_topup = if eip161 {
Expand Down
21 changes: 20 additions & 1 deletion gasometer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ pub fn dynamic_opcode_cost<H: Handler>(
len: U256::from_big_endian(&stack.peek(3)?[..]),
}
}
Opcode::CALLDATACOPY | Opcode::CODECOPY => GasCost::VeryLowCopy {
Opcode::CALLDATACOPY | Opcode::CODECOPY | Opcode::MCOPY => GasCost::VeryLowCopy {
len: U256::from_big_endian(&stack.peek(2)?[..]),
},
Opcode::EXP => GasCost::Exp {
Expand All @@ -599,6 +599,7 @@ pub fn dynamic_opcode_cost<H: Handler>(
target_is_cold: handler.is_cold(address, Some(index))?,
}
}
Opcode::TLOAD => GasCost::TLoad,

Opcode::DELEGATECALL if config.has_delegate_call => {
let target = stack.peek(1)?.into();
Expand Down Expand Up @@ -632,6 +633,7 @@ pub fn dynamic_opcode_cost<H: Handler>(
target_is_cold: handler.is_cold(address, Some(index))?,
}
}
Opcode::TSTORE if !is_static => GasCost::TStore,
Opcode::LOG0 if !is_static => GasCost::Log {
n: 0,
len: U256::from_big_endian(&stack.peek(1)?[..]),
Expand Down Expand Up @@ -704,6 +706,16 @@ pub fn dynamic_opcode_cost<H: Handler>(
len: U256::from_big_endian(&stack.peek(1)?[..]),
}),

Opcode::MCOPY => {
let top0 = U256::from_big_endian(&stack.peek(0)?[..]);
let top1 = U256::from_big_endian(&stack.peek(1)?[..]);
let offset = top0.max(top1);
Some(MemoryCost {
offset,
len: U256::from_big_endian(&stack.peek(2)?[..]),
})
}

Opcode::CODECOPY | Opcode::CALLDATACOPY | Opcode::RETURNDATACOPY => Some(MemoryCost {
offset: U256::from_big_endian(&stack.peek(0)?[..]),
len: U256::from_big_endian(&stack.peek(2)?[..]),
Expand Down Expand Up @@ -867,6 +879,9 @@ impl<'config> Inner<'config> {
target_is_cold,
} => costs::sstore_cost(original, current, new, gas, target_is_cold, self.config)?,

GasCost::TLoad => costs::tload_cost(self.config)?,
GasCost::TStore => costs::tstore_cost(self.config)?,

GasCost::Sha3 { len } => costs::sha3_cost(len)?,
GasCost::Log { n, len } => costs::log_cost(n, len)?,
GasCost::VeryLowCopy { len } => costs::verylowcopy_cost(len)?,
Expand Down Expand Up @@ -1053,6 +1068,10 @@ pub 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,
}

/// Storage opcode will access. Used for tracking accessed storage (EIP-2929).
Expand Down
2 changes: 2 additions & 0 deletions runtime/src/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ pub fn eval<H: Handler>(state: &mut Runtime, opcode: Opcode, handler: &mut H) ->
Opcode::SLOAD => system::sload(state, handler),
Opcode::SSTORE => system::sstore(state, handler),
Opcode::GAS => system::gas(state, handler),
Opcode::TLOAD => system::tload(state, handler),
Opcode::TSTORE => system::tstore(state, handler),
Opcode::LOG0 => system::log(state, 0, handler),
Opcode::LOG1 => system::log(state, 1, handler),
Opcode::LOG2 => system::log(state, 2, handler),
Expand Down
17 changes: 17 additions & 0 deletions runtime/src/eval/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,23 @@ pub fn gas<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
Control::Continue
}

pub fn tload<H: Handler>(runtime: &mut Runtime, handler: &mut H) -> Control<H> {
pop!(runtime, index);
let value = handler.transient_storage(runtime.context.address, index);
push!(runtime, value);

Control::Continue
}

pub fn tstore<H: Handler>(runtime: &mut Runtime, handler: &mut H) -> Control<H> {
pop!(runtime, index, value);

match handler.set_transient_storage(runtime.context.address, index, value) {
Ok(()) => Control::Continue,
Err(e) => Control::Exit(e.into()),
}
}

pub fn log<H: Handler>(runtime: &mut Runtime, n: u8, handler: &mut H) -> Control<H> {
pop_u256!(runtime, offset, len);

Expand Down
10 changes: 10 additions & 0 deletions runtime/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ pub trait Handler {
fn code(&self, address: H160) -> Vec<u8>;
/// 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;

/// Get original storage value of address at index.
fn original_storage(&self, address: H160, index: H256) -> H256;

Expand Down Expand Up @@ -77,6 +80,13 @@ pub trait Handler {

/// 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, address: H160, topics: Vec<H256>, data: Vec<u8>) -> Result<(), ExitError>;
/// Mark an address to be deleted, with funds transferred to target.
Expand Down
32 changes: 32 additions & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ pub struct Config {
pub has_push0: bool,
/// Whether the gasometer is running in estimate mode.
pub estimate: bool,
/// Has EIP-6780. See [EIP-6780](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-6780.md)
pub has_eip_6780: bool,
}

impl Config {
Expand Down Expand Up @@ -342,6 +344,7 @@ impl Config {
has_base_fee: false,
has_push0: false,
estimate: false,
has_eip_6780: false,
}
}

Expand Down Expand Up @@ -396,6 +399,7 @@ impl Config {
has_base_fee: false,
has_push0: false,
estimate: false,
has_eip_6780: false,
}
}

Expand All @@ -419,6 +423,11 @@ impl Config {
Self::config_with_derived_values(DerivedConfigInputs::shanghai())
}

/// Cancun hard fork configuration.
pub const fn cancun() -> Config {
Self::config_with_derived_values(DerivedConfigInputs::cancun())
}

const fn config_with_derived_values(inputs: DerivedConfigInputs) -> Config {
let DerivedConfigInputs {
gas_storage_read_warm,
Expand All @@ -430,6 +439,7 @@ impl Config {
disallow_executable_format,
warm_coinbase_address,
max_initcode_size,
has_eip_6780,
} = inputs;

// See https://eips.ethereum.org/EIPS/eip-2929
Expand Down Expand Up @@ -493,6 +503,7 @@ impl Config {
has_base_fee,
has_push0,
estimate: false,
has_eip_6780,
}
}
}
Expand All @@ -509,6 +520,7 @@ struct DerivedConfigInputs {
disallow_executable_format: bool,
warm_coinbase_address: bool,
max_initcode_size: Option<usize>,
has_eip_6780: bool,
}

impl DerivedConfigInputs {
Expand All @@ -523,6 +535,7 @@ impl DerivedConfigInputs {
disallow_executable_format: false,
warm_coinbase_address: false,
max_initcode_size: None,
has_eip_6780: false,
}
}

Expand All @@ -537,6 +550,7 @@ impl DerivedConfigInputs {
disallow_executable_format: true,
warm_coinbase_address: false,
max_initcode_size: None,
has_eip_6780: false,
}
}

Expand All @@ -551,6 +565,7 @@ impl DerivedConfigInputs {
disallow_executable_format: true,
warm_coinbase_address: false,
max_initcode_size: None,
has_eip_6780: false,
}
}

Expand All @@ -566,6 +581,23 @@ impl DerivedConfigInputs {
warm_coinbase_address: true,
// 2 * 24576 as per EIP-3860
max_initcode_size: Some(0xC000),
has_eip_6780: false,
}
}

const fn cancun() -> Self {
Self {
gas_storage_read_warm: 100,
gas_sload_cold: 2100,
gas_access_list_storage_key: 1900,
decrease_clears_refund: true,
has_base_fee: true,
has_push0: true,
disallow_executable_format: true,
warm_coinbase_address: true,
// 2 * 24576 as per EIP-3860
max_initcode_size: Some(0xC000),
has_eip_6780: true,
}
}
}
Loading

0 comments on commit edba510

Please sign in to comment.