Merge pull request #903 from mir-protocol/misc_evm_fixes

Misc EVM fixes
This commit is contained in:
Daniel Lubarov 2023-03-06 22:51:08 -08:00 committed by GitHub
commit eea8ab6260
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 285 additions and 244 deletions

View File

@ -12,6 +12,7 @@ pub static KERNEL: Lazy<Kernel> = Lazy::new(combined_kernel);
pub(crate) fn combined_kernel() -> Kernel {
let files = vec![
include_str!("asm/core/bootloader.asm"),
include_str!("asm/core/call.asm"),
include_str!("asm/core/create.asm"),
include_str!("asm/core/create_addresses.asm"),
include_str!("asm/core/intrinsic_gas.asm"),

View File

@ -24,12 +24,6 @@ global extcodehash:
%eq_const(@EMPTY_STRING_HASH)
%endmacro
%macro codesize
// stack: (empty)
%address
%extcodesize
%endmacro
%macro extcodesize
%stack (address) -> (address, 0, @SEGMENT_KERNEL_ACCOUNT_CODE, %%after)
%jump(load_code)
@ -82,91 +76,92 @@ global extcodecopy:
%jump(load_code)
extcodecopy_contd:
// stack: code_length, size, offset, dest_offset, retdest
// stack: code_size, size, offset, dest_offset, retdest
SWAP1
// stack: size, code_length, offset, dest_offset, retdest
// stack: size, code_size, offset, dest_offset, retdest
PUSH 0
// Loop copying the `code[offset]` to `memory[dest_offset]` until `i==size`.
// Each iteration increments `offset, dest_offset, i`.
// TODO: Consider implementing this with memcpy.
extcodecopy_loop:
// stack: i, size, code_length, offset, dest_offset, retdest
// stack: i, size, code_size, offset, dest_offset, retdest
DUP2 DUP2 EQ
// stack: i == size, i, size, code_length, offset, dest_offset, retdest
// stack: i == size, i, size, code_size, offset, dest_offset, retdest
%jumpi(extcodecopy_end)
%stack (i, size, code_length, offset, dest_offset, retdest)
-> (offset, code_length, offset, code_length, dest_offset, i, size, retdest)
%stack (i, size, code_size, offset, dest_offset, retdest)
-> (offset, code_size, offset, code_size, dest_offset, i, size, retdest)
LT
// stack: offset < code_length, offset, code_length, dest_offset, i, size, retdest
// stack: offset < code_size, offset, code_size, dest_offset, i, size, retdest
DUP2
// stack: offset, offset < code_length, offset, code_length, dest_offset, i, size, retdest
// stack: offset, offset < code_size, offset, code_size, dest_offset, i, size, retdest
%mload_current(@SEGMENT_KERNEL_ACCOUNT_CODE)
// stack: opcode, offset < code_length, offset, code_length, dest_offset, i, size, retdest
%stack (opcode, offset_lt_code_length, offset, code_length, dest_offset, i, size, retdest)
-> (offset_lt_code_length, 0, opcode, offset, code_length, dest_offset, i, size, retdest)
// If `offset >= code_length`, use `opcode=0`. Necessary since `SEGMENT_KERNEL_ACCOUNT_CODE` might be clobbered from previous calls.
// stack: opcode, offset < code_size, offset, code_size, dest_offset, i, size, retdest
%stack (opcode, offset_lt_code_size, offset, code_size, dest_offset, i, size, retdest)
-> (offset_lt_code_size, 0, opcode, offset, code_size, dest_offset, i, size, retdest)
// If `offset >= code_size`, use `opcode=0`. Necessary since `SEGMENT_KERNEL_ACCOUNT_CODE` might be clobbered from previous calls.
%select_bool
// stack: opcode, offset, code_length, dest_offset, i, size, retdest
// stack: opcode, offset, code_size, dest_offset, i, size, retdest
DUP4
// stack: dest_offset, opcode, offset, code_length, dest_offset, i, size, retdest
// stack: dest_offset, opcode, offset, code_size, dest_offset, i, size, retdest
%mstore_main
// stack: offset, code_length, dest_offset, i, size, retdest
// stack: offset, code_size, dest_offset, i, size, retdest
%increment
// stack: offset+1, code_length, dest_offset, i, size, retdest
// stack: offset+1, code_size, dest_offset, i, size, retdest
SWAP2
// stack: dest_offset, code_length, offset+1, i, size, retdest
// stack: dest_offset, code_size, offset+1, i, size, retdest
%increment
// stack: dest_offset+1, code_length, offset+1, i, size, retdest
// stack: dest_offset+1, code_size, offset+1, i, size, retdest
SWAP3
// stack: i, code_length, offset+1, dest_offset+1, size, retdest
// stack: i, code_size, offset+1, dest_offset+1, size, retdest
%increment
// stack: i+1, code_length, offset+1, dest_offset+1, size, retdest
%stack (i, code_length, offset, dest_offset, size, retdest) -> (i, size, code_length, offset, dest_offset, retdest)
// stack: i+1, code_size, offset+1, dest_offset+1, size, retdest
%stack (i, code_size, offset, dest_offset, size, retdest) -> (i, size, code_size, offset, dest_offset, retdest)
%jump(extcodecopy_loop)
extcodecopy_end:
%stack (i, size, code_length, offset, dest_offset, retdest) -> (retdest)
%stack (i, size, code_size, offset, dest_offset, retdest) -> (retdest)
JUMP
// 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
// Post stack: code_len
// Post stack: code_size
global load_code:
%stack (address, ctx, segment, retdest) -> (extcodehash, address, load_code_ctd, ctx, segment, retdest)
JUMP
load_code_ctd:
// stack: codehash, ctx, segment, retdest
PROVER_INPUT(account_code::length)
// stack: code_length, codehash, ctx, segment, retdest
// stack: code_size, codehash, ctx, segment, retdest
PUSH 0
// Loop non-deterministically querying `code[i]` and storing it in `SEGMENT_KERNEL_ACCOUNT_CODE` at offset `i`, until `i==code_length`.
// Loop non-deterministically querying `code[i]` and storing it in `SEGMENT_KERNEL_ACCOUNT_CODE`
// at offset `i`, until `i==code_size`.
load_code_loop:
// stack: i, code_length, codehash, ctx, segment, retdest
// stack: i, code_size, codehash, ctx, segment, retdest
DUP2 DUP2 EQ
// stack: i == code_length, i, code_length, codehash, ctx, segment, retdest
// stack: i == code_size, i, code_size, codehash, ctx, segment, retdest
%jumpi(load_code_check)
PROVER_INPUT(account_code::get)
// stack: opcode, i, code_length, codehash, ctx, segment, retdest
// stack: opcode, i, code_size, codehash, ctx, segment, retdest
DUP2
// stack: i, opcode, i, code_length, codehash, ctx, segment, retdest
// stack: i, opcode, i, code_size, codehash, ctx, segment, retdest
DUP7 // segment
DUP7 // context
MSTORE_GENERAL
// stack: i, code_length, codehash, ctx, segment, retdest
// stack: i, code_size, codehash, ctx, segment, retdest
%increment
// stack: i+1, code_length, codehash, ctx, segment, retdest
// stack: i+1, code_size, codehash, ctx, segment, retdest
%jump(load_code_loop)
// Check that the hash of the loaded code equals `codehash`.
load_code_check:
// stack: i, code_length, codehash, ctx, segment, retdest
%stack (i, code_length, codehash, ctx, segment, retdest)
-> (ctx, segment, 0, code_length, codehash, retdest, code_length)
// stack: i, code_size, codehash, ctx, segment, retdest
%stack (i, code_size, codehash, ctx, segment, retdest)
-> (ctx, segment, 0, code_size, codehash, retdest, code_size)
KECCAK_GENERAL
// stack: shouldbecodehash, codehash, retdest, code_length
// stack: shouldbecodehash, codehash, retdest, code_size
%assert_eq
JUMP

View File

@ -1,115 +1,178 @@
// Handlers for call-like operations, namely CALL, CALLCODE, STATICCALL and DELEGATECALL.
// TODO: Take kexit_info
// Creates a new sub context and executes the code of the given account.
global call:
global sys_call:
// stack: kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size
%address
%stack (self, gas, address, value)
// These are (static, should_transfer_value, value, sender, address, code_addr, gas)
-> (0, 1, value, self, address, address, gas)
%jump(call_common)
%create_context
// stack: new_ctx, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size
// Each line in the block below does not change the stack.
DUP4 %set_new_ctx_addr
%address %set_new_ctx_caller
DUP5 %set_new_ctx_value
DUP5 DUP5 %address %transfer_eth
%set_new_ctx_parent_ctx
%set_new_ctx_parent_pc(after_call_instruction)
// TODO: Copy memory[args_offset..args_offset + args_size] CALLDATA
// TODO: Set child gas
// TODO: Populate code and codesize field.
// stack: new_ctx, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size
%stack (new_ctx, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size)
-> (new_ctx, kexit_info, ret_offset, ret_size)
%enter_new_ctx
// Creates a new sub context as if calling itself, but with the code of the
// given account. In particular the storage remains the same.
global call_code:
// stack: gas, address, value, args_offset, args_size, ret_offset, ret_size, retdest
%address
%stack (self, gas, address, value)
// These are (static, should_transfer_value, value, sender, address, code_addr, gas)
-> (0, 1, value, self, self, address, gas)
%jump(call_common)
global sys_callcode:
// stack: kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size
%create_context
// stack: new_ctx, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size
// Each line in the block below does not change the stack.
%address %set_new_ctx_addr
%address %set_new_ctx_caller
DUP5 %set_new_ctx_value
DUP5 DUP5 %address %transfer_eth
%set_new_ctx_parent_ctx
%set_new_ctx_parent_pc(after_call_instruction)
// stack: new_ctx, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size
%stack (new_ctx, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size)
-> (new_ctx, kexit_info, ret_offset, ret_size)
%enter_new_ctx
// Creates a new sub context and executes the code of the given account.
// Equivalent to CALL, except that it does not allow any state modifying
// instructions or sending ETH in the sub context. The disallowed instructions
// are CREATE, CREATE2, LOG0, LOG1, LOG2, LOG3, LOG4, SSTORE, SELFDESTRUCT and
// CALL if the value sent is not 0.
global static_all:
// stack: gas, address, args_offset, args_size, ret_offset, ret_size, retdest
%address
%stack (self, gas, address)
// These are (static, should_transfer_value, value, sender, address, code_addr, gas)
-> (1, 0, 0, self, address, address, gas)
%jump(call_common)
global sys_staticcall:
// stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size
%create_context
// stack: new_ctx, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size
// Each line in the block below does not change the stack.
%set_static_true
DUP4 %set_new_ctx_addr
%address %set_new_ctx_caller
PUSH 0 %set_new_ctx_value
%set_new_ctx_parent_ctx
%set_new_ctx_parent_pc(after_call_instruction)
%stack (new_ctx, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size)
-> (new_ctx, kexit_info, ret_offset, ret_size)
%enter_new_ctx
// Creates a new sub context as if calling itself, but with the code of the
// given account. In particular the storage, the current sender and the current
// value remain the same.
global delegate_call:
// stack: gas, address, args_offset, args_size, ret_offset, ret_size, retdest
%address
%sender
%callvalue
%stack (self, sender, value, gas, address)
// These are (static, should_transfer_value, value, sender, address, code_addr, gas)
-> (0, 0, value, sender, self, address, gas)
%jump(call_common)
// Pre stack: static, should_transfer_value, value, sender, address, code_addr, gas, args_offset, args_size, ret_offset, ret_size, retdest
// Post stack: success, leftover_gas
global call_common:
// stack: static, should_transfer_value, value, sender, address, code_addr, gas, args_offset, args_size, ret_offset, ret_size, retdest
global sys_delegatecall:
// stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size
%create_context
// Store the static flag in metadata.
%stack (new_ctx, static) -> (new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_STATIC, static, new_ctx)
// stack: new_ctx, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size
// Each line in the block below does not change the stack.
%address %set_new_ctx_addr
%caller %set_new_ctx_caller
%callvalue %set_new_ctx_value
%set_new_ctx_parent_ctx
%set_new_ctx_parent_pc(after_call_instruction)
%stack (new_ctx, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size)
-> (new_ctx, kexit_info, ret_offset, ret_size)
%enter_new_ctx
// We go here after any CALL type instruction (but not after the special call by the transaction originator).
global after_call_instruction:
// stack: success, leftover_gas, new_ctx, kexit_info, ret_offset, ret_size
SWAP3
// stack: kexit_info, leftover_gas, new_ctx, success, ret_offset, ret_size
// Add the leftover gas into the appropriate bits of kexit_info.
SWAP1 %shl_const(192) ADD
// stack: kexit_info, new_ctx, success, ret_offset, ret_size
// The callee's terminal instruction will have populated RETURNDATA.
// TODO: Copy RETURNDATA to memory[ret_offset..ret_offset + ret_size].
%stack (kexit_info, new_ctx, success, ret_offset, ret_size)
-> (kexit_info, success)
EXIT_KERNEL
// Set @CTX_METADATA_STATIC to 1. Note that there is no corresponding set_static_false routine
// because it will already be 0 by default.
%macro set_static_true
// stack: new_ctx
%stack (new_ctx) -> (new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_STATIC, 1, new_ctx)
MSTORE_GENERAL
// stack: new_ctx, should_transfer_value, value, sender, address, code_addr, gas, args_offset, args_size, ret_offset, ret_size, retdest
// stack: new_ctx
%endmacro
// Store the address in metadata.
%stack (new_ctx, should_transfer_value, value, sender, address)
-> (new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_ADDRESS, address,
new_ctx, should_transfer_value, value, sender, address)
%macro set_new_ctx_addr
// stack: called_addr, new_ctx
%stack (called_addr, new_ctx)
-> (new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_ADDRESS, called_addr, new_ctx)
MSTORE_GENERAL
// stack: new_ctx, should_transfer_value, value, sender, address, code_addr, gas, args_offset, args_size, ret_offset, ret_size, retdest
// stack: new_ctx
%endmacro
// Store the caller in metadata.
%stack (new_ctx, should_transfer_value, value, sender)
-> (new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CALLER, sender,
new_ctx, should_transfer_value, value, sender)
%macro set_new_ctx_caller
// stack: sender, new_ctx
%stack (sender, new_ctx)
-> (new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CALLER, sender, new_ctx)
MSTORE_GENERAL
// stack: new_ctx, should_transfer_value, value, sender, address, code_addr, gas, args_offset, args_size, ret_offset, ret_size, retdest
// stack: new_ctx
%endmacro
// Store the call value field in metadata.
%stack (new_ctx, should_transfer_value, value, sender, address) =
-> (new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CALL_VALUE, value,
should_transfer_value, sender, address, value, new_ctx)
%macro set_new_ctx_value
// stack: value, new_ctx
%stack (value, new_ctx)
-> (new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CALL_VALUE, value, new_ctx)
MSTORE_GENERAL
// stack: should_transfer_value, sender, address, value, new_ctx, code_addr, gas, args_offset, args_size, ret_offset, ret_size, retdest
// stack: new_ctx
%endmacro
%maybe_transfer_eth
// stack: new_ctx, code_addr, gas, args_offset, args_size, ret_offset, ret_size, retdest
%macro set_new_ctx_code_size
// stack: code_size, new_ctx
%stack (code_size, new_ctx)
-> (new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CODE_SIZE, code_size, new_ctx)
MSTORE_GENERAL
// stack: new_ctx
%endmacro
// Store parent context in metadata.
%macro set_new_ctx_gas_limit
// stack: gas_limit, new_ctx
%stack (gas_limit, new_ctx)
-> (new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CODE_SIZE, gas_limit, new_ctx)
MSTORE_GENERAL
// stack: new_ctx
%endmacro
%macro set_new_ctx_parent_ctx
// stack: new_ctx
GET_CONTEXT
PUSH @CTX_METADATA_PARENT_CONTEXT
PUSH @SEGMENT_CONTEXT_METADATA
DUP4 // new_ctx
MSTORE_GENERAL
// stack: new_ctx, code_addr, gas, args_offset, args_size, ret_offset, ret_size, retdest
// stack: new_ctx
%endmacro
// Store parent PC = after_call.
%stack (new_ctx) -> (new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_PARENT_PC, after_call, new_ctx)
%macro set_new_ctx_parent_pc(label)
// stack: new_ctx
%stack (new_ctx)
-> (new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_PARENT_PC, $label, new_ctx)
MSTORE_GENERAL
// stack: new_ctx, code_addr, gas, args_offset, args_size, ret_offset, ret_size, retdest
// stack: new_ctx
%endmacro
// TODO: Populate CALLDATA
// TODO: Save parent gas and set child gas
// TODO: Populate code
// TODO: Temporary, remove after above steps are done.
%stack (new_ctx, code_addr, gas, args_offset, args_size) -> (new_ctx)
// stack: new_ctx, ret_offset, ret_size, retdest
// Now, switch to the new context and go to usermode with PC=0.
%macro enter_new_ctx
// stack: new_ctx
// Switch to the new context and go to usermode with PC=0.
DUP1 // new_ctx
SET_CONTEXT
PUSH 0 // jump dest
EXIT_KERNEL
after_call:
// stack: new_ctx, ret_offset, ret_size, retdest
// TODO: Set RETURNDATA.
// TODO: Return to caller w/ EXIT_KERNEL.
// TODO: Return leftover gas
// (Old context) stack: new_ctx
%endmacro

View File

@ -26,14 +26,18 @@ global create:
// CREATE2; see EIP-1014. Address will be
// address = KEC(0xff || sender || salt || code_hash)[12:]
//
// Pre stack: sender, endowment, salt, CODE_ADDR, code_len, retdest
// Pre stack: sender, endowment, salt, CODE_ADDR: 3, code_len, retdest
// Post stack: address
// Note: CODE_ADDR refers to a (context, segment, offset) tuple.
global sys_create2:
// stack: sender, endowment, salt, CODE_ADDR, code_len, retdest
// stack: sender, endowment, salt, CODE_ADDR: 3, code_len, retdest
DUP7 DUP7 DUP7 DUP7 // CODE_ADDR: 3, code_len
KECCAK_GENERAL
// stack: code_hash, sender, endowment, salt, CODE_ADDR: 3, code_len, retdest
// Call get_create2_address and have it return to create_inner.
%stack (sender, endowment, salt, CODE_ADDR: 3, code_len)
-> (sender, salt, CODE_ADDR, code_len, create_inner, sender, endowment, CODE_ADDR, code_len)
%stack (code_hash, sender, endowment, salt)
-> (sender, salt, code_hash, create_inner, sender, endowment)
// stack: sender, salt, CODE_ADDR, code_len, create_inner, sender, endowment, CODE_ADDR, code_len, retdest
%jump(get_create2_address)

View File

@ -14,14 +14,12 @@ global get_create_address:
// Computes the address for a contract based on the CREATE2 rule, i.e.
// address = KEC(0xff || sender || salt || code_hash)[12:]
//
// Pre stack: sender, salt, CODE_ADDR, code_len, retdest
// Pre stack: sender, salt, code_hash, retdest
// Post stack: address
//
// Note: CODE_ADDR is a (context, segment, offset) tuple.
global get_create2_address:
// stack: sender, salt, CODE_ADDR, code_len, retdest
// stack: sender, salt, code_hash, retdest
// TODO: Replace with actual implementation.
%pop6
%pop3
PUSH 123
SWAP1
JUMP

View File

@ -39,7 +39,7 @@ global buy_gas:
// stack: gas_cost, retdest
%mload_txn_field(@TXN_FIELD_ORIGIN)
// stack: sender_addr, gas_cost, retdest
%deduct_eth
%deduct_eth // TODO: It should be transferred to coinbase instead?
// stack: deduct_eth_status, retdest
global txn_failure_insufficient_balance:
%jumpi(panic)
@ -114,69 +114,28 @@ global process_message_txn_return:
JUMP
global process_message_txn_code_loaded:
// stack: code_len, new_ctx, retdest
POP
// stack: code_size, new_ctx, retdest
%set_new_ctx_code_size
// stack: new_ctx, retdest
// Store the address in metadata.
%mload_txn_field(@TXN_FIELD_TO)
PUSH @CTX_METADATA_ADDRESS
PUSH @SEGMENT_CONTEXT_METADATA
DUP4 // new_ctx
MSTORE_GENERAL
// stack: new_ctx, retdest
// Store the caller in metadata.
%mload_txn_field(@TXN_FIELD_ORIGIN)
PUSH @CTX_METADATA_CALLER
PUSH @SEGMENT_CONTEXT_METADATA
DUP4 // new_ctx
MSTORE_GENERAL
// stack: new_ctx, retdest
// Store the call value field in metadata.
%mload_txn_field(@TXN_FIELD_VALUE)
PUSH @CTX_METADATA_CALL_VALUE
PUSH @SEGMENT_CONTEXT_METADATA
DUP4 // new_ctx
MSTORE_GENERAL
// stack: new_ctx, retdest
// No need to write @CTX_METADATA_STATIC, because it's 0 which is the default.
// Store parent context in metadata.
GET_CONTEXT
PUSH @CTX_METADATA_PARENT_CONTEXT
PUSH @SEGMENT_CONTEXT_METADATA
DUP4 // new_ctx
MSTORE_GENERAL
// stack: new_ctx, retdest
// Store parent PC = process_message_txn_after_call.
PUSH process_message_txn_after_call
PUSH @CTX_METADATA_PARENT_PC
PUSH @SEGMENT_CONTEXT_METADATA
DUP4 // new_ctx
MSTORE_GENERAL
// stack: new_ctx, retdest
// Set the new context's gas limit.
%mload_txn_field(@TXN_FIELD_GAS_LIMIT)
PUSH @CTX_METADATA_GAS_LIMIT
PUSH @SEGMENT_CONTEXT_METADATA
DUP4 // new_ctx
MSTORE_GENERAL
// Each line in the block below does not change the stack.
%mload_txn_field(@TXN_FIELD_TO) %set_new_ctx_addr
%mload_txn_field(@TXN_FIELD_ORIGIN) %set_new_ctx_caller
%mload_txn_field(@TXN_FIELD_VALUE) %set_new_ctx_value
%set_new_ctx_parent_ctx
%set_new_ctx_parent_pc(process_message_txn_after_call)
%mload_txn_field(@TXN_FIELD_GAS_LIMIT) %set_new_ctx_gas_limit
// stack: new_ctx, retdest
// TODO: Copy TXN_DATA to CALLDATA
// Now, switch to the new context and go to usermode with PC=0.
SET_CONTEXT
// stack: retdest
PUSH 0 // jump dest
EXIT_KERNEL
%enter_new_ctx
global process_message_txn_after_call:
// stack: success, retdest
POP // Pop success for now. Will go into the receipt when we support that.
// stack: success, leftover_gas, new_ctx, retdest
POP // TODO: Success will go into the receipt when we support that.
// stack: leftover_gas, new_ctx, retdest
POP // TODO: Refund leftover gas.
// stack: new_ctx, retdest
POP
JUMP

View File

@ -13,24 +13,16 @@ global sys_sgt:
PANIC
global sys_sar:
PANIC
global sys_address:
PANIC
global sys_balance:
PANIC
global sys_origin:
PANIC
global sys_caller:
PANIC
global sys_callvalue:
PANIC
global sys_calldataload:
PANIC
global sys_calldatasize:
PANIC
global sys_calldatacopy:
PANIC
global sys_codesize:
PANIC
global sys_codecopy:
PANIC
global sys_gasprice:
@ -70,12 +62,6 @@ global sys_selfbalance:
PANIC
global sys_basefee:
PANIC
global sys_msize:
// stack: kexit_info
%mload_context_metadata(@CTX_METADATA_MSIZE)
// stack: msize, kexit_info
SWAP1
EXIT_KERNEL
global sys_gas:
// stack: kexit_info
DUP1 %shr_const(192)
@ -96,11 +82,3 @@ global sys_log3:
PANIC
global sys_log4:
PANIC
global sys_call:
PANIC
global sys_callcode:
PANIC
global sys_delegatecall:
PANIC
global sys_staticcall:
PANIC

View File

@ -3,36 +3,37 @@
global sys_stop:
// stack: kexit_info
%refund_leftover_gas
// stack: (empty)
%leftover_gas
// stack: leftover_gas
// TODO: Set parent context's CTX_METADATA_RETURNDATA_SIZE to 0.
PUSH 1 // success
%jump(terminate_common)
global sys_return:
// stack: kexit_info
%refund_leftover_gas
// stack: (empty)
%leftover_gas
// stack: leftover_gas
// TODO: Set parent context's CTX_METADATA_RETURNDATA_SIZE.
// TODO: Copy returned memory to parent context's RETURNDATA (but not if we're returning from a constructor?)
// TODO: Copy returned memory to parent context's memory (as specified in their call instruction)
// TODO: Copy returned memory to parent context's RETURNDATA.
PUSH 1 // success
%jump(terminate_common)
global sys_selfdestruct:
// stack: kexit_info
%consume_gas_const(@GAS_SELFDESTRUCT)
%refund_leftover_gas
// stack: (empty)
%leftover_gas
// stack: leftover_gas
// TODO: Destroy account.
PUSH 1 // success
%jump(terminate_common)
global sys_revert:
// stack: kexit_info
%refund_leftover_gas
// stack: (empty)
%leftover_gas
// stack: leftover_gas
// TODO: Revert state changes.
// TODO: Set parent context's CTX_METADATA_RETURNDATA_SIZE.
// TODO: Copy returned memory to parent context's RETURNDATA.
PUSH 0 // success
%jump(terminate_common)
@ -44,24 +45,36 @@ global sys_revert:
// - the new stack size would be larger than 1024, or
// - state modification is attempted during a static call
global fault_exception:
// stack: (empty)
PUSH 0 // leftover_gas
// TODO: Revert state changes.
// TODO: Set parent context's CTX_METADATA_RETURNDATA_SIZE to 0.
PUSH 0 // success
%jump(terminate_common)
terminate_common:
// stack: success
global terminate_common:
// stack: success, leftover_gas
// TODO: Panic if we exceeded our gas limit?
// We want to move the success flag from our (child) context's stack to the
// parent context's stack. We will write it to memory, specifically
// SEGMENT_KERNEL_GENERAL[0], then load it after the context switch.
PUSH 0
// stack: 0, success
// stack: 0, success, leftover_gas
%mstore_kernel_general
// stack: leftover_gas
// Similarly, we write leftover_gas to SEGMENT_KERNEL_GENERAL[1] so that
// we can later read it after switching to the parent context.
PUSH 1
// stack: 1, leftover_gas
%mstore_kernel_general
// stack: (empty)
// Similarly, we write the parent PC to SEGMENT_KERNEL_GENERAL[1] so that
// Similarly, we write the parent PC to SEGMENT_KERNEL_GENERAL[2] so that
// we can later read it after switching to the parent context.
%mload_context_metadata(@CTX_METADATA_PARENT_PC)
PUSH 1
PUSH 2
%mstore_kernel(@SEGMENT_KERNEL_GENERAL)
// stack: (empty)
@ -70,20 +83,19 @@ terminate_common:
SET_CONTEXT
// stack: (empty)
// Load the success flag and parent PC that we stored in SEGMENT_KERNEL_GENERAL.
PUSH 0 %mload_kernel_general
PUSH 1 %mload_kernel_general
// Load the fields that we stored in SEGMENT_KERNEL_GENERAL.
PUSH 1 %mload_kernel_general // leftover_gas
PUSH 0 %mload_kernel_general // success
PUSH 2 %mload_kernel_general // parent_pc
// stack: parent_pc, success
// stack: parent_pc, success, leftover_gas
JUMP
%macro refund_leftover_gas
%macro leftover_gas
// stack: kexit_info
%shr_const(192)
// stack: gas_used
%mload_context_metadata(@CTX_METADATA_GAS_LIMIT)
SUB
// stack: leftover_gas
POP // TODO: Refund to caller.
// stack: (empty)
%endmacro

View File

@ -6,7 +6,7 @@ global main:
PUSH hash_initial_tries
%jump(load_all_mpts)
hash_initial_tries:
global hash_initial_tries:
%mpt_hash_state_trie %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_BEFORE)
%mpt_hash_txn_trie %mstore_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_BEFORE)
%mpt_hash_receipt_trie %mstore_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_BEFORE)

