From ec083389d5e4d0b9d8a243ae991acaf5c2ff2730 Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Tue, 30 Apr 2024 16:24:58 -0700 Subject: [PATCH 1/6] add support for MCOPY instruction --- interpreter/src/etable.rs | 1 + interpreter/src/eval/misc.rs | 15 +++++++++- interpreter/src/eval/mod.rs | 9 ++++++ interpreter/src/memory.rs | 55 +++++++++++++++++++++++++++++++++-- interpreter/src/opcode.rs | 2 ++ src/standard/gasometer/mod.rs | 2 +- 6 files changed, 80 insertions(+), 4 deletions(-) diff --git a/interpreter/src/etable.rs b/interpreter/src/etable.rs index 45aa98917..bb161dbd3 100644 --- a/interpreter/src/etable.rs +++ b/interpreter/src/etable.rs @@ -174,6 +174,7 @@ impl Etable { 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 _; diff --git a/interpreter/src/eval/misc.rs b/interpreter/src/eval/misc.rs index 0a9b9c5a3..c1f8aefbd 100644 --- a/interpreter/src/eval/misc.rs +++ b/interpreter/src/eval/misc.rs @@ -1,7 +1,7 @@ use super::Control; use crate::utils::u256_to_h256; use crate::{ExitError, ExitException, ExitFatal, ExitSucceed, Machine}; -use core::cmp::min; +use core::cmp::{max,min}; use primitive_types::{H256, U256}; #[inline] @@ -94,6 +94,19 @@ pub fn mload(state: &mut Machine) -> Control { Control::Continue } +/// 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)); + + 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 +} + #[inline] pub fn mstore(state: &mut Machine) -> Control { pop_u256!(state, index); diff --git a/interpreter/src/eval/mod.rs b/interpreter/src/eval/mod.rs index ffd7aa296..b159fc88b 100644 --- a/interpreter/src/eval/mod.rs +++ b/interpreter/src/eval/mod.rs @@ -381,6 +381,15 @@ pub fn eval_jumpdest( Control::Continue } +pub fn eval_mcopy( + machine: &mut Machine, + _handle: &mut H, + _opcode: Opcode, + _position: usize, +) -> Control { + self::misc::mcopy(machine) +} + pub fn eval_push0( machine: &mut Machine, _handle: &mut H, diff --git a/interpreter/src/memory.rs b/interpreter/src/memory.rs index 0c6c92c5a..8470c9cb9 100644 --- a/interpreter/src/memory.rs +++ b/interpreter/src/memory.rs @@ -2,7 +2,7 @@ use crate::{ExitException, ExitFatal}; use alloc::vec; use alloc::vec::Vec; use core::ops::{BitAnd, Not, Range}; -use core::{cmp::min, mem}; +use core::{cmp::{max, min}, mem}; use primitive_types::U256; /// A sequencial memory. It uses Rust's `Vec` for internal @@ -218,6 +218,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. @@ -229,7 +238,7 @@ fn next_multiple_of_32(x: U256) -> Option { #[cfg(test)] mod tests { - use super::{next_multiple_of_32, U256}; + use super::{next_multiple_of_32, U256, Memory}; #[test] fn test_next_multiple_of_32() { @@ -262,4 +271,46 @@ 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()); + } } diff --git a/interpreter/src/opcode.rs b/interpreter/src/opcode.rs index b4d0a4746..fe092d99a 100644 --- a/interpreter/src/opcode.rs +++ b/interpreter/src/opcode.rs @@ -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); diff --git a/src/standard/gasometer/mod.rs b/src/standard/gasometer/mod.rs index a526de53b..b17cbe783 100644 --- a/src/standard/gasometer/mod.rs +++ b/src/standard/gasometer/mod.rs @@ -453,7 +453,7 @@ fn dynamic_opcode_cost( len: U256::from_big_endian(&stack.peek(1)?[..]), }), - Opcode::CODECOPY | Opcode::CALLDATACOPY | Opcode::RETURNDATACOPY => Some(MemoryCost { + Opcode::CODECOPY | Opcode::CALLDATACOPY | Opcode::RETURNDATACOPY | Opcode::MCOPY => Some(MemoryCost { offset: U256::from_big_endian(&stack.peek(0)?[..]), len: U256::from_big_endian(&stack.peek(2)?[..]), }), From ada1d31837edc42d147580bd43ce83e4dc9c5b0d Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Thu, 2 May 2024 07:19:00 -0700 Subject: [PATCH 2/6] update mcopy gas cost --- src/standard/gasometer/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/standard/gasometer/mod.rs b/src/standard/gasometer/mod.rs index b17cbe783..83bdac7f9 100644 --- a/src/standard/gasometer/mod.rs +++ b/src/standard/gasometer/mod.rs @@ -349,7 +349,7 @@ fn dynamic_opcode_cost( 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 { From f402f4d791e0d7baf20bf2a0dd7c650c00bdf8c9 Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Sun, 5 May 2024 12:00:48 -0700 Subject: [PATCH 3/6] fix memory cost for mcopy --- interpreter/src/eval/misc.rs | 4 ++++ src/standard/gasometer/mod.rs | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/interpreter/src/eval/misc.rs b/interpreter/src/eval/misc.rs index c1f8aefbd..c1c934465 100644 --- a/interpreter/src/eval/misc.rs +++ b/interpreter/src/eval/misc.rs @@ -100,6 +100,10 @@ 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; + } + let dst = as_usize_or_fail!(dst); let src = as_usize_or_fail!(src); let len = as_usize_or_fail!(len); diff --git a/src/standard/gasometer/mod.rs b/src/standard/gasometer/mod.rs index 83bdac7f9..c673b02f7 100644 --- a/src/standard/gasometer/mod.rs +++ b/src/standard/gasometer/mod.rs @@ -453,7 +453,14 @@ fn dynamic_opcode_cost( len: U256::from_big_endian(&stack.peek(1)?[..]), }), - Opcode::CODECOPY | Opcode::CALLDATACOPY | Opcode::RETURNDATACOPY | Opcode::MCOPY => Some(MemoryCost { + 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)?[..]), }), From f304e8392ce2c103b6cbb7d85ea21e9c92061835 Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Mon, 6 May 2024 07:26:56 -0700 Subject: [PATCH 4/6] fmt --- interpreter/src/eval/misc.rs | 4 ++-- interpreter/src/memory.rs | 36 ++++++++++++++++++++--------------- src/standard/gasometer/mod.rs | 7 +++++-- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/interpreter/src/eval/misc.rs b/interpreter/src/eval/misc.rs index c1c934465..916e71047 100644 --- a/interpreter/src/eval/misc.rs +++ b/interpreter/src/eval/misc.rs @@ -1,7 +1,7 @@ use super::Control; use crate::utils::u256_to_h256; use crate::{ExitError, ExitException, ExitFatal, ExitSucceed, Machine}; -use core::cmp::{max,min}; +use core::cmp::{max, min}; use primitive_types::{H256, U256}; #[inline] @@ -98,7 +98,7 @@ pub fn mload(state: &mut Machine) -> Control { #[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)); + try_or_fail!(state.memory.resize_offset(max(dst, src), len)); if len.is_zero() { return Control::Continue; diff --git a/interpreter/src/memory.rs b/interpreter/src/memory.rs index 8470c9cb9..65df6afe9 100644 --- a/interpreter/src/memory.rs +++ b/interpreter/src/memory.rs @@ -2,7 +2,10 @@ use crate::{ExitException, ExitFatal}; use alloc::vec; use alloc::vec::Vec; use core::ops::{BitAnd, Not, Range}; -use core::{cmp::{max, min}, mem}; +use core::{ + cmp::{max, min}, + mem, +}; use primitive_types::U256; /// A sequencial memory. It uses Rust's `Vec` for internal @@ -220,8 +223,8 @@ impl Memory { } /// 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); + 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); } @@ -238,7 +241,7 @@ fn next_multiple_of_32(x: U256) -> Option { #[cfg(test)] mod tests { - use super::{next_multiple_of_32, U256, Memory}; + use super::{next_multiple_of_32, Memory, U256}; #[test] fn test_next_multiple_of_32() { @@ -273,44 +276,47 @@ mod tests { } #[test] - fn test_memory_copy_works(){ + 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, + // 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()); + 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()); + assert_eq!(memory.data(), &[1u8, 0u8, 0u8, 1u8, 2u8, 3u8, 4u8].to_vec()); } #[test] - fn test_memory_copy_resize(){ + 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, + // 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()); + 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, + // 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()); + assert_eq!( + memory.data(), + &[0u8, 0u8, 0u8, 4u8, 0u8, 3u8, 4u8, 0u8].to_vec() + ); } } diff --git a/src/standard/gasometer/mod.rs b/src/standard/gasometer/mod.rs index c673b02f7..d3d189d24 100644 --- a/src/standard/gasometer/mod.rs +++ b/src/standard/gasometer/mod.rs @@ -457,8 +457,11 @@ fn dynamic_opcode_cost( 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)?[..]) }) - }, + 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)?[..]), From 3f15f922d23df2f7453c626de5ccdc9c3094c270 Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Tue, 14 May 2024 06:52:59 -0700 Subject: [PATCH 5/6] add flag for eip-5656 --- src/standard/config.rs | 11 +++++++++++ src/standard/gasometer/mod.rs | 5 ++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/standard/config.rs b/src/standard/config.rs index 63300bab4..ace618daa 100644 --- a/src/standard/config.rs +++ b/src/standard/config.rs @@ -97,6 +97,8 @@ pub struct Config { pub has_base_fee: bool, /// Has PUSH0 opcode. See [EIP-3855](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3855.md) pub has_push0: bool, + /// Enables MCOPY instruction. See [EIP-5656](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-5656.md) + pub eip_5656_enabled: bool, } impl Config { @@ -150,6 +152,7 @@ impl Config { has_ext_code_hash: false, has_base_fee: false, has_push0: false, + eip_5656_enabled: false, } } @@ -203,6 +206,7 @@ impl Config { has_ext_code_hash: true, has_base_fee: false, has_push0: false, + eip_5656_enabled: false, } } @@ -237,6 +241,7 @@ impl Config { disallow_executable_format, warm_coinbase_address, max_initcode_size, + eip_5656_enabled, } = inputs; // See https://eips.ethereum.org/EIPS/eip-2929 @@ -299,6 +304,7 @@ impl Config { has_ext_code_hash: true, has_base_fee, has_push0, + eip_5656_enabled, } } } @@ -315,6 +321,7 @@ struct DerivedConfigInputs { disallow_executable_format: bool, warm_coinbase_address: bool, max_initcode_size: Option, + eip_5656_enabled: bool, } impl DerivedConfigInputs { @@ -329,6 +336,7 @@ impl DerivedConfigInputs { disallow_executable_format: false, warm_coinbase_address: false, max_initcode_size: None, + eip_5656_enabled: false, } } @@ -343,6 +351,7 @@ impl DerivedConfigInputs { disallow_executable_format: true, warm_coinbase_address: false, max_initcode_size: None, + eip_5656_enabled: false, } } @@ -357,6 +366,7 @@ impl DerivedConfigInputs { disallow_executable_format: true, warm_coinbase_address: false, max_initcode_size: None, + eip_5656_enabled: false, } } @@ -372,6 +382,7 @@ impl DerivedConfigInputs { warm_coinbase_address: true, // 2 * 24576 as per EIP-3860 max_initcode_size: Some(0xC000), + eip_5656_enabled: false, } } } diff --git a/src/standard/gasometer/mod.rs b/src/standard/gasometer/mod.rs index d3d189d24..377d33034 100644 --- a/src/standard/gasometer/mod.rs +++ b/src/standard/gasometer/mod.rs @@ -349,7 +349,10 @@ fn dynamic_opcode_cost( len: U256::from_big_endian(&stack.peek(3)?[..]), } } - Opcode::CALLDATACOPY | Opcode::CODECOPY | Opcode::MCOPY => GasCost::VeryLowCopy { + Opcode::MCOPY if config.eip_5656_enabled => GasCost::VeryLowCopy { + len: U256::from_big_endian(&stack.peek(2)?[..]), + }, + Opcode::CALLDATACOPY | Opcode::CODECOPY => GasCost::VeryLowCopy { len: U256::from_big_endian(&stack.peek(2)?[..]), }, Opcode::EXP => GasCost::Exp { From 450e50e14f0424ee5f35bcc25fc65da1655435ce Mon Sep 17 00:00:00 2001 From: Rodrigo Quelhas <22591718+RomarQ@users.noreply.github.com> Date: Tue, 4 Jun 2024 09:43:47 +0100 Subject: [PATCH 6/6] Update interpreter/src/machine/memory.rs Co-authored-by: Qinxuan Chen --- interpreter/src/machine/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interpreter/src/machine/memory.rs b/interpreter/src/machine/memory.rs index 8016ca10b..04d93ecf2 100644 --- a/interpreter/src/machine/memory.rs +++ b/interpreter/src/machine/memory.rs @@ -314,7 +314,7 @@ mod tests { 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. + // An extra element is added due to resizing. assert_eq!( memory.data(), &[0u8, 0u8, 0u8, 4u8, 0u8, 3u8, 4u8, 0u8].to_vec()