Merge pull request #1314 from topos-protocol/refactor_wcopy

Refactor `%wcopy` and unify {ext}codecopy related code
This commit is contained in:
Robin Salen 2023-10-27 11:47:42 -04:00 committed by GitHub
commit 0258ad4a3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 145 additions and 208 deletions

View File

@ -80,110 +80,6 @@ global extcodesize:
// stack: extcodesize(address), retdest
SWAP1 JUMP
%macro extcodecopy
// stack: address, dest_offset, offset, size
%stack (address, dest_offset, offset, size) -> (address, dest_offset, offset, size, %%after)
%jump(extcodecopy)
%%after:
%endmacro
// Pre stack: kexit_info, address, dest_offset, offset, size
// Post stack: (empty)
global sys_extcodecopy:
%stack (kexit_info, address, dest_offset, offset, size)
-> (address, dest_offset, offset, size, kexit_info)
%u256_to_addr DUP1 %insert_accessed_addresses
// stack: cold_access, address, dest_offset, offset, size, kexit_info
PUSH @GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS
MUL
PUSH @GAS_WARMACCESS
ADD
// stack: Gaccess, address, dest_offset, offset, size, kexit_info
DUP5
// stack: size, Gaccess, address, dest_offset, offset, size, kexit_info
ISZERO %jumpi(sys_extcodecopy_empty)
// stack: Gaccess, address, dest_offset, offset, size, kexit_info
DUP5 %num_bytes_to_num_words %mul_const(@GAS_COPY) ADD
%stack (gas, address, dest_offset, offset, size, kexit_info) -> (gas, kexit_info, address, dest_offset, offset, size)
%charge_gas
%stack (kexit_info, address, dest_offset, offset, size) -> (dest_offset, size, kexit_info, address, dest_offset, offset, size)
%add_or_fault
// stack: expanded_num_bytes, kexit_info, address, dest_offset, offset, size
DUP1 %ensure_reasonable_offset
%update_mem_bytes
%stack (kexit_info, address, dest_offset, offset, size) -> (address, dest_offset, offset, size, kexit_info)
%extcodecopy
// stack: kexit_info
EXIT_KERNEL
sys_extcodecopy_empty:
%stack (Gaccess, address, dest_offset, offset, size, kexit_info) -> (Gaccess, kexit_info)
%charge_gas
EXIT_KERNEL
// Pre stack: address, dest_offset, offset, size, retdest
// Post stack: (empty)
global extcodecopy:
// stack: address, dest_offset, offset, size, retdest
%stack (address, dest_offset, offset, size, retdest)
-> (address, 0, @SEGMENT_KERNEL_ACCOUNT_CODE, extcodecopy_contd, size, offset, dest_offset, retdest)
%jump(load_code)
extcodecopy_contd:
// stack: code_size, size, offset, dest_offset, retdest
DUP1 DUP4
// stack: offset, code_size, code_size, size, offset, dest_offset, retdest
GT %jumpi(extcodecopy_large_offset)
// stack: code_size, size, offset, dest_offset, retdest
DUP3 DUP3 ADD
// stack: offset + size, code_size, size, offset, dest_offset, retdest
DUP2 GT %jumpi(extcodecopy_within_bounds)
// stack: code_size, size, offset, dest_offset, retdest
DUP3 DUP3 ADD
// stack: offset + size, code_size, size, offset, dest_offset, retdest
SUB
// stack: extra_size = offset + size - code_size, size, offset, dest_offset, retdest
DUP1 DUP3 SUB
// stack: copy_size = size - extra_size, extra_size, size, offset, dest_offset, retdest
// Compute the new dest_offset after actual copies, at which we will start padding with zeroes.
DUP1 DUP6 ADD
// stack: new_dest_offset, copy_size, extra_size, size, offset, dest_offset, retdest
GET_CONTEXT
%stack (context, new_dest_offset, copy_size, extra_size, size, offset, dest_offset, retdest) ->
(context, @SEGMENT_MAIN_MEMORY, dest_offset, 0, @SEGMENT_KERNEL_ACCOUNT_CODE, offset, copy_size, extcodecopy_end, new_dest_offset, extra_size, retdest)
%jump(memcpy_bytes)
extcodecopy_within_bounds:
// stack: code_size, size, offset, dest_offset, retdest
GET_CONTEXT
%stack (context, code_size, size, offset, dest_offset, retdest) ->
(context, @SEGMENT_MAIN_MEMORY, dest_offset, 0, @SEGMENT_KERNEL_ACCOUNT_CODE, offset, size, retdest)
%jump(memcpy_bytes)
// Same as extcodecopy_large_offset, but without `offset` in the stack.
extcodecopy_end:
// stack: dest_offset, size, retdest
GET_CONTEXT
%stack (context, dest_offset, size, retdest) ->
(context, @SEGMENT_MAIN_MEMORY, dest_offset, size, retdest)
%jump(memset)
extcodecopy_large_offset:
// offset is larger than the code size. So we just have to write zeros.
// stack: code_size, size, offset, dest_offset, retdest
GET_CONTEXT
%stack (context, code_size, size, offset, dest_offset, retdest) -> (context, @SEGMENT_MAIN_MEMORY, dest_offset, size, retdest)
%jump(memset)
// Loads the code at `address` into memory, at the given context and segment, starting at offset 0.
// Checks that the hash of the loaded code corresponds to the `codehash` in the state trie.
// Pre stack: address, ctx, segment, retdest

