From a6ac0519521c7b0192746e741d817f0810c6e4f5 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Mon, 6 Mar 2023 21:57:51 -0800 Subject: [PATCH] Misc EVM fixes --- evm/src/cpu/kernel/aggregator.rs | 1 + evm/src/cpu/kernel/asm/account_code.asm | 77 +++---- evm/src/cpu/kernel/asm/core/call.asm | 215 +++++++++++------- evm/src/cpu/kernel/asm/core/create.asm | 12 +- .../cpu/kernel/asm/core/create_addresses.asm | 8 +- evm/src/cpu/kernel/asm/core/process_txn.asm | 75 ++---- evm/src/cpu/kernel/asm/core/syscall_stubs.asm | 22 -- evm/src/cpu/kernel/asm/core/terminate.asm | 56 +++-- evm/src/cpu/kernel/asm/main.asm | 2 +- evm/src/cpu/kernel/asm/memory/metadata.asm | 42 +++- evm/src/cpu/kernel/assembler.rs | 4 + .../cpu/kernel/tests/core/create_addresses.rs | 15 +- 12 files changed, 285 insertions(+), 244 deletions(-) diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index 6738e1e6..1d8f11a0 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -12,6 +12,7 @@ pub static KERNEL: Lazy = 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"), diff --git a/evm/src/cpu/kernel/asm/account_code.asm b/evm/src/cpu/kernel/asm/account_code.asm index 7e1ece6d..f10fbc19 100644 --- a/evm/src/cpu/kernel/asm/account_code.asm +++ b/evm/src/cpu/kernel/asm/account_code.asm @@ -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 diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index cc1f5218..198a6cbb 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -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 diff --git a/evm/src/cpu/kernel/asm/core/create.asm b/evm/src/cpu/kernel/asm/core/create.asm index e2552ff5..eb0f821e 100644 --- a/evm/src/cpu/kernel/asm/core/create.asm +++ b/evm/src/cpu/kernel/asm/core/create.asm @@ -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) diff --git a/evm/src/cpu/kernel/asm/core/create_addresses.asm b/evm/src/cpu/kernel/asm/core/create_addresses.asm index ceda8b13..2d94ee94 100644 --- a/evm/src/cpu/kernel/asm/core/create_addresses.asm +++ b/evm/src/cpu/kernel/asm/core/create_addresses.asm @@ -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 diff --git a/evm/src/cpu/kernel/asm/core/process_txn.asm b/evm/src/cpu/kernel/asm/core/process_txn.asm index 1796b7a5..17770ba4 100644 --- a/evm/src/cpu/kernel/asm/core/process_txn.asm +++ b/evm/src/cpu/kernel/asm/core/process_txn.asm @@ -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 diff --git a/evm/src/cpu/kernel/asm/core/syscall_stubs.asm b/evm/src/cpu/kernel/asm/core/syscall_stubs.asm index 51c1b01b..6dcbbb6e 100644 --- a/evm/src/cpu/kernel/asm/core/syscall_stubs.asm +++ b/evm/src/cpu/kernel/asm/core/syscall_stubs.asm @@ -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 diff --git a/evm/src/cpu/kernel/asm/core/terminate.asm b/evm/src/cpu/kernel/asm/core/terminate.asm index b75ed987..341884ea 100644 --- a/evm/src/cpu/kernel/asm/core/terminate.asm +++ b/evm/src/cpu/kernel/asm/core/terminate.asm @@ -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 diff --git a/evm/src/cpu/kernel/asm/main.asm b/evm/src/cpu/kernel/asm/main.asm index c6e818b2..e87b790c 100644 --- a/evm/src/cpu/kernel/asm/main.asm +++ b/evm/src/cpu/kernel/asm/main.asm @@ -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) diff --git a/evm/src/cpu/kernel/asm/memory/metadata.asm b/evm/src/cpu/kernel/asm/memory/metadata.asm index 1a495682..7ea6d9e5 100644 --- a/evm/src/cpu/kernel/asm/memory/metadata.asm +++ b/evm/src/cpu/kernel/asm/memory/metadata.asm @@ -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 - diff --git a/evm/src/cpu/kernel/assembler.rs b/evm/src/cpu/kernel/assembler.rs index 15fb1d4b..2afd328f 100644 --- a/evm/src/cpu/kernel/assembler.rs +++ b/evm/src/cpu/kernel/assembler.rs @@ -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, ¯os, &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) } diff --git a/evm/src/cpu/kernel/tests/core/create_addresses.rs b/evm/src/cpu/kernel/tests/core/create_addresses.rs index c77ff937..047ddc00 100644 --- a/evm/src/cpu/kernel/tests/core/create_addresses.rs +++ b/evm/src/cpu/kernel/tests/core/create_addresses.rs @@ -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()?;