diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 09c7d576..a8c83e4f 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -141,8 +141,6 @@ impl, const D: usize> Stark for CpuStark, const D: usize> Stark for CpuStark, const D: usize> Stark for CpuStark, const D: usize> Stark for CpuStark Kernel { include_str!("asm/core/util.asm"), include_str!("asm/core/access_lists.asm"), include_str!("asm/core/selfdestruct_list.asm"), + include_str!("asm/core/precompiles/main.asm"), + include_str!("asm/core/precompiles/ecrec.asm"), + include_str!("asm/core/precompiles/sha256.asm"), + include_str!("asm/core/precompiles/rip160.asm"), + include_str!("asm/core/precompiles/id.asm"), + include_str!("asm/core/precompiles/expmod.asm"), + include_str!("asm/core/precompiles/bn_add.asm"), + include_str!("asm/core/precompiles/bn_mul.asm"), + include_str!("asm/core/precompiles/snarkv.asm"), + include_str!("asm/core/precompiles/blake2_f.asm"), include_str!("asm/curve/bls381/util.asm"), include_str!("asm/curve/bn254/curve_arithmetic/constants.asm"), include_str!("asm/curve/bn254/curve_arithmetic/curve_add.asm"), diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index e181246a..e9e9d13b 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -2,7 +2,15 @@ // Creates a new sub context and executes the code of the given account. global sys_call: + // Check that the value is zero if the context is static. // stack: kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size + DUP4 ISZERO %not_bit + // stack: value≠0, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size + %mload_context_metadata(@CTX_METADATA_STATIC) + // stack: is_static, value≠0, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size + MUL // Cheaper than AND + %jumpi(fault_exception) + SWAP2 // stack: address, gas, kexit_info, value, args_offset, args_size, ret_offset, ret_size %u256_to_addr // Truncate to 160 bits @@ -21,19 +29,24 @@ global sys_call: // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size // TODO: Consider call depth + %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> + (new_ctx, args_offset, args_size, new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) + %copy_mem_to_calldata + // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size + DUP5 DUP5 %address %transfer_eth %jumpi(panic) // TODO: Fix this panic. + DUP3 %set_new_ctx_gas_limit + %set_new_ctx_parent_pc(after_call_instruction) + DUP9 DUP9 DUP4 DUP4 DUP8 // Duplicate address, new_ctx, kexit_info, ret_offset, and ret_size. + // stack: address, new_ctx, kexit_info, ret_offset, ret_size, ... + %handle_precompiles + // stack: new_ctx, kexit_info, callgas, 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 %jumpi(panic) // TODO: Fix this panic. - %set_new_ctx_parent_pc(after_call_instruction) - DUP3 %set_new_ctx_gas_limit DUP4 %set_new_ctx_code - %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> - (new_ctx, args_offset, args_size, new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) - %copy_mem_to_calldata - // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> (new_ctx, kexit_info, ret_offset, ret_size) @@ -43,25 +56,44 @@ global sys_call: // given account. In particular the storage remains the same. global sys_callcode: // stack: kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size - // TODO: Charge gas. SWAP2 // stack: address, gas, kexit_info, value, args_offset, args_size, ret_offset, ret_size %u256_to_addr // Truncate to 160 bits - DUP1 %insert_accessed_addresses POP // TODO: Use return value in gas calculation. - SWAP2 - // stack: kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size + DUP1 %insert_accessed_addresses + + %call_charge_gas + + %stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> + (args_size, args_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) + %checked_mem_expansion + %stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> + (ret_size, ret_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) + %checked_mem_expansion + + // stack: kexit_info, callgas, 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 + // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size + + %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> + (new_ctx, args_offset, args_size, new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) + %copy_mem_to_calldata + // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size + DUP3 %set_new_ctx_gas_limit + %set_new_ctx_parent_pc(after_call_instruction) + DUP9 DUP9 DUP4 DUP4 DUP8 // Duplicate address, new_ctx, kexit_info, ret_offset, and ret_size. + // stack: address, new_ctx, kexit_info, ret_offset, ret_size, ... + %handle_precompiles + // stack: new_ctx, kexit_info, callgas, 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 %jumpi(panic) // TODO: Fix this panic. - %set_new_ctx_parent_pc(after_call_instruction) + DUP4 %set_new_ctx_code - // 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) + + // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size + %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> (new_ctx, kexit_info, ret_offset, ret_size) %enter_new_ctx @@ -72,24 +104,46 @@ global sys_callcode: // CALL if the value sent is not 0. global sys_staticcall: // stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size - // TODO: Charge gas. SWAP2 // stack: address, gas, kexit_info, args_offset, args_size, ret_offset, ret_size %u256_to_addr // Truncate to 160 bits - DUP1 %insert_accessed_addresses POP // TODO: Use return value in gas calculation. - SWAP2 - // stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size + DUP1 %insert_accessed_addresses + + // Add a value of 0 to the stack. Slightly inefficient but that way we can reuse %call_charge_gas. + %stack (cold_access, address, gas, kexit_info) -> (cold_access, address, gas, kexit_info, 0) + %call_charge_gas + + %stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> + (args_size, args_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) + %checked_mem_expansion + %stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> + (ret_size, ret_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) + %checked_mem_expansion + + // stack: kexit_info, callgas, address, value, 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 + // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size + + %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> + (new_ctx, args_offset, args_size, new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) + %copy_mem_to_calldata + // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size + DUP3 %set_new_ctx_gas_limit + %set_new_ctx_parent_pc(after_call_instruction) + DUP9 DUP9 DUP4 DUP4 DUP8 // Duplicate address, new_ctx, kexit_info, ret_offset, and ret_size. + // stack: address, new_ctx, kexit_info, ret_offset, ret_size, ... + %handle_precompiles + // stack: new_ctx, kexit_info, callgas, address, value, 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_pc(after_call_instruction) + DUP4 %set_new_ctx_code - %stack (new_ctx, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) + + %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> (new_ctx, kexit_info, ret_offset, ret_size) %enter_new_ctx @@ -98,23 +152,45 @@ global sys_staticcall: // value remain the same. global sys_delegatecall: // stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size - // TODO: Charge gas. SWAP2 // stack: address, gas, kexit_info, args_offset, args_size, ret_offset, ret_size %u256_to_addr // Truncate to 160 bits - DUP1 %insert_accessed_addresses POP // TODO: Use return value in gas calculation. - SWAP2 - // stack: kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size + DUP1 %insert_accessed_addresses + + // Add a value of 0 to the stack. Slightly inefficient but that way we can reuse %call_charge_gas. + %stack (cold_access, address, gas, kexit_info) -> (cold_access, address, gas, kexit_info, 0) + %call_charge_gas + + %stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> + (args_size, args_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) + %checked_mem_expansion + %stack (kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> + (ret_size, ret_offset, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) + %checked_mem_expansion + + // stack: kexit_info, callgas, address, value, 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 + // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size + + %stack (new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) -> + (new_ctx, args_offset, args_size, new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size) + %copy_mem_to_calldata + // stack: new_ctx, kexit_info, callgas, address, value, args_offset, args_size, ret_offset, ret_size + DUP3 %set_new_ctx_gas_limit + %set_new_ctx_parent_pc(after_call_instruction) + DUP9 DUP9 DUP4 DUP4 DUP8 // Duplicate address, new_ctx, kexit_info, ret_offset, and ret_size. + // stack: address, new_ctx, kexit_info, ret_offset, ret_size, ... + %handle_precompiles + // stack: new_ctx, kexit_info, callgas, 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 %caller %set_new_ctx_caller %callvalue %set_new_ctx_value %set_new_ctx_parent_pc(after_call_instruction) + DUP4 %set_new_ctx_code - %stack (new_ctx, kexit_info, gas, address, args_offset, args_size, ret_offset, ret_size) + %stack (new_ctx, kexit_info, callgas, address, args_offset, args_size, ret_offset, ret_size) -> (new_ctx, kexit_info, ret_offset, ret_size) %enter_new_ctx @@ -210,9 +286,7 @@ global after_call_instruction: %stack (address, new_ctx) -> (address, new_ctx, @SEGMENT_CODE, %%after, new_ctx) %jump(load_code) %%after: - %stack (code_size, new_ctx) - -> (new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CODE_SIZE, code_size, new_ctx) - MSTORE_GENERAL + %set_new_ctx_code_size // stack: new_ctx %endmacro @@ -246,12 +320,16 @@ global after_call_instruction: %macro copy_returndata_to_mem // stack: kexit_info, new_ctx, success, ret_offset, ret_size + SWAP4 + %returndatasize + // stack: returndata_size, ret_size, new_ctx, success, ret_offset, kexit_info + %min GET_CONTEXT - %stack (ctx, kexit_info, new_ctx, success, ret_offset, ret_size) -> + %stack (ctx, n, new_ctx, success, ret_offset, kexit_info) -> ( ctx, @SEGMENT_MAIN_MEMORY, ret_offset, // DST ctx, @SEGMENT_RETURNDATA, 0, // SRC - ret_size, %%after, // count, retdest + n, %%after, // count, retdest kexit_info, success ) %jump(memcpy) @@ -262,13 +340,13 @@ global after_call_instruction: // Doesn't include memory expansion costs. %macro call_charge_gas // Compute C_aaccess - // stack: cold_access, address, gas, kexit_info, value, args_offset, args_size, ret_offset, ret_size + // stack: cold_access, address, gas, kexit_info, value %mul_const(@GAS_COLDACCOUNTACCESS_MINUS_WARMACCESS) %add_const(@GAS_WARMACCESS) // Compute C_xfer // stack: Caaccess, address, gas, kexit_info, value - DUP5 ISZERO PUSH 1 SUB + DUP5 ISZERO %not_bit // stack: value≠0, Caaccess, address, gas, kexit_info, value DUP1 %mul_const(@GAS_CALLVALUE) @@ -293,7 +371,7 @@ global after_call_instruction: // stack: leftover_gas=Cextra, (leftover_gas=Cextra, (leftover_gas (Cgascap, address, gas, kexit_info, value) - DUP5 ISZERO PUSH 1 SUB + DUP5 ISZERO %not_bit // stack: value!=0, Cgascap, address, gas, kexit_info, value %mul_const(@GAS_CALLSTIPEND) ADD %stack (C_callgas, address, gas, kexit_info, value) -> diff --git a/evm/src/cpu/kernel/asm/core/create.asm b/evm/src/cpu/kernel/asm/core/create.asm index 3cc457d9..866e482c 100644 --- a/evm/src/cpu/kernel/asm/core/create.asm +++ b/evm/src/cpu/kernel/asm/core/create.asm @@ -4,6 +4,7 @@ // Pre stack: kexit_info, value, code_offset, code_len // Post stack: address global sys_create: + %check_static // stack: kexit_info, value, code_offset, code_len // TODO: Charge gas. %stack (kexit_info, value, code_offset, code_len) @@ -25,6 +26,7 @@ sys_create_got_address: // Pre stack: kexit_info, value, code_offset, code_len, salt // Post stack: address global sys_create2: + %check_static // stack: kexit_info, value, code_offset, code_len, salt // TODO: Charge gas. SWAP4 diff --git a/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm b/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm new file mode 100644 index 00000000..a10c9ba2 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm @@ -0,0 +1,3 @@ +global precompile_blake2_f: + // TODO + PANIC diff --git a/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm b/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm new file mode 100644 index 00000000..ca8bbb5a --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm @@ -0,0 +1,48 @@ +global precompile_bn_add: + // stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size + %pop2 + // stack: new_ctx, kexit_info, ret_offset, ret_size + DUP1 + SET_CONTEXT + // stack: (empty) + PUSH 0x100000000 // = 2^32 (is_kernel = true) + // stack: kexit_info + + %charge_gas_const(@BN_ADD_GAS) + + // Load x0, y0, x1, y1 from the call data using `mload_packing`. + GET_CONTEXT + %stack (ctx, kexit_info) -> (ctx, @SEGMENT_CALLDATA, 96, 32, bn_add_contd, kexit_info) + %jump(mload_packing) +bn_add_contd: + GET_CONTEXT + %stack (ctx, y1, kexit_info) -> (ctx, @SEGMENT_CALLDATA, 64, 32, bn_add_contd2, y1, kexit_info) + %jump(mload_packing) +bn_add_contd2: + GET_CONTEXT + %stack (ctx, x1, y1, kexit_info) -> (ctx, @SEGMENT_CALLDATA, 32, 32, bn_add_contd3, x1, y1, kexit_info) + %jump(mload_packing) +bn_add_contd3: + GET_CONTEXT + %stack (ctx, y0, x1, y1, kexit_info) -> (ctx, @SEGMENT_CALLDATA, 0, 32, bn_add_contd4, y0, x1, y1, kexit_info) + %jump(mload_packing) +bn_add_contd4: + %stack (x0, y0, x1, y1, kexit_info) -> (x0, y0, x1, y1, bn_add_contd5, kexit_info) + %jump(bn_add) +bn_add_contd5: + // stack: x, y, kexit_info + DUP2 %eq_const(@U256_MAX) // bn_add returns (U256_MAX, U256_MAX) on bad input. + DUP2 %eq_const(@U256_MAX) // bn_add returns (U256_MAX, U256_MAX) on bad input. + MUL // Cheaper than AND + %jumpi(fault_exception) + // stack: x, y, kexit_info + + // Store the result (x, y) to the parent's return data using `mstore_unpacking`. + %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 64) + %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) + %stack (parent_ctx, x, y) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, x, 32, bn_add_contd6, parent_ctx, y) + %jump(mstore_unpacking) +bn_add_contd6: + POP + %stack (parent_ctx, y) -> (parent_ctx, @SEGMENT_RETURNDATA, 32, y, 32, pop_and_return_success) + %jump(mstore_unpacking) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm b/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm new file mode 100644 index 00000000..554849ab --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm @@ -0,0 +1,44 @@ +global precompile_bn_mul: + // stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size + %pop2 + // stack: new_ctx, kexit_info, ret_offset, ret_size + DUP1 + SET_CONTEXT + // stack: (empty) + PUSH 0x100000000 // = 2^32 (is_kernel = true) + // stack: kexit_info + + %charge_gas_const(@BN_MUL_GAS) + + // Load x, y, n from the call data using `mload_packing`. + GET_CONTEXT + %stack (ctx, kexit_info) -> (ctx, @SEGMENT_CALLDATA, 64, 32, bn_mul_contd, kexit_info) + %jump(mload_packing) +bn_mul_contd: + GET_CONTEXT + %stack (ctx, n, kexit_info) -> (ctx, @SEGMENT_CALLDATA, 32, 32, bn_mul_contd2, n, kexit_info) + %jump(mload_packing) +bn_mul_contd2: + GET_CONTEXT + %stack (ctx, y, n, kexit_info) -> (ctx, @SEGMENT_CALLDATA, 0, 32, bn_mul_contd3, y, n, kexit_info) + %jump(mload_packing) +bn_mul_contd3: + %stack (x, y, n, kexit_info) -> (x, y, n, bn_mul_contd4, kexit_info) + %jump(bn_mul) +bn_mul_contd4: + // stack: Px, Py, kexit_info + DUP2 %eq_const(@U256_MAX) // bn_mul returns (U256_MAX, U256_MAX) on bad input. + DUP2 %eq_const(@U256_MAX) // bn_mul returns (U256_MAX, U256_MAX) on bad input. + MUL // Cheaper than AND + %jumpi(fault_exception) + // stack: Px, Py, kexit_info + + // Store the result (Px, Py) to the parent's return data using `mstore_unpacking`. + %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 64) + %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) + %stack (parent_ctx, Px, Py) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, Px, 32, bn_mul_contd6, parent_ctx, Py) + %jump(mstore_unpacking) +bn_mul_contd6: + POP + %stack (parent_ctx, Py) -> (parent_ctx, @SEGMENT_RETURNDATA, 32, Py, 32, pop_and_return_success) + %jump(mstore_unpacking) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm b/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm new file mode 100644 index 00000000..e4a03a99 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm @@ -0,0 +1,45 @@ +global precompile_ecrec: + // stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size + %pop2 + // stack: new_ctx, kexit_info, ret_offset, ret_size + DUP1 + SET_CONTEXT + // stack: (empty) + PUSH 0x100000000 // = 2^32 (is_kernel = true) + // stack: kexit_info + + %charge_gas_const(@ECREC_GAS) + + // Load hash, v, r, s from the call data using `mload_packing`. + GET_CONTEXT + %stack (ctx, kexit_info) -> (ctx, @SEGMENT_CALLDATA, 96, 32, ecrec_contd, kexit_info) + %jump(mload_packing) +ecrec_contd: + GET_CONTEXT + %stack (ctx, s, kexit_info) -> (ctx, @SEGMENT_CALLDATA, 64, 32, ecrec_contd2, s, kexit_info) + %jump(mload_packing) +ecrec_contd2: + GET_CONTEXT + %stack (ctx, r, s, kexit_info) -> (ctx, @SEGMENT_CALLDATA, 32, 32, ecrec_contd3, r, s, kexit_info) + %jump(mload_packing) +ecrec_contd3: + GET_CONTEXT + %stack (ctx, v, r, s, kexit_info) -> (ctx, @SEGMENT_CALLDATA, 0, 32, ecrec_contd4, v, r, s, kexit_info) + %jump(mload_packing) +ecrec_contd4: + %stack (hash, v, r, s, kexit_info) -> (hash, v, r, s, ecrec_contd5, kexit_info) + %jump(ecrecover) +ecrec_contd5: + // stack: address, kexit_info + DUP1 %eq_const(@U256_MAX) %jumpi(ecrec_bad_input) // ecrecover returns U256_MAX on bad input. + + // Store the result address to the parent's return data using `mstore_unpacking`. + %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) + %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) + %stack (parent_ctx, address) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, address, 32, pop_and_return_success) + %jump(mstore_unpacking) + +// On bad input, return empty return data but still return success. +ecrec_bad_input: + %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0) + %jump(pop_and_return_success) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm b/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm new file mode 100644 index 00000000..51905cab --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm @@ -0,0 +1,3 @@ +global precompile_expmod: + // TODO + PANIC diff --git a/evm/src/cpu/kernel/asm/core/precompiles/id.asm b/evm/src/cpu/kernel/asm/core/precompiles/id.asm new file mode 100644 index 00000000..167d99f5 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/precompiles/id.asm @@ -0,0 +1,38 @@ +global precompile_id: + // stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size + %pop2 + // stack: new_ctx, kexit_info, ret_offset, ret_size + DUP1 + SET_CONTEXT + // stack: (empty) + PUSH 0x100000000 // = 2^32 (is_kernel = true) + // stack: kexit_info + + %calldatasize + %num_bytes_to_num_words + // stack: data_words_len, kexit_info + %mul_const(@ID_DYNAMIC_GAS) + PUSH @ID_STATIC_GAS + ADD + // stack: gas, kexit_info + %charge_gas + + // Simply copy the call data to the parent's return data. + %calldatasize + DUP1 %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE) + GET_CONTEXT + %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) + %stack (parent_ctx, ctx, size) -> + ( + parent_ctx, @SEGMENT_RETURNDATA, 0, // DST + ctx, @SEGMENT_CALLDATA, 0, // SRC + size, id_contd // count, retdest + ) + %jump(memcpy) + +id_contd: + // stack: kexit_info + %leftover_gas + // stack: leftover_gas + PUSH 1 // success + %jump(terminate_common) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/main.asm b/evm/src/cpu/kernel/asm/core/precompiles/main.asm new file mode 100644 index 00000000..edf53a13 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/precompiles/main.asm @@ -0,0 +1,135 @@ +%macro handle_precompiles + // stack: address, new_ctx, kexit_info, ret_offset, ret_size + PUSH %%after + SWAP1 + // stack: address, %%after, new_ctx, kexit_info, ret_offset, ret_size + %jump(handle_precompiles) +%%after: + // stack: new_ctx, kexit_info, ret_offset, ret_size + %pop4 +%endmacro + +global handle_precompiles: + // stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size + DUP1 %eq_const(@ECREC) %jumpi(precompile_ecrec) + DUP1 %eq_const(@SHA256) %jumpi(precompile_sha256) + DUP1 %eq_const(@RIP160) %jumpi(precompile_rip160) + DUP1 %eq_const(@ID) %jumpi(precompile_id) + DUP1 %eq_const(@EXPMOD) %jumpi(precompile_expmod) + DUP1 %eq_const(@BN_ADD) %jumpi(precompile_bn_add) + DUP1 %eq_const(@BN_MUL) %jumpi(precompile_bn_mul) + DUP1 %eq_const(@SNARKV) %jumpi(precompile_snarkv) + %eq_const(@BLAKE2_F) %jumpi(precompile_blake2_f) + // stack: retdest + JUMP + +global pop_and_return_success: + // stack: _unused, kexit_info + POP + %leftover_gas + // stack: leftover_gas + PUSH 1 // success + %jump(terminate_common) + +%macro zero_out_kernel_general + PUSH 0 PUSH 0 %mstore_kernel_general + PUSH 0 PUSH 1 %mstore_kernel_general + PUSH 0 PUSH 2 %mstore_kernel_general + PUSH 0 PUSH 3 %mstore_kernel_general + PUSH 0 PUSH 4 %mstore_kernel_general + PUSH 0 PUSH 5 %mstore_kernel_general + PUSH 0 PUSH 6 %mstore_kernel_general + PUSH 0 PUSH 7 %mstore_kernel_general + PUSH 0 PUSH 8 %mstore_kernel_general + PUSH 0 PUSH 9 %mstore_kernel_general + PUSH 0 PUSH 10 %mstore_kernel_general + PUSH 0 PUSH 11 %mstore_kernel_general + PUSH 0 PUSH 12 %mstore_kernel_general + PUSH 0 PUSH 13 %mstore_kernel_general + PUSH 0 PUSH 14 %mstore_kernel_general + PUSH 0 PUSH 15 %mstore_kernel_general + PUSH 0 PUSH 16 %mstore_kernel_general + PUSH 0 PUSH 17 %mstore_kernel_general + PUSH 0 PUSH 18 %mstore_kernel_general + PUSH 0 PUSH 19 %mstore_kernel_general + PUSH 0 PUSH 20 %mstore_kernel_general + PUSH 0 PUSH 21 %mstore_kernel_general + PUSH 0 PUSH 22 %mstore_kernel_general + PUSH 0 PUSH 23 %mstore_kernel_general + PUSH 0 PUSH 24 %mstore_kernel_general + PUSH 0 PUSH 25 %mstore_kernel_general + PUSH 0 PUSH 26 %mstore_kernel_general + PUSH 0 PUSH 27 %mstore_kernel_general + PUSH 0 PUSH 28 %mstore_kernel_general + PUSH 0 PUSH 29 %mstore_kernel_general + PUSH 0 PUSH 30 %mstore_kernel_general + PUSH 0 PUSH 31 %mstore_kernel_general + PUSH 0 PUSH 32 %mstore_kernel_general + PUSH 0 PUSH 33 %mstore_kernel_general + PUSH 0 PUSH 34 %mstore_kernel_general + PUSH 0 PUSH 35 %mstore_kernel_general + PUSH 0 PUSH 36 %mstore_kernel_general + PUSH 0 PUSH 37 %mstore_kernel_general + PUSH 0 PUSH 38 %mstore_kernel_general + PUSH 0 PUSH 39 %mstore_kernel_general + PUSH 0 PUSH 40 %mstore_kernel_general + PUSH 0 PUSH 41 %mstore_kernel_general + PUSH 0 PUSH 42 %mstore_kernel_general + PUSH 0 PUSH 43 %mstore_kernel_general + PUSH 0 PUSH 44 %mstore_kernel_general + PUSH 0 PUSH 45 %mstore_kernel_general + PUSH 0 PUSH 46 %mstore_kernel_general + PUSH 0 PUSH 47 %mstore_kernel_general + PUSH 0 PUSH 48 %mstore_kernel_general + PUSH 0 PUSH 49 %mstore_kernel_general + PUSH 0 PUSH 50 %mstore_kernel_general + PUSH 0 PUSH 51 %mstore_kernel_general + PUSH 0 PUSH 52 %mstore_kernel_general + PUSH 0 PUSH 53 %mstore_kernel_general + PUSH 0 PUSH 54 %mstore_kernel_general + PUSH 0 PUSH 55 %mstore_kernel_general + PUSH 0 PUSH 56 %mstore_kernel_general + PUSH 0 PUSH 57 %mstore_kernel_general + PUSH 0 PUSH 58 %mstore_kernel_general + PUSH 0 PUSH 59 %mstore_kernel_general + PUSH 0 PUSH 60 %mstore_kernel_general + PUSH 0 PUSH 61 %mstore_kernel_general + PUSH 0 PUSH 62 %mstore_kernel_general + PUSH 0 PUSH 63 %mstore_kernel_general + PUSH 0 PUSH 64 %mstore_kernel_general + PUSH 0 PUSH 65 %mstore_kernel_general + PUSH 0 PUSH 66 %mstore_kernel_general + PUSH 0 PUSH 67 %mstore_kernel_general + PUSH 0 PUSH 68 %mstore_kernel_general + PUSH 0 PUSH 69 %mstore_kernel_general + PUSH 0 PUSH 70 %mstore_kernel_general + PUSH 0 PUSH 71 %mstore_kernel_general + PUSH 0 PUSH 72 %mstore_kernel_general + PUSH 0 PUSH 73 %mstore_kernel_general + PUSH 0 PUSH 74 %mstore_kernel_general + PUSH 0 PUSH 75 %mstore_kernel_general + PUSH 0 PUSH 76 %mstore_kernel_general + PUSH 0 PUSH 77 %mstore_kernel_general + PUSH 0 PUSH 78 %mstore_kernel_general + PUSH 0 PUSH 79 %mstore_kernel_general + PUSH 0 PUSH 80 %mstore_kernel_general + PUSH 0 PUSH 81 %mstore_kernel_general + PUSH 0 PUSH 82 %mstore_kernel_general + PUSH 0 PUSH 83 %mstore_kernel_general + PUSH 0 PUSH 84 %mstore_kernel_general + PUSH 0 PUSH 85 %mstore_kernel_general + PUSH 0 PUSH 86 %mstore_kernel_general + PUSH 0 PUSH 87 %mstore_kernel_general + PUSH 0 PUSH 88 %mstore_kernel_general + PUSH 0 PUSH 89 %mstore_kernel_general + PUSH 0 PUSH 90 %mstore_kernel_general + PUSH 0 PUSH 91 %mstore_kernel_general + PUSH 0 PUSH 92 %mstore_kernel_general + PUSH 0 PUSH 93 %mstore_kernel_general + PUSH 0 PUSH 94 %mstore_kernel_general + PUSH 0 PUSH 95 %mstore_kernel_general + PUSH 0 PUSH 96 %mstore_kernel_general + PUSH 0 PUSH 97 %mstore_kernel_general + PUSH 0 PUSH 98 %mstore_kernel_general + PUSH 0 PUSH 99 %mstore_kernel_general +%endmacro diff --git a/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm b/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm new file mode 100644 index 00000000..c7137f9b --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm @@ -0,0 +1,38 @@ +global precompile_rip160: + // stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size + %pop2 + // stack: new_ctx, kexit_info, ret_offset, ret_size + DUP1 + SET_CONTEXT + // stack: (empty) + PUSH 0x100000000 // = 2^32 (is_kernel = true) + // stack: kexit_info + + %calldatasize + %num_bytes_to_num_words + // stack: data_words_len, kexit_info + %mul_const(@RIP160_DYNAMIC_GAS) + PUSH @RIP160_STATIC_GAS + ADD + // stack: gas, kexit_info + %charge_gas + + // Copy the call data to the kernel general segment (ripemd expects it there) and call ripemd. + %calldatasize + GET_CONTEXT + %stack (ctx, size) -> + ( + 0, @SEGMENT_KERNEL_GENERAL, 200, // DST + ctx, @SEGMENT_CALLDATA, 0, // SRC + size, ripemd, // count, retdest + 200, size, rip160_contd // ripemd input: virt, num_bytes, retdest + ) + %jump(memcpy) + +rip160_contd: + // stack: hash, kexit_info + // Store the result hash to the parent's return data using `mstore_unpacking`. + %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) + %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) + %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, hash, 32, pop_and_return_success) + %jump(mstore_unpacking) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm b/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm new file mode 100644 index 00000000..9bfd544e --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm @@ -0,0 +1,41 @@ +global precompile_sha256: + // stack: address, retdest, new_ctx, kexit_info, ret_offset, ret_size + %pop2 + // stack: new_ctx, kexit_info, ret_offset, ret_size + DUP1 + SET_CONTEXT + // stack: (empty) + PUSH 0x100000000 // = 2^32 (is_kernel = true) + // stack: kexit_info + + %calldatasize + %num_bytes_to_num_words + // stack: data_words_len, kexit_info + %mul_const(@SHA256_DYNAMIC_GAS) + PUSH @SHA256_STATIC_GAS + ADD + // stack: gas, kexit_info + %charge_gas + + // TODO: fix this + %zero_out_kernel_general + + // Copy the call data to the kernel general segment (sha2 expects it there) and call sha2. + %calldatasize + GET_CONTEXT + %stack (ctx, size) -> + ( + 0, @SEGMENT_KERNEL_GENERAL, 1, // DST + ctx, @SEGMENT_CALLDATA, 0, // SRC + size, sha2, // count, retdest + 0, size, sha256_contd // sha2 input: virt, num_bytes, retdest + ) + %jump(memcpy) + +sha256_contd: + // stack: hash, kexit_info + // Store the result hash to the parent's return data using `mstore_unpacking`. + %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) + %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) + %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, hash, 32, pop_and_return_success) + %jump(mstore_unpacking) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm b/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm new file mode 100644 index 00000000..fbb46670 --- /dev/null +++ b/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm @@ -0,0 +1,3 @@ +global precompile_snarkv: + // TODO + PANIC diff --git a/evm/src/cpu/kernel/asm/core/syscall_stubs.asm b/evm/src/cpu/kernel/asm/core/syscall_stubs.asm index 95b50b0b..1232576c 100644 --- a/evm/src/cpu/kernel/asm/core/syscall_stubs.asm +++ b/evm/src/cpu/kernel/asm/core/syscall_stubs.asm @@ -6,22 +6,18 @@ global sys_blockhash: global sys_prevrandao: // TODO: What semantics will this have for Edge? PANIC -global sys_chainid: - // TODO: Return the block's chain ID instead of the txn's, even though they should match. - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - %mload_txn_field(@TXN_FIELD_CHAIN_ID) - // stack: chain_id, kexit_info - SWAP1 - EXIT_KERNEL global sys_log0: + %check_static PANIC global sys_log1: + %check_static PANIC global sys_log2: + %check_static PANIC global sys_log3: + %check_static PANIC global sys_log4: + %check_static PANIC diff --git a/evm/src/cpu/kernel/asm/core/terminate.asm b/evm/src/cpu/kernel/asm/core/terminate.asm index 5d9ed853..a46bbf0f 100644 --- a/evm/src/cpu/kernel/asm/core/terminate.asm +++ b/evm/src/cpu/kernel/asm/core/terminate.asm @@ -45,6 +45,7 @@ sys_return_finish: %jump(terminate_common) global sys_selfdestruct: + %check_static // stack: kexit_info, recipient SWAP1 %u256_to_addr %address DUP1 %balance diff --git a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_add.asm b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_add.asm index aacb7d3a..499d88d5 100644 --- a/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_add.asm +++ b/evm/src/cpu/kernel/asm/curve/bn254/curve_arithmetic/curve_add.asm @@ -1,6 +1,7 @@ // BN254 elliptic curve addition. // Uses the standard affine addition formula. global bn_add: + // stack: x0, y0, x1, y1, retdest // Check if points are valid BN254 points. DUP2 // stack: y0, x0, y0, x1, y1, retdest @@ -257,7 +258,7 @@ global bn_double: // Return (u256::MAX, u256::MAX) which is used to indicate the input was invalid. %macro bn_invalid_input // stack: retdest - PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH @U256_MAX // stack: u256::MAX, retdest DUP1 // stack: u256::MAX, u256::MAX, retdest diff --git a/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm b/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm index 6e9df123..951ca97c 100644 --- a/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm +++ b/evm/src/cpu/kernel/asm/curve/secp256k1/ecrecover.asm @@ -108,9 +108,7 @@ pubkey_to_addr: // stack: PKx, PKy, retdest %keccak256_u256_pair // stack: hash, retdest - PUSH 0xffffffffffffffffffffffffffffffffffffffff - // stack: 2^160-1, hash, retdest - AND + %u256_to_addr // stack: address, retdest SWAP1 // stack: retdest, address diff --git a/evm/src/cpu/kernel/asm/hash/sha2/main.asm b/evm/src/cpu/kernel/asm/hash/sha2/main.asm index 1deab294..8679bdb0 100644 --- a/evm/src/cpu/kernel/asm/hash/sha2/main.asm +++ b/evm/src/cpu/kernel/asm/hash/sha2/main.asm @@ -6,7 +6,7 @@ global sha2: // stack: virt, num_bytes, virt, retdest %mstore_kernel_general // stack: virt, retdest - + // Precodition: input is in memory, starting at virt of kernel general segment, of the form // num_bytes, x[0], x[1], ..., x[num_bytes - 1] diff --git a/evm/src/cpu/kernel/asm/memory/metadata.asm b/evm/src/cpu/kernel/asm/memory/metadata.asm index 4941f8d6..c158718b 100644 --- a/evm/src/cpu/kernel/asm/memory/metadata.asm +++ b/evm/src/cpu/kernel/asm/memory/metadata.asm @@ -200,6 +200,19 @@ global sys_gaslimit: SWAP1 EXIT_KERNEL +%macro blockchainid + %mload_global_metadata(@GLOBAL_METADATA_BLOCK_CHAIN_ID) +%endmacro + +global sys_chainid: + // stack: kexit_info + %charge_gas_const(@GAS_BASE) + // stack: kexit_info + %blockchainid + // stack: chain_id, kexit_info + SWAP1 + EXIT_KERNEL + %macro basefee %mload_global_metadata(@GLOBAL_METADATA_BLOCK_BASE_FEE) %endmacro @@ -283,3 +296,10 @@ global sys_basefee: %jumpi(fault_exception) // stack: (empty) %endmacro + +// Convenience macro for checking if the current context is static. +// Called before state-changing opcodes. +%macro check_static + %mload_context_metadata(@CTX_METADATA_STATIC) + %jumpi(fault_exception) +%endmacro diff --git a/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm b/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm index 90fb0e0b..6da8f567 100644 --- a/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm +++ b/evm/src/cpu/kernel/asm/mpt/storage/storage_write.asm @@ -4,6 +4,7 @@ // Post stack: (empty) global sys_sstore: + %check_static %stack (kexit_info, slot, value) -> (slot, kexit_info, slot, value) %address %insert_accessed_storage_keys POP // TODO: Use return value in gas calculation. // TODO: Assuming a cold zero -> nonzero write for now. diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index c57f0649..279d449a 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -360,8 +360,6 @@ %macro not_bit // stack: b - PUSH 1 - // stack: 1, b - SUB - // stack: 1 - b + ISZERO + // stack: not b %endmacro diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index 2889e7c7..75a96403 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -36,6 +36,10 @@ pub fn evm_constants() -> HashMap { c.insert(name.into(), U256::from(value)); } + for (name, value) in PRECOMPILES_GAS { + c.insert(name.into(), U256::from(value)); + } + for segment in Segment::all() { c.insert(segment.var_name().into(), (segment as u32).into()); } @@ -207,3 +211,19 @@ const PRECOMPILES: [(&str, u16); 9] = [ ("SNARKV", 8), ("BLAKE2_F", 9), ]; + +const PRECOMPILES_GAS: [(&str, u16); 13] = [ + ("ECREC_GAS", 3_000), + ("SHA256_STATIC_GAS", 60), + ("SHA256_DYNAMIC_GAS", 12), + ("RIP160_STATIC_GAS", 600), + ("RIP160_DYNAMIC_GAS", 120), + ("ID_STATIC_GAS", 15), + ("ID_DYNAMIC_GAS", 3), + ("EXPMOD_MIN_GAS", 200), + ("BN_ADD_GAS", 150), + ("BN_MUL_GAS", 6_000), + ("SNARKV_STATIC_GAS", 45_000), + ("SNARKV_DYNAMIC_GAS", 34_000), + ("BLAKE2_F_DYNAMIC_GAS", 1), +]; diff --git a/evm/src/cpu/stack.rs b/evm/src/cpu/stack.rs index e9258172..f5e9c78c 100644 --- a/evm/src/cpu/stack.rs +++ b/evm/src/cpu/stack.rs @@ -29,7 +29,7 @@ const BASIC_BINARY_OP: Option = Some(StackBehavior { disable_other_channels: true, }); const BASIC_TERNARY_OP: Option = Some(StackBehavior { - num_pops: 2, + num_pops: 3, pushes: true, disable_other_channels: true, }); @@ -60,8 +60,16 @@ const STACK_BEHAVIORS: OpsColumnsView> = OpsColumnsView { xor: BASIC_BINARY_OP, not: BASIC_UNARY_OP, byte: BASIC_BINARY_OP, - shl: BASIC_BINARY_OP, - shr: BASIC_BINARY_OP, + shl: Some(StackBehavior { + num_pops: 2, + pushes: true, + disable_other_channels: false, + }), + shr: Some(StackBehavior { + num_pops: 2, + pushes: true, + disable_other_channels: false, + }), keccak_general: None, // TODO prover_input: None, // TODO pop: None, // TODO diff --git a/evm/src/cpu/stack_bounds.rs b/evm/src/cpu/stack_bounds.rs index 627411ea..4c4995f4 100644 --- a/evm/src/cpu/stack_bounds.rs +++ b/evm/src/cpu/stack_bounds.rs @@ -26,13 +26,13 @@ pub const MAX_USER_STACK_SIZE: usize = 1024; // Other operations that have a minimum stack size (e.g. `MULMOD`, which has three inputs) read // all their inputs from memory. On underflow, the cross-table lookup fails, as -1, ..., -17 are // invalid memory addresses. -const DECREMENTING_FLAGS: [usize; 1] = [COL_MAP.op.pop]; +pub(crate) const DECREMENTING_FLAGS: [usize; 1] = [COL_MAP.op.pop]; // Operations that increase the stack length by 1, but excluding: // - privileged (kernel-only) operations (superfluous; doesn't affect correctness), // - operations that from userspace to the kernel (required for correctness). // TODO: This list is incomplete. -const INCREMENTING_FLAGS: [usize; 2] = [COL_MAP.op.pc, COL_MAP.op.dup]; +pub(crate) const INCREMENTING_FLAGS: [usize; 2] = [COL_MAP.op.pc, COL_MAP.op.dup]; /// Calculates `lv.stack_len_bounds_aux`. Note that this must be run after decode. pub fn generate(lv: &mut CpuColumnsView) { diff --git a/evm/src/keccak_sponge/columns.rs b/evm/src/keccak_sponge/columns.rs index 440c59ab..d15e0e4b 100644 --- a/evm/src/keccak_sponge/columns.rs +++ b/evm/src/keccak_sponge/columns.rs @@ -17,10 +17,6 @@ pub(crate) struct KeccakSpongeColumnsView { /// not a padding byte; 0 otherwise. pub is_full_input_block: T, - /// 1 if this row represents the final block of a sponge, in which case some or all of the bytes - /// in the block will be padding bytes; 0 otherwise. - pub is_final_block: T, - // The base address at which we will read the input block. pub context: T, pub segment: T, diff --git a/evm/src/keccak_sponge/keccak_sponge_stark.rs b/evm/src/keccak_sponge/keccak_sponge_stark.rs index 6c8f7f0d..4c91d6fd 100644 --- a/evm/src/keccak_sponge/keccak_sponge_stark.rs +++ b/evm/src/keccak_sponge/keccak_sponge_stark.rs @@ -128,7 +128,7 @@ pub(crate) fn ctl_looking_logic(i: usize) -> Vec> { pub(crate) fn ctl_looked_filter() -> Column { // The CPU table is only interested in our final-block rows, since those contain the final // sponge output. - Column::single(KECCAK_SPONGE_COL_MAP.is_final_block) + Column::sum(KECCAK_SPONGE_COL_MAP.is_final_input_len) } /// CTL filter for reading the `i`th byte of input from memory. @@ -143,12 +143,12 @@ pub(crate) fn ctl_looking_memory_filter(i: usize) -> Column { /// CTL filter for looking at XORs in the logic table. pub(crate) fn ctl_looking_logic_filter() -> Column { let cols = KECCAK_SPONGE_COL_MAP; - Column::sum([cols.is_full_input_block, cols.is_final_block]) + Column::sum(once(&cols.is_full_input_block).chain(&cols.is_final_input_len)) } pub(crate) fn ctl_looking_keccak_filter() -> Column { let cols = KECCAK_SPONGE_COL_MAP; - Column::sum([cols.is_full_input_block, cols.is_final_block]) + Column::sum(once(&cols.is_full_input_block).chain(&cols.is_final_input_len)) } /// Information about a Keccak sponge operation needed for witness generation. @@ -269,10 +269,7 @@ impl, const D: usize> KeccakSpongeStark { ) -> KeccakSpongeColumnsView { assert_eq!(already_absorbed_bytes + final_inputs.len(), op.input.len()); - let mut row = KeccakSpongeColumnsView { - is_final_block: F::ONE, - ..Default::default() - }; + let mut row = KeccakSpongeColumnsView::default(); for (block_byte, input_byte) in row.block_bytes.iter_mut().zip(final_inputs) { *block_byte = F::from_canonical_u8(*input_byte); @@ -372,7 +369,7 @@ impl, const D: usize> Stark for KeccakSpongeS let is_full_input_block = local_values.is_full_input_block; yield_constr.constraint(is_full_input_block * (is_full_input_block - P::ONES)); - let is_final_block = local_values.is_final_block; + let is_final_block: P = local_values.is_final_input_len.iter().copied().sum(); yield_constr.constraint(is_final_block * (is_final_block - P::ONES)); for &is_final_len in local_values.is_final_input_len.iter() { @@ -382,13 +379,6 @@ impl, const D: usize> Stark for KeccakSpongeS // Ensure that full-input block and final block flags are not set to 1 at the same time. yield_constr.constraint(is_final_block * is_full_input_block); - // Sum of is_final_input_len should equal is_final_block (which will be 0 or 1). - let is_final_input_len_sum: P = local_values.is_final_input_len.iter().copied().sum(); - yield_constr.constraint(is_final_input_len_sum - is_final_block); - - // If this is a full-input block, is_final_input_len should contain all 0s. - yield_constr.constraint(is_full_input_block * is_final_input_len_sum); - // If this is the first row, the original sponge state should be 0 and already_absorbed_bytes = 0. let already_absorbed_bytes = local_values.already_absorbed_bytes; yield_constr.constraint_first_row(already_absorbed_bytes); @@ -447,8 +437,9 @@ impl, const D: usize> Stark for KeccakSpongeS // A dummy row is always followed by another dummy row, so the prover can't put dummy rows "in between" to avoid the above checks. let is_dummy = P::ONES - is_full_input_block - is_final_block; + let next_is_final_block: P = next_values.is_final_input_len.iter().copied().sum(); yield_constr.constraint_transition( - is_dummy * (next_values.is_full_input_block + next_values.is_final_block), + is_dummy * (next_values.is_full_input_block + next_is_final_block), ); // If this is a final block, is_final_input_len implies `len - already_absorbed == i`. @@ -479,7 +470,7 @@ impl, const D: usize> Stark for KeccakSpongeS ); yield_constr.constraint(builder, constraint); - let is_final_block = local_values.is_final_block; + let is_final_block = builder.add_many_extension(local_values.is_final_input_len); let constraint = builder.mul_sub_extension(is_final_block, is_final_block, is_final_block); yield_constr.constraint(builder, constraint); @@ -492,21 +483,6 @@ impl, const D: usize> Stark for KeccakSpongeS let constraint = builder.mul_extension(is_final_block, is_full_input_block); yield_constr.constraint(builder, constraint); - // Sum of is_final_input_len should equal is_final_block (which will be 0 or 1). - let mut is_final_input_len_sum = builder.add_extension( - local_values.is_final_input_len[0], - local_values.is_final_input_len[1], - ); - for &input_len in local_values.is_final_input_len.iter().skip(2) { - is_final_input_len_sum = builder.add_extension(is_final_input_len_sum, input_len); - } - let constraint = builder.sub_extension(is_final_input_len_sum, is_final_block); - yield_constr.constraint(builder, constraint); - - // If this is a full-input block, is_final_input_len should contain all 0s. - let constraint = builder.mul_extension(is_full_input_block, is_final_input_len_sum); - yield_constr.constraint(builder, constraint); - // If this is the first row, the original sponge state should be 0 and already_absorbed_bytes = 0. let already_absorbed_bytes = local_values.already_absorbed_bytes; yield_constr.constraint_first_row(builder, already_absorbed_bytes); @@ -580,9 +556,9 @@ impl, const D: usize> Stark for KeccakSpongeS let tmp = builder.sub_extension(one, is_final_block); builder.sub_extension(tmp, is_full_input_block) }; + let next_is_final_block = builder.add_many_extension(next_values.is_final_input_len); let constraint = { - let tmp = - builder.add_extension(next_values.is_final_block, next_values.is_full_input_block); + let tmp = builder.add_extension(next_is_final_block, next_values.is_full_input_block); builder.mul_extension(is_dummy, tmp) }; yield_constr.constraint_transition(builder, constraint); diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index 65b7ddcf..0184e183 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -4,6 +4,7 @@ use plonky2::field::types::Field; use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::stack_bounds::{DECREMENTING_FLAGS, INCREMENTING_FLAGS, MAX_USER_STACK_SIZE}; use crate::generation::state::GenerationState; use crate::memory::segments::Segment; use crate::witness::errors::ProgramError; @@ -258,6 +259,24 @@ fn try_perform_instruction(state: &mut GenerationState) -> Result<( fill_op_flag(op, &mut row); + let check_underflow: F = DECREMENTING_FLAGS.map(|i| row[i]).into_iter().sum(); + let check_overflow: F = INCREMENTING_FLAGS.map(|i| row[i]).into_iter().sum(); + let no_check = F::ONE - (check_underflow + check_overflow); + + let disallowed_len = check_overflow * F::from_canonical_usize(MAX_USER_STACK_SIZE) - no_check; + let diff = row.stack_len - disallowed_len; + + let user_mode = F::ONE - row.is_kernel_mode; + let rhs = user_mode + check_underflow; + + row.stack_len_bounds_aux = match diff.try_inverse() { + Some(diff_inv) => diff_inv * rhs, // `rhs` may be a value other than 1 or 0 + None => { + assert_eq!(rhs, F::ZERO); + F::ZERO + } + }; + perform_op(state, op, row) }