View File

@ -42,12 +42,6 @@ global memcpy:
// Continue the loop.
%jump(memcpy)
memcpy_finish:
// stack: DST, SRC, count, retdest
%pop7
// stack: retdest
JUMP
%macro memcpy
%stack (dst: 3, src: 3, count) -> (dst, src, count, %%after)
%jump(memcpy)
@ -63,7 +57,7 @@ global memcpy_bytes:
// stack: count, DST, SRC, count, retdest
ISZERO
// stack: count == 0, DST, SRC, count, retdest
%jumpi(memcpy_bytes_empty)
%jumpi(memcpy_finish)
// stack: DST, SRC, count, retdest
@ -126,12 +120,8 @@ memcpy_bytes_finish:
MSTORE_32BYTES
// stack: DST, SRC, count, retdest
%pop7
// stack: retdest
JUMP
memcpy_bytes_empty:
// stack: DST, SRC, 0, retdest
memcpy_finish:
// stack: DST, SRC, count, retdest
%pop7
// stack: retdest
JUMP

View File

@ -70,15 +70,10 @@ calldataload_large_offset:
%stack (kexit_info, i) -> (kexit_info, 0)
EXIT_KERNEL
// Macro for {CALLDATA,CODE,RETURNDATA}COPY (W_copy in Yellow Paper).
// Macro for {CALLDATA, RETURNDATA}COPY (W_copy in Yellow Paper).
%macro wcopy(segment, context_metadata_size)
// stack: kexit_info, dest_offset, offset, size
PUSH @GAS_VERYLOW
DUP5
// stack: size, Gverylow, kexit_info, dest_offset, offset, size
ISZERO %jumpi(wcopy_empty)
// stack: Gverylow, kexit_info, dest_offset, offset, size
DUP5 %num_bytes_to_num_words %mul_const(@GAS_COPY) ADD %charge_gas
%wcopy_charge_gas
%stack (kexit_info, dest_offset, offset, size) -> (dest_offset, size, kexit_info, dest_offset, offset, size)
%add_or_fault
@ -92,54 +87,44 @@ calldataload_large_offset:
// stack: offset, total_size, kexit_info, dest_offset, offset, size
GT %jumpi(wcopy_large_offset)
PUSH $segment
%mload_context_metadata($context_metadata_size)
// stack: total_size, segment, kexit_info, dest_offset, offset, size
DUP6 DUP6 ADD
// stack: offset + size, total_size, segment, kexit_info, dest_offset, offset, size
LT %jumpi(wcopy_within_bounds)
%mload_context_metadata($context_metadata_size)
// stack: total_size, segment, kexit_info, dest_offset, offset, size
DUP6 DUP6 ADD
// stack: offset + size, total_size, segment, kexit_info, dest_offset, offset, size
SUB // extra_size = offset + size - total_size
// stack: extra_size, segment, kexit_info, dest_offset, offset, size
DUP1 DUP7 SUB
// stack: copy_size = size - extra_size, extra_size, segment, kexit_info, dest_offset, offset, size
// Compute the new dest_offset after actual copies, at which we will start padding with zeroes.
DUP1 DUP6 ADD
// stack: new_dest_offset, copy_size, extra_size, segment, kexit_info, dest_offset, offset, size
// stack: kexit_info, dest_offset, offset, size
GET_CONTEXT
%stack (context, new_dest_offset, copy_size, extra_size, segment, kexit_info, dest_offset, offset, size) ->
(context, @SEGMENT_MAIN_MEMORY, dest_offset, context, segment, offset, copy_size, wcopy_over_range, new_dest_offset, extra_size, kexit_info)
%jump(memcpy_bytes)
PUSH $segment
// stack: segment, context, kexit_info, dest_offset, offset, size
%jump(wcopy_within_bounds)
%endmacro
%macro wcopy_charge_gas
// stack: kexit_info, dest_offset, offset, size
PUSH @GAS_VERYLOW
DUP5
// stack: size, Gverylow, kexit_info, dest_offset, offset, size
ISZERO %jumpi(wcopy_empty)
// stack: Gverylow, kexit_info, dest_offset, offset, size
DUP5 %num_bytes_to_num_words %mul_const(@GAS_COPY) ADD %charge_gas
%endmacro
codecopy_within_bounds:
// stack: total_size, segment, src_ctx, kexit_info, dest_offset, offset, size
POP
wcopy_within_bounds:
// stack: segment, kexit_info, dest_offset, offset, size
// stack: segment, src_ctx, kexit_info, dest_offset, offset, size
GET_CONTEXT
%stack (context, segment, kexit_info, dest_offset, offset, size) ->
(context, @SEGMENT_MAIN_MEMORY, dest_offset, context, segment, offset, size, wcopy_after, kexit_info)
%stack (context, segment, src_ctx, kexit_info, dest_offset, offset, size) ->
(context, @SEGMENT_MAIN_MEMORY, dest_offset, src_ctx, segment, offset, size, wcopy_after, kexit_info)
%jump(memcpy_bytes)
// Same as wcopy_large_offset, but without `offset` in the stack.
wcopy_over_range:
// stack: dest_offset, size, kexit_info
GET_CONTEXT
%stack (context, dest_offset, size, kexit_info) ->
(context, @SEGMENT_MAIN_MEMORY, dest_offset, size, wcopy_after, kexit_info)
%jump(memset)
wcopy_empty:
// stack: Gverylow, kexit_info, dest_offset, offset, size
%charge_gas
%stack (kexit_info, dest_offset, offset, size) -> (kexit_info)
EXIT_KERNEL
codecopy_large_offset:
// stack: total_size, src_ctx, kexit_info, dest_offset, offset, size
%pop2
wcopy_large_offset:
// offset is larger than the size of the {CALLDATA,CODE,RETURNDATA}. So we just have to write zeros.
// stack: kexit_info, dest_offset, offset, size
@ -152,64 +137,107 @@ wcopy_after:
// stack: kexit_info
EXIT_KERNEL
// Pre stack: kexit_info, dest_offset, offset, size
// Post stack: (empty)
global sys_calldatacopy:
%wcopy(@SEGMENT_CALLDATA, @CTX_METADATA_CALLDATA_SIZE)
global sys_codecopy:
%wcopy(@SEGMENT_CODE, @CTX_METADATA_CODE_SIZE)
// Same as %wcopy but with overflow checks.
// Pre stack: kexit_info, dest_offset, offset, size
// Post stack: (empty)
global sys_returndatacopy:
DUP4 DUP4 %add_or_fault // Overflow check
%mload_context_metadata(@CTX_METADATA_RETURNDATA_SIZE) LT %jumpi(fault_exception) // Data len check
%wcopy(@SEGMENT_RETURNDATA, @CTX_METADATA_RETURNDATA_SIZE)
// Pre stack: kexit_info, dest_offset, offset, size
// Post stack: (empty)
global sys_codecopy:
// 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
%wcopy_charge_gas
%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, kexit_info
DUP4 DUP4 %add_or_fault // Overflow check
%mload_context_metadata(@CTX_METADATA_RETURNDATA_SIZE) LT %jumpi(fault_exception) // Data len check
// stack: kexit_info, dest_offset, offset, size
DUP4
// stack: size, kexit_info, dest_offset, offset, size
ISZERO %jumpi(returndatacopy_empty)
%mload_context_metadata(@CTX_METADATA_RETURNDATA_SIZE)
// stack: total_size, kexit_info, dest_offset, offset, size
DUP4
// stack: offset, total_size, kexit_info, dest_offset, offset, size
GT %jumpi(wcopy_large_offset)
PUSH @SEGMENT_RETURNDATA
%mload_context_metadata(@CTX_METADATA_RETURNDATA_SIZE)
// stack: total_size, returndata_segment, kexit_info, dest_offset, offset, size
DUP6 DUP6 ADD
// stack: offset + size, total_size, returndata_segment, kexit_info, dest_offset, offset, size
LT %jumpi(wcopy_within_bounds)
%mload_context_metadata(@CTX_METADATA_RETURNDATA_SIZE)
// stack: total_size, returndata_segment, kexit_info, dest_offset, offset, size
DUP6 DUP6 ADD
// stack: offset + size, total_size, returndata_segment, kexit_info, dest_offset, offset, size
SUB // extra_size = offset + size - total_size
// stack: extra_size, returndata_segment, kexit_info, dest_offset, offset, size
DUP1 DUP7 SUB
// stack: copy_size = size - extra_size, extra_size, returndata_segment, kexit_info, dest_offset, offset, size
// Compute the new dest_offset after actual copies, at which we will start padding with zeroes.
DUP1 DUP6 ADD
// stack: new_dest_offset, copy_size, extra_size, returndata_segment, kexit_info, dest_offset, offset, size
GET_CONTEXT
%stack (context, new_dest_offset, copy_size, extra_size, returndata_segment, kexit_info, dest_offset, offset, size) ->
(context, @SEGMENT_MAIN_MEMORY, dest_offset, context, returndata_segment, offset, copy_size, wcopy_over_range, new_dest_offset, extra_size, kexit_info)
%jump(memcpy_bytes)
%mload_context_metadata(@CTX_METADATA_CODE_SIZE)
// stack: code_size, ctx, kexit_info, dest_offset, offset, size
%codecopy_after_checks(@SEGMENT_CODE)
returndatacopy_empty:
%stack (kexit_info, dest_offset, offset, size) -> (kexit_info)
// Pre stack: kexit_info, address, dest_offset, offset, size
// Post stack: (empty)
global sys_extcodecopy:
%stack (kexit_info, address, dest_offset, offset, size)
-> (address, dest_offset, offset, size, kexit_info)
%u256_to_addr DUP1 %insert_accessed_addresses
// stack: cold_access, address, dest_offset, offset, size, kexit_info
PUSH @GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS
MUL
PUSH @GAS_WARMACCESS
ADD
// stack: Gaccess, address, dest_offset, offset, size, kexit_info
DUP5
// stack: size, Gaccess, address, dest_offset, offset, size, kexit_info
ISZERO %jumpi(sys_extcodecopy_empty)
// stack: Gaccess, address, dest_offset, offset, size, kexit_info
DUP5 %num_bytes_to_num_words %mul_const(@GAS_COPY) ADD
%stack (gas, address, dest_offset, offset, size, kexit_info) -> (gas, kexit_info, address, dest_offset, offset, size)
%charge_gas
%stack (kexit_info, address, dest_offset, offset, size) -> (dest_offset, size, kexit_info, address, dest_offset, offset, size)
%add_or_fault
// stack: expanded_num_bytes, kexit_info, address, dest_offset, offset, size
DUP1 %ensure_reasonable_offset
%update_mem_bytes
%stack (kexit_info, address, dest_offset, offset, size) ->
(address, 0, @SEGMENT_KERNEL_ACCOUNT_CODE, extcodecopy_contd, 0, kexit_info, dest_offset, offset, size)
%jump(load_code)
sys_extcodecopy_empty:
%stack (Gaccess, address, dest_offset, offset, size, kexit_info) -> (Gaccess, kexit_info)
%charge_gas
EXIT_KERNEL
extcodecopy_contd:
// stack: code_size, src_ctx, kexit_info, dest_offset, offset, size
%codecopy_after_checks(@SEGMENT_KERNEL_ACCOUNT_CODE)
// The internal logic is similar to wcopy, but handles range overflow differently.
// It is used for both CODECOPY and EXTCODECOPY.
%macro codecopy_after_checks(segment)
// stack: total_size, src_ctx, kexit_info, dest_offset, offset, size
DUP1 DUP6
// stack: offset, total_size, total_size, src_ctx, kexit_info, dest_offset, offset, size
GT %jumpi(codecopy_large_offset)
PUSH $segment SWAP1
// stack: total_size, segment, src_ctx, kexit_info, dest_offset, offset, size
DUP1 DUP8 DUP8 ADD
// stack: offset + size, total_size, total_size, segment, src_ctx, kexit_info, dest_offset, offset, size
LT %jumpi(codecopy_within_bounds)
// stack: total_size, segment, src_ctx, kexit_info, dest_offset, offset, size
DUP7 DUP7 ADD
// stack: offset + size, total_size, segment, src_ctx, kexit_info, dest_offset, offset, size
SUB // extra_size = offset + size - total_size
// stack: extra_size, segment, src_ctx, kexit_info, dest_offset, offset, size
DUP1 DUP8 SUB
// stack: copy_size = size - extra_size, extra_size, segment, src_ctx, kexit_info, dest_offset, offset, size
// Compute the new dest_offset after actual copies, at which we will start padding with zeroes.
DUP1 DUP7 ADD
// stack: new_dest_offset, copy_size, extra_size, segment, src_ctx, kexit_info, dest_offset, offset, size
GET_CONTEXT
%stack (context, new_dest_offset, copy_size, extra_size, segment, src_ctx, kexit_info, dest_offset, offset, size) ->
(context, @SEGMENT_MAIN_MEMORY, dest_offset, src_ctx, segment, offset, copy_size, wcopy_large_offset, kexit_info, new_dest_offset, offset, extra_size)
%jump(memcpy_bytes)
%endmacro

