diff --git a/evm/Cargo.toml b/evm/Cargo.toml index 3e5f3482..63f4ec0c 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -13,7 +13,7 @@ edition = "2021" anyhow = "1.0.40" blake2 = "0.10.5" env_logger = "0.10.0" -eth_trie_utils = "0.5.0" +eth_trie_utils = "0.6.0" ethereum-types = "0.14.0" hex = { version = "0.4.3", optional = true } hex-literal = "0.3.4" diff --git a/evm/src/cpu/kernel/asm/core/create.asm b/evm/src/cpu/kernel/asm/core/create.asm index 866e482c..6045454b 100644 --- a/evm/src/cpu/kernel/asm/core/create.asm +++ b/evm/src/cpu/kernel/asm/core/create.asm @@ -5,8 +5,13 @@ // Post stack: address global sys_create: %check_static + + %stack (kexit_info, value, code_offset, code_len) -> (code_len, code_offset, kexit_info, value, code_offset, code_len) + %checked_mem_expansion // stack: kexit_info, value, code_offset, code_len - // TODO: Charge gas. + %charge_gas_const(@GAS_CREATE) + // TODO: If using EIP-3860, we should limit and charge gas on `code_len`. + %stack (kexit_info, value, code_offset, code_len) -> (sys_create_got_address, value, code_offset, code_len, kexit_info) %address @@ -27,8 +32,15 @@ sys_create_got_address: // Post stack: address global sys_create2: %check_static + // stack: kexit_info, value, code_offset, code_len, salt - // TODO: Charge gas. + %stack (kexit_info, value, code_offset, code_len) -> (code_len, code_offset, kexit_info, value, code_offset, code_len) + %checked_mem_expansion + // stack: kexit_info, value, code_offset, code_len, salt + DUP4 %num_bytes_to_num_words + %mul_const(@GAS_KECCAK256WORD) %add_const(@GAS_CREATE) %charge_gas + // TODO: If using EIP-3860, we should limit and charge gas on `code_len`. + SWAP4 %stack (salt) -> (salt, create_common) // stack: salt, create_common, value, code_offset, code_len, kexit_info @@ -78,7 +90,10 @@ global create_common: GET_CONTEXT // stack: src_ctx, new_ctx, address, value, code_offset, code_len, kexit_info - // Copy the code from txdata to the new context's code segment. + %stack (src_ctx, new_ctx, address, value, code_offset, code_len) -> + (code_len, new_ctx, src_ctx, new_ctx, address, value, code_offset, code_len) + %set_new_ctx_code_size POP + // Copy the code from memory to the new context's code segment. %stack (src_ctx, new_ctx, address, value, code_offset, code_len) -> (new_ctx, @SEGMENT_CODE, 0, // DST src_ctx, @SEGMENT_MAIN_MEMORY, code_offset, // SRC @@ -113,7 +128,35 @@ after_constructor: // stack: success, leftover_gas, new_ctx, address, kexit_info SWAP2 // stack: new_ctx, leftover_gas, success, address, kexit_info - POP // TODO: Ignoring new_ctx for now, but we will need it to store code that was returned, if any. + POP + + // TODO: EIP-3541: Reject new contract code starting with the 0xEF byte + + // TODO: Skip blocks below if success is false. + // Charge gas for the code size. + SWAP3 + // stack: kexit_info, success, address, leftover_gas + %returndatasize // Size of the code. + // stack: code_size, kexit_info, success, address, leftover_gas + DUP1 %gt_const(@MAX_CODE_SIZE) + %jumpi(fault_exception) + // stack: code_size, kexit_info, success, address, leftover_gas + %mul_const(@GAS_CODEDEPOSIT) %charge_gas + SWAP3 + + // Store the code hash of the new contract. + GET_CONTEXT + %returndatasize + %stack (size, ctx) -> (ctx, @SEGMENT_RETURNDATA, 0, size) // context, segment, offset, len + KECCAK_GENERAL + // stack: codehash, leftover_gas, success, address, kexit_info + DUP4 + // stack: address, codehash, leftover_gas, success, address, kexit_info + %set_codehash + + // Set the return data size to 0. + %mstore_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0) + // stack: leftover_gas, success, address, kexit_info %shl_const(192) // stack: leftover_gas << 192, success, address, kexit_info @@ -123,6 +166,26 @@ after_constructor: // stack: address_if_success, leftover_gas << 192, kexit_info SWAP2 // stack: kexit_info, leftover_gas << 192, address_if_success - ADD + SUB // stack: kexit_info, address_if_success EXIT_KERNEL + +%macro set_codehash + %stack (addr, codehash) -> (addr, codehash, %%after) + %jump(set_codehash) +%%after: + // stack: (empty) +%endmacro + +// Pre stack: addr, codehash, redest +// Post stack: (empty) +// TODO: Should it be copy-on-write (with make_account_copy) instead of mutating the trie? +set_codehash: + // stack: addr, codehash, retdest + %mpt_read_state_trie + // stack: account_ptr, codehash, retdest + %add_const(3) + // stack: codehash_ptr, codehash, retdest + %mstore_trie_data + // stack: retdest + JUMP diff --git a/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm b/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm index ca8bbb5a..ee036355 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm @@ -1,7 +1,7 @@ global precompile_bn_add: - // stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size + // stack: address, retdest, new_ctx, (old stack) %pop2 - // stack: new_ctx, kexit_info, ret_offset, ret_size + // stack: new_ctx, (old stack) DUP1 SET_CONTEXT // stack: (empty) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm b/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm index 554849ab..d83597ba 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm @@ -1,7 +1,7 @@ global precompile_bn_mul: - // stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size + // stack: address, retdest, new_ctx, (old stack) %pop2 - // stack: new_ctx, kexit_info, ret_offset, ret_size + // stack: new_ctx, (old stack) DUP1 SET_CONTEXT // stack: (empty) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm b/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm index e4a03a99..f19b57be 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm @@ -1,7 +1,7 @@ global precompile_ecrec: - // stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size + // stack: address, retdest, new_ctx, (old stack) %pop2 - // stack: new_ctx, kexit_info, ret_offset, ret_size + // stack: new_ctx, (old stack) DUP1 SET_CONTEXT // stack: (empty) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/id.asm b/evm/src/cpu/kernel/asm/core/precompiles/id.asm index 167d99f5..7cfb6dfc 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/id.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/id.asm @@ -1,7 +1,7 @@ global precompile_id: - // stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size + // stack: address, retdest, new_ctx, (old stack) %pop2 - // stack: new_ctx, kexit_info, ret_offset, ret_size + // stack: new_ctx, (old stack) DUP1 SET_CONTEXT // stack: (empty) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/main.asm b/evm/src/cpu/kernel/asm/core/precompiles/main.asm index edf53a13..df9b77ec 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/main.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/main.asm @@ -1,16 +1,16 @@ %macro handle_precompiles - // stack: address, new_ctx, kexit_info, ret_offset, ret_size + // stack: address, new_ctx, (old stack) PUSH %%after SWAP1 - // stack: address, %%after, new_ctx, kexit_info, ret_offset, ret_size + // stack: address, %%after, new_ctx, (old stack) %jump(handle_precompiles) %%after: - // stack: new_ctx, kexit_info, ret_offset, ret_size + // stack: new_ctx, (old stack) %pop4 %endmacro global handle_precompiles: - // stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size + // stack: address, retdest, new_ctx, (old stack) DUP1 %eq_const(@ECREC) %jumpi(precompile_ecrec) DUP1 %eq_const(@SHA256) %jumpi(precompile_sha256) DUP1 %eq_const(@RIP160) %jumpi(precompile_rip160) @@ -31,6 +31,38 @@ global pop_and_return_success: PUSH 1 // success %jump(terminate_common) +%macro handle_precompiles_from_eoa + // stack: retdest + %mload_txn_field(@TXN_FIELD_TO) + // stack: addr, retdest + DUP1 %ge_const(@ECREC) DUP2 %le_const(@BLAKE2_F) + // stack: addr<=9, addr>=1, addr, retdest + MUL // Cheaper than AND + %jumpi(handle_precompiles_from_eoa) + // stack: addr, retdest + POP +%endmacro + +global handle_precompiles_from_eoa: + // stack: addr, retdest + %create_context + // stack: new_ctx, addr, retdest + %set_new_ctx_parent_pc(process_message_txn_after_call) + %non_intrinisic_gas %set_new_ctx_gas_limit + // stack: new_ctx, addr, retdest + + // Set calldatasize and copy txn data to calldata. + %mload_txn_field(@TXN_FIELD_DATA_LEN) + %stack (calldata_size, new_ctx) -> (calldata_size, new_ctx, calldata_size) + %set_new_ctx_calldata_size + %stack (new_ctx, calldata_size) -> (new_ctx, @SEGMENT_CALLDATA, 0, 0, @SEGMENT_TXN_DATA, 0, calldata_size, handle_precompiles_from_eoa_finish, new_ctx) + %jump(memcpy) + +handle_precompiles_from_eoa_finish: + %stack (new_ctx, addr, retdest) -> (addr, new_ctx, retdest) + %handle_precompiles + PANIC // We already checked that a precompile is called, so this should be unreachable. + %macro zero_out_kernel_general PUSH 0 PUSH 0 %mstore_kernel_general PUSH 0 PUSH 1 %mstore_kernel_general diff --git a/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm b/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm index 2f7b2c32..70711234 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm @@ -1,7 +1,7 @@ global precompile_rip160: - // stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size + // stack: address, retdest, new_ctx, (old stack) %pop2 - // stack: new_ctx, kexit_info, ret_offset, ret_size + // stack: new_ctx, (old stack) DUP1 SET_CONTEXT // stack: (empty) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm b/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm index 86461a59..9ed59d8b 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm @@ -1,7 +1,7 @@ global precompile_sha256: - // stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size + // stack: address, retdest, new_ctx, (old stack) %pop2 - // stack: new_ctx, kexit_info, ret_offset, ret_size + // stack: new_ctx, (old stack) DUP1 SET_CONTEXT // stack: (empty) diff --git a/evm/src/cpu/kernel/asm/core/process_txn.asm b/evm/src/cpu/kernel/asm/core/process_txn.asm index a626cd39..6c466749 100644 --- a/evm/src/cpu/kernel/asm/core/process_txn.asm +++ b/evm/src/cpu/kernel/asm/core/process_txn.asm @@ -145,6 +145,16 @@ 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 + %returndatasize // Size of the code. + // stack: code_size, leftover_gas, new_ctx, address, retdest + DUP1 %gt_const(@MAX_CODE_SIZE) %jumpi(panic) // TODO: need to revert changes here. + // stack: code_size, leftover_gas, new_ctx, address, retdest + %mul_const(@GAS_CODEDEPOSIT) SWAP1 + // stack: leftover_gas, codedeposit_cost, new_ctx, address, retdest + DUP2 DUP2 LT %jumpi(panic) // TODO: need to revert changes here. + // stack: leftover_gas, codedeposit_cost, new_ctx, address, retdest + SUB + // stack: leftover_gas, new_ctx, address, retdest %pay_coinbase_and_refund_sender // TODO: Delete accounts in self-destruct list and empty touched addresses. // stack: new_ctx, address, retdest @@ -165,6 +175,13 @@ global process_message_txn: %jumpi(process_message_txn_insufficient_balance) // stack: retdest + %handle_precompiles_from_eoa + + // If to's code is empty, return. + %mload_txn_field(@TXN_FIELD_TO) %ext_code_empty + // stack: code_empty, retdest + %jumpi(process_message_txn_return) + // Add precompiles to accessed addresses. PUSH @ECREC %insert_accessed_addresses_no_return PUSH @SHA256 %insert_accessed_addresses_no_return @@ -175,12 +192,6 @@ global process_message_txn: PUSH @BN_MUL %insert_accessed_addresses_no_return PUSH @SNARKV %insert_accessed_addresses_no_return PUSH @BLAKE2_F %insert_accessed_addresses_no_return - // TODO: Handle precompiles. - - // If to's code is empty, return. - %mload_txn_field(@TXN_FIELD_TO) %ext_code_empty - // stack: code_empty, retdest - %jumpi(process_message_txn_return) // Otherwise, load to's code and execute it in a new context. // stack: retdest diff --git a/evm/src/cpu/kernel/asm/memory/metadata.asm b/evm/src/cpu/kernel/asm/memory/metadata.asm index c158718b..42a88957 100644 --- a/evm/src/cpu/kernel/asm/memory/metadata.asm +++ b/evm/src/cpu/kernel/asm/memory/metadata.asm @@ -34,6 +34,15 @@ // stack: (empty) %endmacro +// Store the given context metadata field to memory. +%macro mstore_context_metadata(field, value) + PUSH $value + PUSH $field + // stack: offset, value + %mstore_current(@SEGMENT_CONTEXT_METADATA) + // stack: (empty) +%endmacro + %macro mstore_parent_context_metadata(field) // stack: value %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index 0f64bcc9..f25032ff 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -40,6 +40,10 @@ pub fn evm_constants() -> HashMap { c.insert(name.into(), U256::from(value)); } + for (name, value) in CODE_SIZE_LIMIT { + c.insert(name.into(), U256::from(value)); + } + for segment in Segment::all() { c.insert(segment.var_name().into(), (segment as u32).into()); } @@ -235,3 +239,9 @@ const PRECOMPILES_GAS: [(&str, u16); 13] = [ ("SNARKV_DYNAMIC_GAS", 34_000), ("BLAKE2_F_DYNAMIC_GAS", 1), ]; + +const CODE_SIZE_LIMIT: [(&str, u64); 3] = [ + ("MAX_CODE_SIZE", 0x6000), + ("MAX_INITCODE_SIZE", 0xc000), + ("INITCODE_WORD_COST", 2), +];