View File

@ -38,18 +38,57 @@
%mload_context_metadata(@CTX_METADATA_ADDRESS)
%endmacro
%macro sender
global sys_address:
// stack: kexit_info
%address
// stack: address, kexit_info
SWAP1
EXIT_KERNEL
%macro caller
%mload_context_metadata(@CTX_METADATA_CALLER)
%endmacro
global sys_caller:
// stack: kexit_info
%caller
// stack: caller, kexit_info
SWAP1
EXIT_KERNEL
%macro callvalue
%mload_context_metadata(@CTX_METADATA_CALL_VALUE)
%endmacro
%macro codesize
%mload_context_metadata(@CTX_METADATA_CODE_SIZE)
%endmacro
global sys_codesize:
// stack: kexit_info
%codesize
// stack: codesize, kexit_info
SWAP1
EXIT_KERNEL
global sys_callvalue:
// stack: kexit_info
%callvalue
// stack: callvalue, kexit_info
SWAP1
EXIT_KERNEL
%macro msize
%mload_context_metadata(@CTX_METADATA_MSIZE)
%endmacro
global sys_msize:
// stack: kexit_info
%msize
// stack: msize, kexit_info
SWAP1
EXIT_KERNEL
%macro update_msize
// stack: offset
%add_const(32)
@ -64,4 +103,3 @@
// stack: new_msize
%mstore_context_metadata(@CTX_METADATA_MSIZE)
%endmacro