View File

@ -426,7 +426,7 @@ impl<'a> Interpreter<'a> {
0xf6 => self.run_get_context(), // "GET_CONTEXT",
0xf7 => self.run_set_context(), // "SET_CONTEXT",
0xf8 => self.run_mload_32bytes(), // "MLOAD_32BYTES",
0xf9 => todo!(), // "EXIT_KERNEL",
0xf9 => self.run_exit_kernel(), // "EXIT_KERNEL",
0xfa => todo!(), // "STATICCALL",
0xfb => self.run_mload_general(), // "MLOAD_GENERAL",
0xfc => self.run_mstore_general(), // "MSTORE_GENERAL",
@ -1126,6 +1126,24 @@ impl<'a> Interpreter<'a> {
}
}
fn run_exit_kernel(&mut self) {
let kexit_info = self.pop();
let kexit_info_u64 = kexit_info.0[0];
let program_counter = kexit_info_u64 as u32 as usize;
let is_kernel_mode_val = (kexit_info_u64 >> 32) as u32;
assert!(is_kernel_mode_val == 0 || is_kernel_mode_val == 1);
let is_kernel_mode = is_kernel_mode_val != 0;
let gas_used_val = kexit_info.0[3];
if TryInto::<u64>::try_into(gas_used_val).is_err() {
panic!("Gas overflow");
}
self.generation_state.registers.program_counter = program_counter;
self.generation_state.registers.is_kernel = is_kernel_mode;
self.generation_state.registers.gas_used = gas_used_val;
}
pub(crate) fn stack_len(&self) -> usize {
self.generation_state.registers.stack_len
}

