diff --git a/evm/src/cpu/kernel/asm/balance.asm b/evm/src/cpu/kernel/asm/balance.asm index 1c7fb400..1c6429d2 100644 --- a/evm/src/cpu/kernel/asm/balance.asm +++ b/evm/src/cpu/kernel/asm/balance.asm @@ -43,4 +43,4 @@ global sys_selfbalance: %address %jump(balance) %%after: -%endmacro \ No newline at end of file +%endmacro diff --git a/evm/src/cpu/kernel/asm/core/create.asm b/evm/src/cpu/kernel/asm/core/create.asm index cc37f832..d7e6eed2 100644 --- a/evm/src/cpu/kernel/asm/core/create.asm +++ b/evm/src/cpu/kernel/asm/core/create.asm @@ -1,3 +1,7 @@ +// TODO: This file needs to be cleaned up. +// `create` is no longer being used for contract-creation txns, +// so it can be inlined. Also need to set metadata on new ctx. + // The CREATE syscall. // // Pre stack: kexit_info, value, code_offset, code_len @@ -24,8 +28,8 @@ sys_create_finish: // Note: CODE_ADDR refers to a (context, segment, offset) tuple. global create: // stack: sender, endowment, CODE_ADDR, code_len, retdest - // TODO: Charge gas. - DUP1 %get_nonce + DUP1 %nonce + // stack: nonce, sender, endowment, CODE_ADDR, code_len, retdest // Call get_create_address and have it return to create_inner. %stack (nonce, sender) @@ -67,12 +71,14 @@ sys_create2_finish: // 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: +global 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: transfer_eth_status, sender, address, CODE_ADDR, code_len, retdest + %jumpi(fault_exception) // stack: sender, address, CODE_ADDR, code_len, retdest %increment_nonce diff --git a/evm/src/cpu/kernel/asm/core/create_addresses.asm b/evm/src/cpu/kernel/asm/core/create_addresses.asm index 67fd65a6..7dd4e889 100644 --- a/evm/src/cpu/kernel/asm/core/create_addresses.asm +++ b/evm/src/cpu/kernel/asm/core/create_addresses.asm @@ -5,14 +5,33 @@ // Post stack: address global get_create_address: // stack: sender, nonce, retdest - // TODO: Replace with actual implementation. - %pop2 - PUSH 123 + %alloc_rlp_block + // stack: rlp_start, sender, nonce, retdest + %stack (rlp_start, sender, nonce) -> (rlp_start, sender, nonce, rlp_start) + // stack: rlp_start, sender, nonce, rlp_start, retdest + %encode_rlp_160 // TODO: or encode_rlp_scalar? + // stack: rlp_pos, nonce, rlp_start, retdest + %encode_rlp_scalar + // stack: rlp_pos, rlp_start, retdest + %prepend_rlp_list_prefix + // stack: rlp_prefix_start, rlp_len, retdest + PUSH @SEGMENT_RLP_RAW + PUSH 0 // context + // stack: RLP_ADDR: 3, rlp_len, retdest + KECCAK_GENERAL + %mod_const(0x10000000000000000000000000000000000000000) // 2^160 // stack: address, retdest %observe_new_address SWAP1 JUMP +// Convenience macro to call get_create_address and return where we left off. +%macro get_create_address + %stack (sender, nonce) -> (sender, nonce, %%after) + %jump(get_create_address) +%%after: +%endmacro + // Computes the address for a contract based on the CREATE2 rule, i.e. // address = KEC(0xff || sender || salt || code_hash)[12:] // diff --git a/evm/src/cpu/kernel/asm/core/nonce.asm b/evm/src/cpu/kernel/asm/core/nonce.asm index 042d81f3..973a032d 100644 --- a/evm/src/cpu/kernel/asm/core/nonce.asm +++ b/evm/src/cpu/kernel/asm/core/nonce.asm @@ -1,17 +1,21 @@ -// Increment the nonce of the given account. +// Get the nonce of the given account. // Pre stack: address, retdest // Post stack: (empty) - -global get_nonce: +global nonce: // stack: address, retdest - // TODO: Replace with actual implementation. - POP - JUMP + %mpt_read_state_trie + // stack: account_ptr, retdest + // The nonce is the first account field, so we deref the account pointer itself. + // Note: We don't need to handle account_ptr=0, as trie_data[0] = 0, + // so the deref will give 0 (the default nonce) as desired. + %mload_trie_data + // stack: nonce, retdest + SWAP1 JUMP -// Convenience macro to call get_nonce and return where we left off. -%macro get_nonce +// Convenience macro to call nonce and return where we left off. +%macro nonce %stack (address) -> (address, %%after) - %jump(get_nonce) + %jump(nonce) %%after: %endmacro @@ -20,7 +24,7 @@ global increment_nonce: // stack: address, retdest %mpt_read_state_trie // stack: account_ptr, retdest - DUP1 ISZERO %jumpi(panic) + DUP1 ISZERO %jumpi(increment_nonce_no_such_account) // stack: nonce_ptr, retdest DUP1 %mload_trie_data // stack: nonce, nonce_ptr, retdest @@ -30,6 +34,8 @@ global increment_nonce: %mstore_trie_data // stack: retdest JUMP +global increment_nonce_no_such_account: + PANIC // Convenience macro to call increment_nonce and return where we left off. %macro increment_nonce diff --git a/evm/src/cpu/kernel/asm/core/process_txn.asm b/evm/src/cpu/kernel/asm/core/process_txn.asm index 9629d647..acce98f9 100644 --- a/evm/src/cpu/kernel/asm/core/process_txn.asm +++ b/evm/src/cpu/kernel/asm/core/process_txn.asm @@ -50,23 +50,76 @@ global process_based_on_type: global process_contract_creation_txn: // stack: retdest - PUSH process_contract_creation_txn_after_create - // stack: process_contract_creation_txn_after_create, retdest - %mload_txn_field(@TXN_FIELD_DATA_LEN) - // stack: code_len, process_contract_creation_txn_after_create, retdest - PUSH 0 - // stack: code_offset, code_len, process_contract_creation_txn_after_create, retdest - PUSH @SEGMENT_TXN_DATA - // stack: code_segment, code_offset, code_len, process_contract_creation_txn_after_create, retdest - PUSH 0 // context - // stack: CODE_ADDR, code_len, process_contract_creation_txn_after_create, retdest + + %mload_txn_field(@TXN_FIELD_ORIGIN) + // stack: origin, retdest + DUP1 %nonce + // stack: origin_nonce, origin, retdest + SWAP1 + // stack: origin, origin_nonce, retdest + %get_create_address + // stack: address, retdest + + // Deduct value from caller. %mload_txn_field(@TXN_FIELD_VALUE) %mload_txn_field(@TXN_FIELD_ORIGIN) - // stack: sender, endowment, CODE_ADDR, code_len, process_contract_creation_txn_after_create, retdest - %jump(create) + %deduct_eth + // stack: deduct_eth_status, address, retdest + %jumpi(panic) + // stack: address, retdest -global process_contract_creation_txn_after_create: - // stack: new_address, retdest + // Write the new account's data to MPT data, and get a pointer to it. + %get_trie_data_size + // stack: account_ptr, address, retdest + PUSH 1 %append_to_trie_data // nonce = 1 + // stack: account_ptr, address, retdest + DUP2 %balance %mload_txn_field(@TXN_FIELD_VALUE) ADD %append_to_trie_data // balance = old_balance + txn_value + // stack: account_ptr, address, retdest + PUSH 0 %append_to_trie_data // storage_root = nil + // stack: account_ptr, address, retdest + PUSH @EMPTY_STRING_HASH %append_to_trie_data // code_hash = keccak('') + // stack: account_ptr, address, retdest + DUP2 + // stack: address, account_ptr, address, retdest + %mpt_insert_state_trie + // stack: address, retdest + + %create_context + // stack: new_ctx, address, retdest + + // Copy the code from txdata to the new context's code segment. + PUSH process_contract_creation_txn_after_code_loaded + %mload_txn_field(@TXN_FIELD_DATA_LEN) + PUSH 0 // SRC.offset + PUSH @SEGMENT_TXN_DATA // SRC.segment + PUSH 0 // SRC.context + PUSH 0 // DST.offset + PUSH @SEGMENT_CODE // DST.segment + DUP7 // DST.context = new_ctx + %jump(memcpy) + +process_contract_creation_txn_after_code_loaded: + // stack: new_ctx, address, retdest + + // Each line in the block below does not change the stack. + DUP2 %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_contract_creation_txn_after_constructor) + %non_intrinisic_gas %set_new_ctx_gas_limit + // stack: new_ctx, address, retdest + + %enter_new_ctx + // (Old context) stack: new_ctx, address, retdest + +global process_contract_creation_txn_after_constructor: + // stack: success, leftover_gas, new_ctx, address, retdest + POP // TODO: Success will go into the receipt when we support that. + // stack: leftover_gas, new_ctx, address, retdest + %pay_coinbase_and_refund_sender + // stack: new_ctx, address, retdest + POP POP JUMP @@ -105,9 +158,8 @@ global process_message_txn_insufficient_balance: global process_message_txn_return: // stack: retdest - %mload_txn_field(@TXN_FIELD_INTRINSIC_GAS) - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - SUB + // Since no code was executed, the leftover gas is the non-intrinsic gas. + %non_intrinisic_gas // stack: leftover_gas, retdest %pay_coinbase_and_refund_sender // stack: retdest @@ -124,18 +176,13 @@ global process_message_txn_code_loaded: %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) - // stack: new_ctx, retdest - - // The gas provided to the callee is gas_limit - intrinsic_gas. - %mload_txn_field(@TXN_FIELD_INTRINSIC_GAS) - %mload_txn_field(@TXN_FIELD_GAS_LIMIT) - SUB - %set_new_ctx_gas_limit + %non_intrinisic_gas %set_new_ctx_gas_limit // stack: new_ctx, retdest // TODO: Copy TXN_DATA to CALLDATA %enter_new_ctx + // (Old context) stack: new_ctx, retdest global process_message_txn_after_call: // stack: success, leftover_gas, new_ctx, retdest @@ -206,3 +253,11 @@ global process_message_txn_after_call: %mstore_txn_field(@TXN_FIELD_COMPUTED_PRIORITY_FEE_PER_GAS) // stack: (empty) %endmacro + +%macro non_intrinisic_gas + // stack: (empty) + %mload_txn_field(@TXN_FIELD_INTRINSIC_GAS) + %mload_txn_field(@TXN_FIELD_GAS_LIMIT) + SUB + // stack: gas_limit - intrinsic_gas +%endmacro diff --git a/evm/src/cpu/kernel/asm/core/terminate.asm b/evm/src/cpu/kernel/asm/core/terminate.asm index 4a3fbf02..26fb113c 100644 --- a/evm/src/cpu/kernel/asm/core/terminate.asm +++ b/evm/src/cpu/kernel/asm/core/terminate.asm @@ -97,6 +97,15 @@ global terminate_common: %shr_const(192) // stack: gas_used %mload_context_metadata(@CTX_METADATA_GAS_LIMIT) + // stack: gas_limit, gas_used + SWAP1 + // stack: gas_used, gas_limit + DUP2 DUP2 LT + // stack: gas_used < gas_limit, gas_used, gas_limit + SWAP2 + // stack: gas_limit, gas_used, gas_used < gas_limit SUB - // stack: leftover_gas + // stack: gas_limit - gas_used, gas_used < gas_limit + MUL + // stack: leftover_gas = (gas_limit - gas_used) * (gas_used < gas_limit) %endmacro diff --git a/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm index 225f3cc0..60df845e 100644 --- a/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm +++ b/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm @@ -15,3 +15,9 @@ mpt_insert_state_trie_save: // stack: updated_node_ptr, retdest %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) JUMP + +%macro mpt_insert_state_trie + %stack (key, value_ptr) -> (key, value_ptr, %%after) + %jump(mpt_insert_state_trie) +%%after: +%endmacro diff --git a/evm/src/cpu/kernel/asm/transactions/type_0.asm b/evm/src/cpu/kernel/asm/transactions/type_0.asm index d00b10d4..45c57361 100644 --- a/evm/src/cpu/kernel/asm/transactions/type_0.asm +++ b/evm/src/cpu/kernel/asm/transactions/type_0.asm @@ -102,7 +102,7 @@ type_0_compute_signed_data: // stack: rlp_pos, rlp_start, retdest %mload_txn_field(@TXN_FIELD_TO) - SWAP1 %encode_rlp_160 + SWAP1 %encode_rlp_scalar // stack: rlp_pos, rlp_start, retdest %mload_txn_field(@TXN_FIELD_VALUE) diff --git a/evm/src/cpu/kernel/tests/core/create_addresses.rs b/evm/src/cpu/kernel/tests/core/create_addresses.rs index 047ddc00..03d780d8 100644 --- a/evm/src/cpu/kernel/tests/core/create_addresses.rs +++ b/evm/src/cpu/kernel/tests/core/create_addresses.rs @@ -1,4 +1,6 @@ use anyhow::Result; +use ethereum_types::U256; +use hex_literal::hex; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; @@ -7,11 +9,11 @@ use crate::cpu::kernel::interpreter::Interpreter; 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. + // This is copied from OpenEthereum's `test_contract_address`. let retaddr = 0xdeadbeefu32.into(); - let nonce = 5.into(); - let sender = 0.into(); - let expected_addr = 123.into(); + let nonce = 88.into(); + let sender = U256::from_big_endian(&hex!("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6")); + let expected_addr = U256::from_big_endian(&hex!("3f09c73a5ed19289fb9bdc72f1742566df146f56")); let initial_stack = vec![retaddr, nonce, sender]; let mut interpreter = Interpreter::new_with_kernel(get_create_address, initial_stack); diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index 9399e4b6..26a0ac81 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use ethereum_types::{Address, H160, H256, U256}; +use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; use keccak_hash::keccak; use plonky2::field::types::Field; @@ -72,10 +72,10 @@ impl GenerationState { pub fn jump_to(&mut self, dst: usize) { self.registers.program_counter = dst; if dst == KERNEL.global_labels["observe_new_address"] { - let address = stack_peek(self, 0).expect("Empty stack"); - let mut address_bytes = [0; 20]; - address.to_big_endian(&mut address_bytes); - self.observe_address(H160(address_bytes)); + let tip_u256 = stack_peek(self, 0).expect("Empty stack"); + let tip_h256 = H256::from_uint(&tip_u256); + let tip_h160 = H160::from(tip_h256); + self.observe_address(tip_h160); } }