From cc61c7211c091a6ffe1b9d40835f0bbf02629f7f Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Thu, 28 Jul 2022 15:46:36 -0700 Subject: [PATCH 1/2] Core transaction processing logic With lots of TODOs to fill in afterward; this is just a start. --- evm/src/cpu/columns/mod.rs | 3 - evm/src/cpu/kernel/aggregator.rs | 10 ++- evm/src/cpu/kernel/asm/core/bootloader.asm | 11 +++ evm/src/cpu/kernel/asm/core/call.asm | 59 +++++++++++++ evm/src/cpu/kernel/asm/core/create.asm | 84 +++++++++++++++++++ .../cpu/kernel/asm/core/create_addresses.asm | 27 ++++++ evm/src/cpu/kernel/asm/core/intrinsic_gas.asm | 65 ++++++++++++++ evm/src/cpu/kernel/asm/core/nonce.asm | 28 +++++++ evm/src/cpu/kernel/asm/core/process_txn.asm | 39 +++++++++ evm/src/cpu/kernel/asm/core/terminate.asm | 56 +++++++++++++ evm/src/cpu/kernel/asm/core/transfer.asm | 16 ++++ evm/src/cpu/kernel/asm/core/util.asm | 32 +++++++ evm/src/cpu/kernel/asm/keccak.asm | 8 ++ evm/src/cpu/kernel/asm/memory/metadata.asm | 12 +++ .../asm/transactions/process_normalized.asm | 5 -- evm/src/cpu/kernel/interpreter.rs | 7 +- .../cpu/kernel/tests/core/create_addresses.rs | 54 ++++++++++++ .../cpu/kernel/tests/core/intrinsic_gas.rs | 27 ++++++ evm/src/cpu/kernel/tests/core/mod.rs | 2 + evm/src/cpu/kernel/tests/mod.rs | 1 + evm/src/memory/segments.rs | 2 +- 21 files changed, 537 insertions(+), 11 deletions(-) create mode 100644 evm/src/cpu/kernel/asm/core/bootloader.asm create mode 100644 evm/src/cpu/kernel/asm/core/call.asm create mode 100644 evm/src/cpu/kernel/asm/core/create.asm create mode 100644 evm/src/cpu/kernel/asm/core/create_addresses.asm create mode 100644 evm/src/cpu/kernel/asm/core/intrinsic_gas.asm create mode 100644 evm/src/cpu/kernel/asm/core/nonce.asm create mode 100644 evm/src/cpu/kernel/asm/core/process_txn.asm create mode 100644 evm/src/cpu/kernel/asm/core/terminate.asm create mode 100644 evm/src/cpu/kernel/asm/core/transfer.asm create mode 100644 evm/src/cpu/kernel/asm/core/util.asm create mode 100644 evm/src/cpu/kernel/asm/keccak.asm delete mode 100644 evm/src/cpu/kernel/asm/transactions/process_normalized.asm create mode 100644 evm/src/cpu/kernel/tests/core/create_addresses.rs create mode 100644 evm/src/cpu/kernel/tests/core/intrinsic_gas.rs create mode 100644 evm/src/cpu/kernel/tests/core/mod.rs diff --git a/evm/src/cpu/columns/mod.rs b/evm/src/cpu/columns/mod.rs index 01260ec2..8168bc63 100644 --- a/evm/src/cpu/columns/mod.rs +++ b/evm/src/cpu/columns/mod.rs @@ -17,9 +17,6 @@ pub struct CpuColumnsView { /// Filter. 1 if the row is part of bootstrapping the kernel code, 0 otherwise. pub is_bootstrap_kernel: T, - /// Filter. 1 if the row is part of bootstrapping a contract's code, 0 otherwise. - pub is_bootstrap_contract: T, - /// Filter. 1 if the row corresponds to a cycle of execution and 0 otherwise. /// Lets us re-use columns in non-cycle rows. pub is_cpu_cycle: T, diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index 5f28d89b..eb55238b 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -11,6 +11,15 @@ 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/create.asm"), + include_str!("asm/core/create_addresses.asm"), + include_str!("asm/core/intrinsic_gas.asm"), + include_str!("asm/core/nonce.asm"), + include_str!("asm/core/process_txn.asm"), + include_str!("asm/core/terminate.asm"), + include_str!("asm/core/transfer.asm"), + include_str!("asm/core/util.asm"), include_str!("asm/curve/bn254/curve_add.asm"), include_str!("asm/curve/bn254/curve_mul.asm"), include_str!("asm/curve/bn254/moddiv.asm"), @@ -33,7 +42,6 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/rlp/read_to_memory.asm"), include_str!("asm/storage/read.asm"), include_str!("asm/storage/write.asm"), - include_str!("asm/transactions/process_normalized.asm"), include_str!("asm/transactions/router.asm"), include_str!("asm/transactions/type_0.asm"), include_str!("asm/transactions/type_1.asm"), diff --git a/evm/src/cpu/kernel/asm/core/bootloader.asm b/evm/src/cpu/kernel/asm/core/bootloader.asm new file mode 100644 index 00000000..7ebdf022 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/bootloader.asm @@ -0,0 +1,11 @@ +// Loads some prover-provided contract code into the code segment of memory, +// then hashes the code and returns the hash. + +global bootload_contract: + // stack: retdest + + // TODO + + // stack: code_hash, retdest + SWAP1 + JUMP diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm new file mode 100644 index 00000000..3cbbb441 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -0,0 +1,59 @@ +// Handlers for call-like operations, namely CALL, CALLCODE, STATICCALL and DELEGATECALL. + +// Creates a new sub context and executes the code of the given account. +global call: + // stack: gas, address, value, args_offset, args_size, ret_offset, ret_size + %address + %stack (self, gas, address, value) + // These are (should_transfer_value, value, static, gas, sender, storage, code_addr) + -> (1, value, 0, gas, self, address, address) + %jump(call_common) + +// 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 + %address + %stack (self, gas, address, value) + // These are (should_transfer_value, value, static, gas, sender, storage, code_addr) + -> (1, value, 0, gas, self, self, address) + %jump(call_common) + +// 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 + %address + %stack (self, gas, address) + // These are (should_transfer_value, value, static, gas, sender, storage, code_addr) + -> (0, 0, 1, gas, self, address, address) + %jump(call_common) + +// 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 + %address + %sender + %callvalue + %stack (self, sender, value, gas, address) + // These are (should_transfer_value, value, static, gas, sender, storage, code_addr) + -> (0, value, 0, gas, sender, self, address) + %jump(call_common) + +call_common: + // stack: should_transfer_value, value, static, gas, sender, storage, code_addr, args_offset, args_size, ret_offset, ret_size + // TODO: Set all the appropriate metadata fields... + %create_context + // stack: new_ctx, after_call + // Now, switch to the new context and go to usermode with PC=0. + SET_CONTEXT + PUSH 0 + EXIT_KERNEL + +after_call: + // TODO: Set RETURNDATA etc. diff --git a/evm/src/cpu/kernel/asm/core/create.asm b/evm/src/cpu/kernel/asm/core/create.asm new file mode 100644 index 00000000..dfdb6b76 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/create.asm @@ -0,0 +1,84 @@ +// Create a new contract account with the traditional address scheme, i.e. +// address = KEC(RLP(sender, nonce))[12:] +// This can be used both for the CREATE instruction and for contract-creation +// transactions. +// +// Pre stack: CODE_ADDR, code_len, retdest +// Post stack: address +// Note: CODE_ADDR refers to a (context, segment, offset) tuple. +global create: + // stack: sender, endowment, CODE_ADDR, code_len, retdest + DUP1 %get_nonce + // stack: nonce, sender, endowment, CODE_ADDR, code_len, retdest + // Call get_create_address and have it return to create_inner. + %stack (nonce, sender) + -> (sender, nonce, create_inner, sender) + %jump(get_create_address) + +// 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 +// Post stack: address +// Note: CODE_ADDR refers to a (context, segment, offset) tuple. +global create2: + // stack: sender, endowment, salt, CODE_ADDR, code_len, retdest + // Call get_create2_address and have it return to create_inner. + %stack (sender, endowment, salt) -> (salt, sender, endowment) + // stack: salt, sender, endowment, CODE_ADDR, code_len, retdest + DUP7 DUP7 DUP7 DUP7 // CODE_ADDR and code_len + // stack: CODE_ADDR, code_len, salt, sender, endowment, CODE_ADDR, code_len, retdest + PUSH create_inner + // stack: create_inner, CODE_ADDR, code_len, salt, sender, endowment, CODE_ADDR, code_len, retdest + SWAP5 // create_inner <-> salt + // stack: salt, CODE_ADDR, code_len, create_inner, sender, endowment, CODE_ADDR, code_len, retdest + DUP7 // sender + // stack: sender, salt, CODE_ADDR, code_len, create_inner, sender, endowment, CODE_ADDR, code_len, retdest + %jump(get_create2_address) + +// Pre stack: address, sender, endowment, CODE_ADDR, code_len, retdest +// Post stack: address +// Note: CODE_ADDR refers to a (context, segment, offset) tuple. +create_inner: + // stack: address, sender, endowment, CODE_ADDR, code_len, retdest + %stack (address, sender, endowment) + -> (sender, address, endowment, sender, address) + // TODO: Need to handle insufficient balance failure. + %transfer_eth + // stack: sender, address, CODE_ADDR, code_len, retdest + + %increment_nonce + // stack: address, CODE_ADDR, code_len, retdest + + %create_context + // stack: new_ctx, address, CODE_ADDR, code_len, retdest + %stack (new_ctx, address, src_ctx, src_segment, src_offset, code_len) + -> (new_ctx, @SEGMENT_CODE, 0, + src_ctx, src_segment, src_offset, + code_len, run_constructor, + new_ctx, address) + %jump(memcpy) + +run_constructor: + // stack: new_ctx, address, retdest + // At this point, the initialization code has been loaded. + // Save our return address in memory, so we'll be in `after_constructor` + // after the new context returns. + // Note: We can't use %mstore_context_metadata because we're writing to + // memory owned by the new context, not the current one. + %stack (new_ctx) -> (new_ctx, @SEGMENT_CONTEXT_METADATA, + @CTX_METADATA_PARENT_PC, after_constructor, new_ctx) + MSTORE_GENERAL + // stack: new_ctx, address, retdest + + // Now, switch to the new context and go to usermode with PC=0. + SET_CONTEXT + // stack: (empty, since we're in the new context) + PUSH 0 + EXIT_KERNEL + +after_constructor: + // stack: address, retdest + // TODO: If code was returned, store it in the account. + SWAP1 + JUMP diff --git a/evm/src/cpu/kernel/asm/core/create_addresses.asm b/evm/src/cpu/kernel/asm/core/create_addresses.asm new file mode 100644 index 00000000..ceda8b13 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/create_addresses.asm @@ -0,0 +1,27 @@ +// Computes the address of a contract based on the conventional scheme, i.e. +// address = KEC(RLP(sender, nonce))[12:] +// +// Pre stack: sender, nonce, retdest +// Post stack: address +global get_create_address: + // stack: sender, nonce, retdest + // TODO: Replace with actual implementation. + %pop2 + PUSH 123 + SWAP1 + JUMP + +// 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 +// Post stack: address +// +// Note: CODE_ADDR is a (context, segment, offset) tuple. +global get_create2_address: + // stack: sender, salt, CODE_ADDR, code_len, retdest + // TODO: Replace with actual implementation. + %pop6 + PUSH 123 + SWAP1 + JUMP diff --git a/evm/src/cpu/kernel/asm/core/intrinsic_gas.asm b/evm/src/cpu/kernel/asm/core/intrinsic_gas.asm new file mode 100644 index 00000000..3c6f47b6 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/intrinsic_gas.asm @@ -0,0 +1,65 @@ +// After the transaction data has been parsed into a normalized set of fields +// (see NormalizedTxnField), this routine processes the transaction. + +global intrinsic_gas: + // stack: retdest + // Calculate the number of zero and nonzero bytes in the txn data. + PUSH 0 // zeros = 0 + PUSH 0 // i = 0 + +count_zeros_loop: + // stack: i, zeros, retdest + DUP1 + %mload_txn_field(@TXN_FIELD_DATA_LEN) + EQ + // stack: i == data.len, i, zeros, retdest + %jumpi(count_zeros_finish) + + // stack: i, zeros, retdest + DUP1 + %mload_kernel(@SEGMENT_TXN_DATA) + ISZERO + // stack: data[i] == 0, i, zeros + %stack (data_i_is_zero, i, zeros) -> (data_i_is_zero, zeros, i) + ADD + // stack: zeros', i, retdest + SWAP1 + // stack: i, zeros', retdest + %add_const(1) + // stack: i', zeros', retdest + %jump(count_zeros_loop) + +count_zeros_finish: + // stack: i, zeros, retdest + POP + // stack: zeros, retdest + DUP1 + // stack: zeros, zeros, retdest + %mload_txn_field(@TXN_FIELD_DATA_LEN) + // stack: data.len, zeros, zeros, retdest + SUB + // stack: nonzeros, zeros, retdest + %mul_const(@GAS_TXDATANONZERO) + // stack: gas_nonzeros, zeros, retdest + SWAP1 + %mul_const(@GAS_TXDATAZERO) + // stack: gas_zeros, gas_nonzeros, retdest + ADD + // stack: gas_txndata, retdest + + %is_contract_creation + %mul_const(@GAS_TXCREATE) + // stack: gas_creation, gas_txndata, retdest + + PUSH @GAS_TRANSACTION + // stack: gas_txn, gas_creation, gas_txndata, retdest + + // TODO: Add num_access_list_addresses * GAS_ACCESSLISTADDRESS + // TODO: Add num_access_list_slots * GAS_ACCESSLISTSTORAGE + + ADD + ADD + // stack: total_gas, retdest + + SWAP1 + JUMP diff --git a/evm/src/cpu/kernel/asm/core/nonce.asm b/evm/src/cpu/kernel/asm/core/nonce.asm new file mode 100644 index 00000000..6e57eb40 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/nonce.asm @@ -0,0 +1,28 @@ +// Increment the nonce of the given account. +// Pre stack: address, retdest +// Post stack: (empty) + +global get_nonce: + // stack: address, retdest + // TODO: Replace with actual implementation. + JUMP + +// Convenience macro to call get_nonce and return where we left off. +%macro get_nonce + %stack (address) -> (address, %%after) + %jump(get_nonce) +%%after: +%endmacro + +global increment_nonce: + // stack: address, retdest + // TODO: Replace with actual implementation. + POP + JUMP + +// Convenience macro to call increment_nonce and return where we left off. +%macro increment_nonce + %stack (address) -> (address, %%after) + %jump(increment_nonce) +%%after: +%endmacro diff --git a/evm/src/cpu/kernel/asm/core/process_txn.asm b/evm/src/cpu/kernel/asm/core/process_txn.asm new file mode 100644 index 00000000..b3d92131 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/process_txn.asm @@ -0,0 +1,39 @@ +// After the transaction data has been parsed into a normalized set of fields +// (see NormalizedTxnField), this routine processes the transaction. + +global process_normalized_txn: + // stack: (empty) + PUSH validate + %jump(intrinsic_gas) + +validate: + // stack: intrinsic_gas + // TODO: Check gas >= intrinsic_gas. + // TODO: Check sender_balance >= intrinsic_gas + value. + +buy_gas: + // TODO: Deduct gas from sender (some may be refunded later). + +increment_nonce: + // TODO: Increment nonce. + +process_based_on_type: + %is_contract_creation + %jumpi(process_contract_creation_txn) + %jump(process_message_txn) + +process_contract_creation_txn: + // stack: (empty) + // Push the code address & length onto the stack, then call `create`. + %mload_txn_field(@TXN_FIELD_DATA_LEN) + // stack: code_len + PUSH 0 + // stack: code_offset, code_len + PUSH @SEGMENT_TXN_DATA + // stack: code_segment, code_offset, code_len + PUSH 0 // context + // stack: CODE_ADDR, code_len + %jump(create) + +process_message_txn: + // TODO diff --git a/evm/src/cpu/kernel/asm/core/terminate.asm b/evm/src/cpu/kernel/asm/core/terminate.asm new file mode 100644 index 00000000..270a6c90 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/terminate.asm @@ -0,0 +1,56 @@ +// Handlers for operations which terminate the current context, namely STOP, +// RETURN, SELFDESTRUCT, REVERT, and exceptions such as stack underflow. + +global stop: + // TODO: Set parent context's CTX_METADATA_RETURNDATA_SIZE to 0. + %jump(terminate_common) + +global return: + // 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) + %jump(terminate_common) + +global selfdestruct: + %consume_gas_const(@GAS_SELFDESTRUCT) + // TODO + %jump(terminate_common) + +global revert: + // TODO + %jump(terminate_common) + +// The execution is in an exceptional halt-ing state if +// - there is insufficient gas +// - the instruction is invalid +// - there are insufficient stack items +// - a JUMP/JUMPI destination is invalid +// - the new stack size would be larger than 1024, or +// - state modification is attempted during a static call +global exception: + // TODO + %jump(terminate_common) + +terminate_common: + // stack: success + // 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 + %mstore_kernel(@SEGMENT_KERNEL_GENERAL) + // stack: (empty) + + // Go back to the parent context. + %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) + SET_CONTEXT + // stack: (empty) + + // Load the success flag that we stored in SEGMENT_KERNEL_GENERAL[0]. + PUSH 0 + %mload_kernel(@SEGMENT_KERNEL_GENERAL) + // stack: success + + // JUMP to the parent IP. + %mload_context_metadata(@CTX_METADATA_PARENT_PC) + JUMP diff --git a/evm/src/cpu/kernel/asm/core/transfer.asm b/evm/src/cpu/kernel/asm/core/transfer.asm new file mode 100644 index 00000000..41057aff --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/transfer.asm @@ -0,0 +1,16 @@ +// Transfers some ETH from one address to another. The amount is given in wei. +// Pre stack: from, to, amount, retdest +// Post stack: (empty) + +global transfer_eth: + // stack: from, to, amount, retdest + // TODO: Replace with actual implementation. + %pop3 + JUMP + +// Convenience macro to call transfer_eth and return where we left off. +%macro transfer_eth + %stack (from, to, amount) -> (from, to, amount, %%after) + %jump(transfer_eth) +%%after: +%endmacro diff --git a/evm/src/cpu/kernel/asm/core/util.asm b/evm/src/cpu/kernel/asm/core/util.asm new file mode 100644 index 00000000..4ceaec3b --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/util.asm @@ -0,0 +1,32 @@ +// Return the next context ID, and record the old context ID in the new one's +// @CTX_METADATA_PARENT_CONTEXT field. Does not actually enter the new context. +%macro create_context + %next_context_id + GET_CONTEXT + %stack (ctx, next_ctx) + -> (next_ctx, @SEGMENT_NORMALIZED_TXN, @CTX_METADATA_PARENT_CONTEXT, + ctx, next_ctx) + MSTORE_GENERAL + // stack: next_ctx +%endmacro + +// Get and increment @GLOBAL_METADATA_LARGEST_CONTEXT to determine the next context ID. +%macro next_context_id + // stack: (empty) + %mload_global_metadata(@GLOBAL_METADATA_LARGEST_CONTEXT) + %add_const(1) + // stack: new_ctx + DUP1 + %mstore_global_metadata(@GLOBAL_METADATA_LARGEST_CONTEXT) + // stack: new_ctx +%endmacro + +// Returns whether the current transaction is a contract creation transaction. +%macro is_contract_creation + // stack: (empty) + %mload_txn_field(@TXN_FIELD_TO) + // stack: to + ISZERO + // If there is no "to" field, then this is a contract creation. + // stack: to == 0 +%endmacro diff --git a/evm/src/cpu/kernel/asm/keccak.asm b/evm/src/cpu/kernel/asm/keccak.asm new file mode 100644 index 00000000..d464bb6a --- /dev/null +++ b/evm/src/cpu/kernel/asm/keccak.asm @@ -0,0 +1,8 @@ +// Computes the Keccak256 hash of some arbitrary bytes in memory. +// The given memory values should be in the range of a byte. +// +// Pre stack: ADDR, len, retdest +// Post stack: hash +global keccak_general: + // stack: ADDR, len + // TODO diff --git a/evm/src/cpu/kernel/asm/memory/metadata.asm b/evm/src/cpu/kernel/asm/memory/metadata.asm index 22eb853f..23c45d13 100644 --- a/evm/src/cpu/kernel/asm/memory/metadata.asm +++ b/evm/src/cpu/kernel/asm/memory/metadata.asm @@ -33,3 +33,15 @@ %mload_current(@SEGMENT_CONTEXT_METADATA) // stack: (empty) %endmacro + +%macro address + %mload_context_metadata(0) // TODO: Read proper field. +%endmacro + +%macro sender + %mload_context_metadata(0) // TODO: Read proper field. +%endmacro + +%macro callvalue + %mload_context_metadata(0) // TODO: Read proper field. +%endmacro diff --git a/evm/src/cpu/kernel/asm/transactions/process_normalized.asm b/evm/src/cpu/kernel/asm/transactions/process_normalized.asm deleted file mode 100644 index d99041b0..00000000 --- a/evm/src/cpu/kernel/asm/transactions/process_normalized.asm +++ /dev/null @@ -1,5 +0,0 @@ -// After the transaction data has been parsed into a normalized set of fields -// (see TxnField), this routine processes the transaction. - -global process_normalized_txn: - // TODO diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 5d1938e5..45208d61 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -134,7 +134,12 @@ impl<'a> Interpreter<'a> { } pub(crate) fn get_txn_field(&self, field: NormalizedTxnField) -> U256 { - self.memory.context_memory[0].segments[Segment::TxnFields as usize].content[field as usize] + self.memory.context_memory[0].segments[Segment::TxnFields as usize].get(field as usize) + } + + pub(crate) fn set_txn_field(&mut self, field: NormalizedTxnField, value: U256) { + self.memory.context_memory[0].segments[Segment::TxnFields as usize] + .set(field as usize, value); } pub(crate) fn get_txn_data(&self) -> &[U256] { diff --git a/evm/src/cpu/kernel/tests/core/create_addresses.rs b/evm/src/cpu/kernel/tests/core/create_addresses.rs new file mode 100644 index 00000000..c77ff937 --- /dev/null +++ b/evm/src/cpu/kernel/tests/core/create_addresses.rs @@ -0,0 +1,54 @@ +use anyhow::Result; + +use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::interpreter::Interpreter; + +#[test] +fn test_get_create_address() -> Result<()> { + let get_create_address = KERNEL.global_labels["get_create_address"]; + + // TODO: Replace with real data once we have a real implementation. + let retaddr = 0xdeadbeefu32.into(); + let nonce = 5.into(); + let sender = 0.into(); + let expected_addr = 123.into(); + + let initial_stack = vec![retaddr, nonce, sender]; + let mut interpreter = Interpreter::new_with_kernel(get_create_address, initial_stack); + interpreter.run()?; + + assert_eq!(interpreter.stack(), &[expected_addr]); + + Ok(()) +} + +#[test] +fn test_get_create2_address() -> Result<()> { + let get_create2_address = KERNEL.global_labels["get_create2_address"]; + + // 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 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 mut interpreter = Interpreter::new_with_kernel(get_create2_address, initial_stack); + interpreter.run()?; + + assert_eq!(interpreter.stack(), &[expected_addr]); + + Ok(()) +} diff --git a/evm/src/cpu/kernel/tests/core/intrinsic_gas.rs b/evm/src/cpu/kernel/tests/core/intrinsic_gas.rs new file mode 100644 index 00000000..cc96f955 --- /dev/null +++ b/evm/src/cpu/kernel/tests/core/intrinsic_gas.rs @@ -0,0 +1,27 @@ +use anyhow::Result; + +use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::interpreter::Interpreter; +use crate::cpu::kernel::txn_fields::NormalizedTxnField; + +const GAS_TX: u32 = 21_000; +const GAS_TXCREATE: u32 = 32_000; + +#[test] +fn test_intrinsic_gas() -> Result<()> { + let intrinsic_gas = KERNEL.global_labels["intrinsic_gas"]; + + // Contract creation transaction. + let initial_stack = vec![0xdeadbeefu32.into()]; + let mut interpreter = Interpreter::new_with_kernel(intrinsic_gas, initial_stack.clone()); + interpreter.run()?; + assert_eq!(interpreter.stack(), vec![(GAS_TX + GAS_TXCREATE).into()]); + + // Message transaction. + let mut interpreter = Interpreter::new_with_kernel(intrinsic_gas, initial_stack); + interpreter.set_txn_field(NormalizedTxnField::To, 123.into()); + interpreter.run()?; + assert_eq!(interpreter.stack(), vec![GAS_TX.into()]); + + Ok(()) +} diff --git a/evm/src/cpu/kernel/tests/core/mod.rs b/evm/src/cpu/kernel/tests/core/mod.rs new file mode 100644 index 00000000..dc9b2f39 --- /dev/null +++ b/evm/src/cpu/kernel/tests/core/mod.rs @@ -0,0 +1,2 @@ +mod create_addresses; +mod intrinsic_gas; diff --git a/evm/src/cpu/kernel/tests/mod.rs b/evm/src/cpu/kernel/tests/mod.rs index 1396f9f2..925db56f 100644 --- a/evm/src/cpu/kernel/tests/mod.rs +++ b/evm/src/cpu/kernel/tests/mod.rs @@ -1,3 +1,4 @@ +mod core; mod curve_ops; mod ecrecover; mod exp; diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index 712db644..48f9136b 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -18,7 +18,7 @@ pub(crate) enum Segment { /// General purpose kernel memory, used by various kernel functions. /// In general, calling a helper function can result in this memory being clobbered. KernelGeneral = 7, - /// Contains normalized transaction fields; see `TxnField`. + /// Contains normalized transaction fields; see `NormalizedTxnField`. TxnFields = 8, /// Contains the data field of a transaction. TxnData = 9, From 74b1fd25e6fef011314674afe110f213a6bd3416 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Fri, 12 Aug 2022 17:37:00 -0700 Subject: [PATCH 2/2] TODOs --- evm/src/cpu/kernel/asm/core/terminate.asm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/evm/src/cpu/kernel/asm/core/terminate.asm b/evm/src/cpu/kernel/asm/core/terminate.asm index 270a6c90..7aff0e43 100644 --- a/evm/src/cpu/kernel/asm/core/terminate.asm +++ b/evm/src/cpu/kernel/asm/core/terminate.asm @@ -3,21 +3,24 @@ global stop: // TODO: Set parent context's CTX_METADATA_RETURNDATA_SIZE to 0. + // TODO: Refund unused gas to parent. %jump(terminate_common) global return: // 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: Refund unused gas to parent. %jump(terminate_common) global selfdestruct: %consume_gas_const(@GAS_SELFDESTRUCT) - // TODO + // TODO: Destroy account. + // TODO: Refund unused gas to parent. %jump(terminate_common) global revert: - // TODO + // TODO: Refund unused gas to parent. %jump(terminate_common) // The execution is in an exceptional halt-ing state if