View File

@ -7,6 +7,7 @@ use keccak_hash::keccak;
use rand::{thread_rng, Rng};
use crate::cpu::kernel::aggregator::KERNEL;
use crate::cpu::kernel::constants::context_metadata::ContextMetadata::GasLimit;
use crate::cpu::kernel::constants::global_metadata::GlobalMetadata;
use crate::cpu::kernel::interpreter::Interpreter;
use crate::cpu::kernel::tests::mpt::nibbles_64;
@ -142,7 +143,11 @@ fn test_extcodecopy() -> Result<()> {
// Prepare the interpreter by inserting the account in the state trie.
prepare_interpreter(&mut interpreter, address, &account)?;
let extcodecopy = KERNEL.global_labels["extcodecopy"];
interpreter.generation_state.memory.contexts[interpreter.context].segments
[Segment::ContextMetadata as usize]
.set(GasLimit as usize, U256::from(1000000000000u64) << 192);
let extcodecopy = KERNEL.global_labels["sys_extcodecopy"];
// Put random data in main memory and the `KernelAccountCode` segment for realism.
let mut rng = thread_rng();
@ -164,11 +169,11 @@ fn test_extcodecopy() -> Result<()> {
interpreter.generation_state.registers.program_counter = extcodecopy;
interpreter.pop();
assert!(interpreter.stack().is_empty());
interpreter.push(0xDEADBEEFu32.into());
interpreter.push(size.into());
interpreter.push(offset.into());
interpreter.push(dest_offset.into());
interpreter.push(U256::from_big_endian(address.as_bytes()));
interpreter.push(0xDEADBEEFu32.into()); // kexit_info
interpreter.generation_state.inputs.contract_code =
HashMap::from([(keccak(&code), code.clone())]);
interpreter.run()?;