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: support memory copying instruction (EIP-5656) #279

Merged
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
1 change: 1 addition & 0 deletions interpreter/src/etable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ impl<S, H, Tr> Etable<S, H, Tr> {
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
19 changes: 18 additions & 1 deletion interpreter/src/eval/misc.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use core::cmp::min;
use core::cmp::{max, min};

use primitive_types::{H256, U256};

Expand Down Expand Up @@ -99,6 +99,23 @@ pub fn mload<S, Tr>(state: &mut Machine<S>) -> Control<Tr> {
Control::Continue
}

/// Support for EIP-5656: MCOPY instruction.
#[inline]
pub fn mcopy<S, Tr>(state: &mut Machine<S>) -> Control<Tr> {
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);
state.memory.copy(dst, src, len);
Control::Continue
}

#[inline]
pub fn mstore<S, Tr>(state: &mut Machine<S>) -> Control<Tr> {
pop_u256!(state, index);
Expand Down
9 changes: 9 additions & 0 deletions interpreter/src/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,15 @@ pub fn eval_jumpdest<S, H, Tr>(
Control::Continue
}

pub fn eval_mcopy<S, H, Tr>(
machine: &mut Machine<S>,
_handle: &mut H,
_opcode: Opcode,
_position: usize,
) -> Control<Tr> {
self::misc::mcopy(machine)
}

macro_rules! eval_push {
($($num:expr),*) => {
$(paste::paste! {
Expand Down
58 changes: 56 additions & 2 deletions interpreter/src/machine/memory.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use alloc::{vec, vec::Vec};
use core::{
cmp::min,
cmp::{max, min},
mem,
ops::{BitAnd, Not, Range},
};
Expand Down Expand Up @@ -222,6 +222,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 @@ -233,7 +242,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 @@ -266,4 +275,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 resizing.
assert_eq!(
memory.data(),
&[0u8, 0u8, 0u8, 4u8, 0u8, 3u8, 4u8, 0u8].to_vec()
);
}
}
2 changes: 2 additions & 0 deletions interpreter/src/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ impl Opcode {

/// `JUMPDEST`
pub const JUMPDEST: Opcode = Opcode(0x5b);
/// `MCOPY`
pub const MCOPY: Opcode = Opcode(0x5e);

/// `PUSHn`
pub const PUSH0: Opcode = Opcode(0x5f);
Expand Down
11 changes: 11 additions & 0 deletions src/standard/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ pub struct Config {
pub has_push0: bool,
/// Enables transient storage. See [EIP-1153](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1153.md)
pub eip_1153_enabled: 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 {
Expand Down Expand Up @@ -153,6 +155,7 @@ impl Config {
has_base_fee: false,
has_push0: false,
eip_1153_enabled: false,
eip_5656_enabled: false,
}
}

Expand Down Expand Up @@ -207,6 +210,7 @@ impl Config {
has_base_fee: false,
has_push0: false,
eip_1153_enabled: false,
eip_5656_enabled: false,
}
}

Expand Down Expand Up @@ -242,6 +246,7 @@ impl Config {
warm_coinbase_address,
max_initcode_size,
eip_1153_enabled,
eip_5656_enabled,
} = inputs;

// See https://eips.ethereum.org/EIPS/eip-2929
Expand Down Expand Up @@ -305,6 +310,7 @@ impl Config {
has_base_fee,
has_push0,
eip_1153_enabled,
eip_5656_enabled,
}
}
}
Expand All @@ -322,6 +328,7 @@ struct DerivedConfigInputs {
warm_coinbase_address: bool,
max_initcode_size: Option<usize>,
eip_1153_enabled: bool,
eip_5656_enabled: bool,
}

impl DerivedConfigInputs {
Expand All @@ -337,6 +344,7 @@ impl DerivedConfigInputs {
warm_coinbase_address: false,
max_initcode_size: None,
eip_1153_enabled: false,
eip_5656_enabled: false,
}
}

Expand All @@ -352,6 +360,7 @@ impl DerivedConfigInputs {
warm_coinbase_address: false,
max_initcode_size: None,
eip_1153_enabled: false,
eip_5656_enabled: false,
}
}

Expand All @@ -367,6 +376,7 @@ impl DerivedConfigInputs {
warm_coinbase_address: false,
max_initcode_size: None,
eip_1153_enabled: false,
eip_5656_enabled: false,
}
}

Expand All @@ -383,6 +393,7 @@ impl DerivedConfigInputs {
// 2 * 24576 as per EIP-3860
max_initcode_size: Some(0xC000),
eip_1153_enabled: false,
eip_5656_enabled: false,
}
}
}
13 changes: 13 additions & 0 deletions src/standard/gasometer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,9 @@ fn dynamic_opcode_cost<H: RuntimeBackend>(
len: U256::from_big_endian(&stack.peek(3)?[..]),
}
}
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)?[..]),
},
Expand Down Expand Up @@ -459,6 +462,16 @@ fn dynamic_opcode_cost<H: RuntimeBackend>(
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