mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-06 15:53:10 +00:00
Merge branch 'main' into stack_bound
This commit is contained in:
commit
4946c3d5fd
@ -156,7 +156,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D
|
||||
pc::eval_packed(local_values, yield_constr);
|
||||
shift::eval_packed(local_values, yield_constr);
|
||||
simple_logic::eval_packed(local_values, yield_constr);
|
||||
stack::eval_packed(local_values, &mut dummy_yield_constr);
|
||||
stack::eval_packed(local_values, yield_constr);
|
||||
stack_bounds::eval_packed(local_values, yield_constr);
|
||||
syscalls::eval_packed(local_values, next_values, yield_constr);
|
||||
}
|
||||
@ -186,7 +186,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D
|
||||
pc::eval_ext_circuit(builder, local_values, yield_constr);
|
||||
shift::eval_ext_circuit(builder, local_values, yield_constr);
|
||||
simple_logic::eval_ext_circuit(builder, local_values, yield_constr);
|
||||
stack::eval_ext_circuit(builder, local_values, &mut dummy_yield_constr);
|
||||
stack::eval_ext_circuit(builder, local_values, yield_constr);
|
||||
stack_bounds::eval_ext_circuit(builder, local_values, yield_constr);
|
||||
syscalls::eval_ext_circuit(builder, local_values, next_values, yield_constr);
|
||||
}
|
||||
|
||||
@ -2,16 +2,31 @@
|
||||
|
||||
// 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
|
||||
// TODO: Charge gas.
|
||||
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
|
||||
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
|
||||
|
||||
%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
|
||||
// TODO: Consider call depth
|
||||
|
||||
// Each line in the block below does not change the stack.
|
||||
@ -20,15 +35,15 @@ global sys_call:
|
||||
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 // TODO: This is not correct in most cases. Use C_callgas as in the YP.
|
||||
DUP3 %set_new_ctx_gas_limit
|
||||
DUP4 %set_new_ctx_code
|
||||
|
||||
%stack (new_ctx, kexit_info, gas, address, value, args_offset, args_size, ret_offset, ret_size) ->
|
||||
(new_ctx, args_offset, args_size, 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) ->
|
||||
(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, 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
|
||||
|
||||
@ -36,25 +51,38 @@ 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
|
||||
|
||||
// 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)
|
||||
DUP3 %set_new_ctx_gas_limit
|
||||
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) ->
|
||||
(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)
|
||||
%enter_new_ctx
|
||||
|
||||
@ -65,15 +93,25 @@ 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
|
||||
|
||||
// Each line in the block below does not change the stack.
|
||||
%set_static_true
|
||||
@ -81,8 +119,14 @@ global sys_staticcall:
|
||||
%address %set_new_ctx_caller
|
||||
PUSH 0 %set_new_ctx_value
|
||||
%set_new_ctx_parent_pc(after_call_instruction)
|
||||
DUP3 %set_new_ctx_gas_limit
|
||||
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, 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)
|
||||
-> (new_ctx, kexit_info, ret_offset, ret_size)
|
||||
%enter_new_ctx
|
||||
|
||||
@ -91,23 +135,39 @@ 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
|
||||
|
||||
// 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)
|
||||
DUP3 %set_new_ctx_gas_limit
|
||||
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, 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, args_offset, args_size, ret_offset, ret_size)
|
||||
-> (new_ctx, kexit_info, ret_offset, ret_size)
|
||||
%enter_new_ctx
|
||||
|
||||
@ -117,7 +177,7 @@ global after_call_instruction:
|
||||
SWAP3
|
||||
// stack: kexit_info, leftover_gas, new_ctx, success, ret_offset, ret_size
|
||||
// Add the leftover gas into the appropriate bits of kexit_info.
|
||||
SWAP1 %shl_const(192) ADD
|
||||
SWAP1 %shl_const(192) SWAP1 SUB
|
||||
// stack: kexit_info, new_ctx, success, ret_offset, ret_size
|
||||
|
||||
// The callee's terminal instruction will have populated RETURNDATA.
|
||||
@ -203,9 +263,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
|
||||
|
||||
@ -239,14 +297,93 @@ 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)
|
||||
%%after:
|
||||
%endmacro
|
||||
|
||||
// Charge gas for *call opcodes and return the sub-context gas limit.
|
||||
// Doesn't include memory expansion costs.
|
||||
%macro call_charge_gas
|
||||
// Compute C_aaccess
|
||||
// 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 %not_bit
|
||||
// stack: value≠0, Caaccess, address, gas, kexit_info, value
|
||||
DUP1
|
||||
%mul_const(@GAS_CALLVALUE)
|
||||
|
||||
// Compute C_new
|
||||
// stack: Cxfer, value≠0, Caaccess, address, gas, kexit_info, value
|
||||
SWAP1
|
||||
// stack: value≠0, Cxfer, Caaccess, address, gas, kexit_info, value
|
||||
DUP4 %is_dead MUL
|
||||
// stack: is_dead(address) and value≠0, Cxfer, Caaccess, address, gas, kexit_info, value
|
||||
%mul_const(@GAS_NEWACCOUNT)
|
||||
// stack: Cnew, Cxfer, Caaccess, address, gas, kexit_info, value
|
||||
|
||||
// Compute C_extra
|
||||
ADD ADD
|
||||
|
||||
// Compute C_gascap
|
||||
// stack: Cextra, address, gas, kexit_info, value
|
||||
DUP4 %leftover_gas
|
||||
// stack: leftover_gas, Cextra, address, gas, kexit_info, value
|
||||
DUP2 DUP2 LT
|
||||
// stack: leftover_gas<Cextra, leftover_gas, Cextra, address, gas, kexit_info, value
|
||||
DUP5 DUP2 MUL
|
||||
// stack: (leftover_gas<Cextra)*gas, leftover_gas<Cextra, leftover_gas, Cextra, address, gas, kexit_info, value
|
||||
SWAP1 %not_bit
|
||||
// stack: leftover_gas>=Cextra, (leftover_gas<Cextra)*gas, leftover_gas, Cextra, address, gas, kexit_info, value
|
||||
DUP4 DUP4 SUB
|
||||
// stack: leftover_gas - Cextra, leftover_gas>=Cextra, (leftover_gas<Cextra)*gas, leftover_gas, Cextra, address, gas, kexit_info, value
|
||||
%all_but_one_64th
|
||||
// stack: L(leftover_gas - Cextra), leftover_gas>=Cextra, (leftover_gas<Cextra)*gas, leftover_gas, Cextra, address, gas, kexit_info, value
|
||||
DUP7 %min MUL ADD
|
||||
// stack: Cgascap, leftover_gas, Cextra, address, gas, kexit_info, value
|
||||
|
||||
// Compute C_call and charge for it.
|
||||
%stack (Cgascap, leftover_gas, Cextra) -> (Cextra, Cgascap, Cgascap)
|
||||
ADD
|
||||
%stack (C_call, Cgascap, address, gas, kexit_info, value) ->
|
||||
(C_call, kexit_info, Cgascap, address, gas, value)
|
||||
%charge_gas
|
||||
|
||||
// Compute C_callgas
|
||||
%stack (kexit_info, Cgascap, address, gas, value) ->
|
||||
(Cgascap, address, gas, kexit_info, value)
|
||||
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) ->
|
||||
(kexit_info, C_callgas, address, value)
|
||||
%endmacro
|
||||
|
||||
// Checked memory expansion.
|
||||
%macro checked_mem_expansion
|
||||
// stack: size, offset, kexit_info
|
||||
DUP1 ISZERO %jumpi(%%zero)
|
||||
ADD // TODO: check for overflow
|
||||
// stack: expanded_num_bytes, kexit_info
|
||||
DUP1 %ensure_reasonable_offset
|
||||
%update_mem_bytes
|
||||
%jump(%%after)
|
||||
%%zero:
|
||||
%pop2
|
||||
%%after:
|
||||
%endmacro
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -66,4 +66,4 @@
|
||||
DUP1 %is_non_existent
|
||||
SWAP1 %is_empty
|
||||
ADD // OR
|
||||
%endmacro
|
||||
%endmacro
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -360,8 +360,6 @@
|
||||
|
||||
%macro not_bit
|
||||
// stack: b
|
||||
PUSH 1
|
||||
// stack: 1, b
|
||||
SUB
|
||||
// stack: 1 - b
|
||||
ISZERO
|
||||
// stack: not b
|
||||
%endmacro
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
//! An EVM interpreter for testing and debugging purposes.
|
||||
|
||||
use core::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Range;
|
||||
|
||||
@ -305,20 +306,20 @@ impl<'a> Interpreter<'a> {
|
||||
0x02 => self.run_mul(), // "MUL",
|
||||
0x03 => self.run_sub(), // "SUB",
|
||||
0x04 => self.run_div(), // "DIV",
|
||||
0x05 => todo!(), // "SDIV",
|
||||
0x05 => self.run_sdiv(), // "SDIV",
|
||||
0x06 => self.run_mod(), // "MOD",
|
||||
0x07 => todo!(), // "SMOD",
|
||||
0x07 => self.run_smod(), // "SMOD",
|
||||
0x08 => self.run_addmod(), // "ADDMOD",
|
||||
0x09 => self.run_mulmod(), // "MULMOD",
|
||||
0x0a => self.run_exp(), // "EXP",
|
||||
0x0b => todo!(), // "SIGNEXTEND",
|
||||
0x0b => self.run_signextend(), // "SIGNEXTEND",
|
||||
0x0c => self.run_addfp254(), // "ADDFP254",
|
||||
0x0d => self.run_mulfp254(), // "MULFP254",
|
||||
0x0e => self.run_subfp254(), // "SUBFP254",
|
||||
0x10 => self.run_lt(), // "LT",
|
||||
0x11 => self.run_gt(), // "GT",
|
||||
0x12 => todo!(), // "SLT",
|
||||
0x13 => todo!(), // "SGT",
|
||||
0x12 => self.run_slt(), // "SLT",
|
||||
0x13 => self.run_sgt(), // "SGT",
|
||||
0x14 => self.run_eq(), // "EQ",
|
||||
0x15 => self.run_iszero(), // "ISZERO",
|
||||
0x16 => self.run_and(), // "AND",
|
||||
@ -328,7 +329,7 @@ impl<'a> Interpreter<'a> {
|
||||
0x1a => self.run_byte(), // "BYTE",
|
||||
0x1b => self.run_shl(), // "SHL",
|
||||
0x1c => self.run_shr(), // "SHR",
|
||||
0x1d => todo!(), // "SAR",
|
||||
0x1d => self.run_sar(), // "SAR",
|
||||
0x20 => self.run_keccak256(), // "KECCAK256",
|
||||
0x21 => self.run_keccak_general(), // "KECCAK_GENERAL",
|
||||
0x30 => todo!(), // "ADDRESS",
|
||||
@ -467,12 +468,75 @@ impl<'a> Interpreter<'a> {
|
||||
self.push(if y.is_zero() { U256::zero() } else { x / y });
|
||||
}
|
||||
|
||||
fn run_sdiv(&mut self) {
|
||||
let mut x = self.pop();
|
||||
let mut y = self.pop();
|
||||
|
||||
let y_is_zero = y.is_zero();
|
||||
|
||||
if y_is_zero {
|
||||
self.push(U256::zero());
|
||||
} else if y.eq(&MINUS_ONE) && x.eq(&MIN_VALUE) {
|
||||
self.push(MIN_VALUE);
|
||||
} else {
|
||||
let x_is_pos = x.eq(&(x & SIGN_MASK));
|
||||
let y_is_pos = y.eq(&(y & SIGN_MASK));
|
||||
|
||||
// We compute the absolute quotient first,
|
||||
// then adapt its sign based on the operands.
|
||||
if !x_is_pos {
|
||||
x = two_complement(x);
|
||||
}
|
||||
if !y_is_pos {
|
||||
y = two_complement(y);
|
||||
}
|
||||
let div = x / y;
|
||||
if div.eq(&U256::zero()) {
|
||||
self.push(U256::zero());
|
||||
}
|
||||
|
||||
self.push(if x_is_pos == y_is_pos {
|
||||
div
|
||||
} else {
|
||||
two_complement(div)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn run_mod(&mut self) {
|
||||
let x = self.pop();
|
||||
let y = self.pop();
|
||||
self.push(if y.is_zero() { U256::zero() } else { x % y });
|
||||
}
|
||||
|
||||
fn run_smod(&mut self) {
|
||||
let mut x = self.pop();
|
||||
let mut y = self.pop();
|
||||
|
||||
if y.is_zero() {
|
||||
self.push(U256::zero());
|
||||
} else {
|
||||
let x_is_pos = x.eq(&(x & SIGN_MASK));
|
||||
let y_is_pos = y.eq(&(y & SIGN_MASK));
|
||||
|
||||
// We compute the absolute remainder first,
|
||||
// then adapt its sign based on the operands.
|
||||
if !x_is_pos {
|
||||
x = two_complement(x);
|
||||
}
|
||||
if !y_is_pos {
|
||||
y = two_complement(y);
|
||||
}
|
||||
let rem = x % y;
|
||||
if rem.eq(&U256::zero()) {
|
||||
self.push(U256::zero());
|
||||
}
|
||||
|
||||
// Remainder always has the same sign as the dividend.
|
||||
self.push(if x_is_pos { rem } else { two_complement(rem) });
|
||||
}
|
||||
}
|
||||
|
||||
fn run_addmod(&mut self) {
|
||||
let x = U512::from(self.pop());
|
||||
let y = U512::from(self.pop());
|
||||
@ -513,6 +577,43 @@ impl<'a> Interpreter<'a> {
|
||||
self.push_bool(x > y);
|
||||
}
|
||||
|
||||
fn run_slt(&mut self) {
|
||||
let x = self.pop();
|
||||
let y = self.pop();
|
||||
self.push_bool(signed_cmp(x, y) == Ordering::Less);
|
||||
}
|
||||
|
||||
fn run_sgt(&mut self) {
|
||||
let x = self.pop();
|
||||
let y = self.pop();
|
||||
self.push_bool(signed_cmp(x, y) == Ordering::Greater);
|
||||
}
|
||||
|
||||
fn run_signextend(&mut self) {
|
||||
let n = self.pop();
|
||||
let x = self.pop();
|
||||
if n > U256::from(31) {
|
||||
self.push(x);
|
||||
} else {
|
||||
let n = n.low_u64() as usize;
|
||||
let num_bytes_prepend = 31 - n;
|
||||
|
||||
let mut x_bytes = [0u8; 32];
|
||||
x.to_big_endian(&mut x_bytes);
|
||||
let x_bytes = x_bytes[num_bytes_prepend..].to_vec();
|
||||
let sign_bit = x_bytes[0] >> 7;
|
||||
|
||||
let mut bytes = if sign_bit == 0 {
|
||||
vec![0; num_bytes_prepend]
|
||||
} else {
|
||||
vec![0xff; num_bytes_prepend]
|
||||
};
|
||||
bytes.extend_from_slice(&x_bytes);
|
||||
|
||||
self.push(U256::from_big_endian(&bytes));
|
||||
}
|
||||
}
|
||||
|
||||
fn run_eq(&mut self) {
|
||||
let x = self.pop();
|
||||
let y = self.pop();
|
||||
@ -574,6 +675,30 @@ impl<'a> Interpreter<'a> {
|
||||
self.push(value >> shift);
|
||||
}
|
||||
|
||||
fn run_sar(&mut self) {
|
||||
let shift = self.pop();
|
||||
let value = self.pop();
|
||||
let value_is_neg = !value.eq(&(value & SIGN_MASK));
|
||||
|
||||
if shift < U256::from(256usize) {
|
||||
let shift = shift.low_u64() as usize;
|
||||
let mask = !(MINUS_ONE >> shift);
|
||||
let value_shifted = value >> shift;
|
||||
|
||||
if value_is_neg {
|
||||
self.push(value_shifted | mask);
|
||||
} else {
|
||||
self.push(value_shifted);
|
||||
};
|
||||
} else {
|
||||
self.push(if value_is_neg {
|
||||
MINUS_ONE
|
||||
} else {
|
||||
U256::zero()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn run_keccak256(&mut self) {
|
||||
let offset = self.pop().as_usize();
|
||||
let size = self.pop().as_usize();
|
||||
@ -837,6 +962,70 @@ impl<'a> Interpreter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// Computes the two's complement of the given integer.
|
||||
fn two_complement(x: U256) -> U256 {
|
||||
let flipped_bits = x ^ MINUS_ONE;
|
||||
flipped_bits.overflowing_add(U256::one()).0
|
||||
}
|
||||
|
||||
fn signed_cmp(x: U256, y: U256) -> Ordering {
|
||||
let x_is_zero = x.is_zero();
|
||||
let y_is_zero = y.is_zero();
|
||||
|
||||
if x_is_zero && y_is_zero {
|
||||
return Ordering::Equal;
|
||||
}
|
||||
|
||||
let x_is_pos = x.eq(&(x & SIGN_MASK));
|
||||
let y_is_pos = y.eq(&(y & SIGN_MASK));
|
||||
|
||||
if x_is_zero {
|
||||
if y_is_pos {
|
||||
return Ordering::Less;
|
||||
} else {
|
||||
return Ordering::Greater;
|
||||
}
|
||||
};
|
||||
|
||||
if y_is_zero {
|
||||
if x_is_pos {
|
||||
return Ordering::Greater;
|
||||
} else {
|
||||
return Ordering::Less;
|
||||
}
|
||||
};
|
||||
|
||||
match (x_is_pos, y_is_pos) {
|
||||
(true, true) => x.cmp(&y),
|
||||
(true, false) => Ordering::Greater,
|
||||
(false, true) => Ordering::Less,
|
||||
(false, false) => x.cmp(&y).reverse(),
|
||||
}
|
||||
}
|
||||
|
||||
/// -1 in two's complement representation consists in all bits set to 1.
|
||||
const MINUS_ONE: U256 = U256([
|
||||
0xffffffffffffffff,
|
||||
0xffffffffffffffff,
|
||||
0xffffffffffffffff,
|
||||
0xffffffffffffffff,
|
||||
]);
|
||||
|
||||
/// -2^255 in two's complement representation consists in the MSB set to 1.
|
||||
const MIN_VALUE: U256 = U256([
|
||||
0x0000000000000000,
|
||||
0x0000000000000000,
|
||||
0x0000000000000000,
|
||||
0x8000000000000000,
|
||||
]);
|
||||
|
||||
const SIGN_MASK: U256 = U256([
|
||||
0xffffffffffffffff,
|
||||
0xffffffffffffffff,
|
||||
0xffffffffffffffff,
|
||||
0x7fffffffffffffff,
|
||||
]);
|
||||
|
||||
/// Return the (ordered) JUMPDEST offsets in the code.
|
||||
fn find_jumpdests(code: &[u8]) -> Vec<usize> {
|
||||
let mut offset = 0;
|
||||
|
||||
@ -29,7 +29,7 @@ const BASIC_BINARY_OP: Option<StackBehavior> = Some(StackBehavior {
|
||||
disable_other_channels: true,
|
||||
});
|
||||
const BASIC_TERNARY_OP: Option<StackBehavior> = Some(StackBehavior {
|
||||
num_pops: 2,
|
||||
num_pops: 3,
|
||||
pushes: true,
|
||||
disable_other_channels: true,
|
||||
});
|
||||
@ -60,8 +60,16 @@ const STACK_BEHAVIORS: OpsColumnsView<Option<StackBehavior>> = 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
|
||||
|
||||
@ -17,10 +17,6 @@ pub(crate) struct KeccakSpongeColumnsView<T: Copy> {
|
||||
/// 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,
|
||||
|
||||
@ -128,7 +128,7 @@ pub(crate) fn ctl_looking_logic<F: Field>(i: usize) -> Vec<Column<F>> {
|
||||
pub(crate) fn ctl_looked_filter<F: Field>() -> Column<F> {
|
||||
// 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<F: Field>(i: usize) -> Column<F> {
|
||||
/// CTL filter for looking at XORs in the logic table.
|
||||
pub(crate) fn ctl_looking_logic_filter<F: Field>() -> Column<F> {
|
||||
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<F: Field>() -> Column<F> {
|
||||
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<F: RichField + Extendable<D>, const D: usize> KeccakSpongeStark<F, D> {
|
||||
) -> KeccakSpongeColumnsView<F> {
|
||||
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<F: RichField + Extendable<D>, const D: usize> Stark<F, D> 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<F: RichField + Extendable<D>, const D: usize> Stark<F, D> 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<F: RichField + Extendable<D>, const D: usize> Stark<F, D> 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<F: RichField + Extendable<D>, const D: usize> Stark<F, D> 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<F: RichField + Extendable<D>, const D: usize> Stark<F, D> 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<F: RichField + Extendable<D>, const D: usize> Stark<F, D> 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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user