View File

@ -1,4 +1,5 @@
use std::collections::HashMap;
use std::time::Instant;
use ethereum_types::U256;
use itertools::{izip, Itertools};
@ -111,6 +112,7 @@ pub(crate) fn assemble(
let mut local_labels = Vec::with_capacity(files.len());
let mut macro_counter = 0;
for file in files {
let start = Instant::now();
let mut file = file.body;
file = expand_macros(file, &macros, &mut macro_counter);
file = inline_constants(file, &constants);
@ -125,6 +127,7 @@ pub(crate) fn assemble(
&mut prover_inputs,
));
expanded_files.push(file);
debug!("Expanding file took {:?}", start.elapsed());
}
let mut code = vec![];
for (file, locals) in izip!(expanded_files, local_labels) {
@ -134,6 +137,7 @@ pub(crate) fn assemble(
debug!("Assembled file size: {} bytes", file_len);
}
assert_eq!(code.len(), offset, "Code length doesn't match offset.");
debug!("Total kernel size: {} bytes", code.len());
Kernel::new(code, global_labels, prover_inputs)
}

View File

@ -28,23 +28,12 @@ fn test_get_create2_address() -> Result<()> {
// TODO: Replace with real data once we have a real implementation.
let retaddr = 0xdeadbeefu32.into();
let code_len = 0.into();
let code_offset = 0.into();
let code_segment = 0.into();
let code_context = 0.into();
let code_hash = 0.into();
let salt = 5.into();
let sender = 0.into();
let expected_addr = 123.into();
let initial_stack = vec![
retaddr,
code_len,
code_offset,
code_segment,
code_context,
salt,
sender,
];
let initial_stack = vec![retaddr, code_hash, salt, sender];
let mut interpreter = Interpreter::new_with_kernel(get_create2_address, initial_stack);
interpreter.run()?;