Skip to content

Commit

Permalink
Merge pull request #1312 from topos-protocol/mcopy
Browse files Browse the repository at this point in the history
Implement EIP-5656 (MCOPY)
  • Loading branch information
Nashtare authored Oct 26, 2023
2 parents 44af80f + 530a1d9 commit d4d0868
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 4 deletions.
6 changes: 3 additions & 3 deletions evm/src/cpu/kernel/asm/core/exception.asm
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,9 @@ min_stack_len_for_opcode:
BYTES 0 // 0x59, MSIZE
BYTES 0 // 0x5a, GAS
BYTES 0 // 0x5b, JUMPDEST
%rep 3 // 0x5c-0x5e, invalid
BYTES 0
%endrep
BYTES 0 // 0x5c, invalid
BYTES 0 // 0x5d, invalid
BYTES 3 // 0x5e, MCOPY

%rep 33 // 0x5f-0x7f, PUSH0-PUSH32
BYTES 0
Expand Down
2 changes: 1 addition & 1 deletion evm/src/cpu/kernel/asm/core/syscall.asm
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ global syscall_jumptable:
JUMPTABLE panic // jumpdest is implemented natively
JUMPTABLE panic // 0x5c is an invalid opcode
JUMPTABLE panic // 0x5d is an invalid opcode
JUMPTABLE panic // 0x5e is an invalid opcode
JUMPTABLE sys_mcopy
JUMPTABLE panic // 0x5f is an invalid opcode

// 0x60-0x6f
Expand Down
64 changes: 64 additions & 0 deletions evm/src/cpu/kernel/asm/memory/syscalls.asm
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,67 @@ global sys_returndatacopy:
returndatacopy_empty:
%stack (kexit_info, dest_offset, offset, size) -> (kexit_info)
EXIT_KERNEL

// Same as %wcopy but with special handling in case of overlapping ranges.
global sys_mcopy:
// stack: kexit_info, dest_offset, offset, size
PUSH @GAS_VERYLOW
// stack: Gverylow, kexit_info, dest_offset, offset, size
DUP5 %num_bytes_to_num_words %mul_const(@GAS_COPY) ADD %charge_gas

// stack: kexit_info, dest_offset, offset, size
DUP4
// stack: size, kexit_info, dest_offset, offset, size
ISZERO %jumpi(returndatacopy_empty) // If size is empty, just pop the stack and exit the kernel

%stack (kexit_info, dest_offset, offset, size) -> (dest_offset, size, kexit_info, dest_offset, offset, size)
%add_or_fault
// stack: expanded_num_bytes, kexit_info, dest_offset, offset, size, kexit_info
DUP1 %ensure_reasonable_offset
%update_mem_bytes

// stack: kexit_info, dest_offset, offset, size
DUP3 DUP3 EQ
// stack: dest_offset = offset, kexit_info, dest_offset, offset, size
%jumpi(returndatacopy_empty) // If SRC == DST, just pop the stack and exit the kernel

// stack: kexit_info, dest_offset, offset, size
PUSH @SEGMENT_MAIN_MEMORY
DUP5 DUP5 ADD
// stack: offset + size, segment, kexit_info, dest_offset, offset, size
DUP4 LT
// stack: dest_offset < offset + size, segment, kexit_info, dest_offset, offset, size
DUP5 DUP5 GT
// stack: dest_offset > offset, dest_offset < offset + size, segment, kexit_info, dest_offset, offset, size
AND
// stack: (dest_offset > offset) && (dest_offset < offset + size), segment, kexit_info, dest_offset, offset, size

// If both conditions are satisfied, that means we will get an overlap, in which case we need to process the copy
// in two chunks to prevent overwriting memory data before reading it.
%jumpi(mcopy_with_overlap)

// stack: segment, kexit_info, dest_offset, offset, size
PUSH wcopy_within_bounds
JUMP

mcopy_with_overlap:
// We do have an overlap between the SRC and DST ranges. We will first copy the overlapping segment
// (i.e. end of the copy portion), then copy the remaining (i.e. beginning) portion.

// stack: segment, kexit_info, dest_offset, offset, size
DUP4 DUP4 SUB
// stack: remaining_size = dest_offset - offset, segment, kexit_info, dest_offset, offset, size
DUP1 DUP7
SUB // overlapping_size = size - remaining_size
// stack: overlapping_size, remaining_size, segment, kexit_info, dest_offset, offset, size

// Shift the initial offsets to copy the overlapping segment first.
DUP2 DUP7 ADD
// stack: offset_first_copy, overlapping_size, remaining_size, segment, kexit_info, dest_offset, offset, size
DUP3 DUP7 ADD
// stack: dest_offset_first_copy, offset_first_copy, overlapping_size, remaining_size, segment, kexit_info, dest_offset, offset, size

GET_CONTEXT
%stack (context, dest_offset_first_copy, offset_first_copy, overlapping_size, remaining_size, segment, kexit_info, dest_offset, offset, size) ->
(context, segment, dest_offset_first_copy, context, segment, offset_first_copy, overlapping_size, wcopy_within_bounds, segment, kexit_info, dest_offset, offset, remaining_size)
%jump(memcpy_bytes)
26 changes: 26 additions & 0 deletions evm/src/cpu/kernel/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ impl<'a> Interpreter<'a> {
0x59 => self.run_msize(), // "MSIZE",
0x5a => todo!(), // "GAS",
0x5b => self.run_jumpdest(), // "JUMPDEST",
0x5e => self.run_mcopy(), // "MCOPY",
x if (0x5f..0x80).contains(&x) => self.run_push(x - 0x5f), // "PUSH"
x if (0x80..0x90).contains(&x) => self.run_dup(x - 0x7f), // "DUP"
x if (0x90..0xa0).contains(&x) => self.run_swap(x - 0x8f)?, // "SWAP"
Expand Down Expand Up @@ -1023,6 +1024,31 @@ impl<'a> Interpreter<'a> {
assert!(!self.kernel_mode, "JUMPDEST is not needed in kernel code");
}

fn run_mcopy(&mut self) {
let dest_offset = self.pop().as_usize();
let offset = self.pop().as_usize();
let size = self.pop().as_usize();

let intermediary_memory: Vec<U256> = (0..size)
.map(|i| {
self.generation_state.memory.mload_general(
self.context,
Segment::MainMemory,
offset + i,
)
})
.collect();

for i in 0..size {
self.generation_state.memory.mstore_general(
self.context,
Segment::MainMemory,
dest_offset + i,
intermediary_memory[i],
);
}
}

fn jump_to(&mut self, offset: usize) {
// The JUMPDEST rule is not enforced in kernel mode.
if !self.kernel_mode && self.jumpdests.binary_search(&offset).is_err() {
Expand Down
1 change: 1 addition & 0 deletions evm/src/witness/transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ fn decode(registers: RegistersState, opcode: u8) -> Result<Operation, ProgramErr
(0x59, _) => Ok(Operation::Syscall(opcode, 0, true)), // MSIZE
(0x5a, _) => Ok(Operation::Syscall(opcode, 0, true)), // GAS
(0x5b, _) => Ok(Operation::Jumpdest),
(0x5e, _) => Ok(Operation::Syscall(opcode, 3, false)), // MCOPY
(0x5f..=0x7f, _) => Ok(Operation::Push(opcode - 0x5f)),
(0x80..=0x8f, _) => Ok(Operation::Dup(opcode & 0xf)),
(0x90..=0x9f, _) => Ok(Operation::Swap(opcode & 0xf)),
Expand Down

0 comments on commit d4d0868

Please sign in to comment.