mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-04 06:43:07 +00:00
Merge pull request #838 from mir-protocol/transfer_test
Fixes to get test_simple_transfer working
This commit is contained in:
commit
4ae1d840a9
@ -142,14 +142,14 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D
|
||||
let mut dummy_yield_constr = ConstraintConsumer::new(vec![], P::ZEROS, P::ZEROS, P::ZEROS);
|
||||
bootstrap_kernel::eval_bootstrap_kernel(vars, yield_constr);
|
||||
control_flow::eval_packed_generic(local_values, next_values, yield_constr);
|
||||
decode::eval_packed_generic(local_values, yield_constr);
|
||||
decode::eval_packed_generic(local_values, &mut dummy_yield_constr);
|
||||
dup_swap::eval_packed(local_values, yield_constr);
|
||||
jumps::eval_packed(local_values, next_values, &mut dummy_yield_constr);
|
||||
membus::eval_packed(local_values, yield_constr);
|
||||
modfp254::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, yield_constr);
|
||||
stack::eval_packed(local_values, &mut dummy_yield_constr);
|
||||
stack_bounds::eval_packed(local_values, &mut dummy_yield_constr);
|
||||
syscalls::eval_packed(local_values, next_values, yield_constr);
|
||||
}
|
||||
@ -168,14 +168,14 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D
|
||||
RecursiveConstraintConsumer::new(zero, vec![], zero, zero, zero);
|
||||
bootstrap_kernel::eval_bootstrap_kernel_circuit(builder, vars, yield_constr);
|
||||
control_flow::eval_ext_circuit(builder, local_values, next_values, yield_constr);
|
||||
decode::eval_ext_circuit(builder, local_values, yield_constr);
|
||||
decode::eval_ext_circuit(builder, local_values, &mut dummy_yield_constr);
|
||||
dup_swap::eval_ext_circuit(builder, local_values, yield_constr);
|
||||
jumps::eval_ext_circuit(builder, local_values, next_values, &mut dummy_yield_constr);
|
||||
membus::eval_ext_circuit(builder, local_values, yield_constr);
|
||||
modfp254::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, yield_constr);
|
||||
stack::eval_ext_circuit(builder, local_values, &mut dummy_yield_constr);
|
||||
stack_bounds::eval_ext_circuit(builder, local_values, &mut dummy_yield_constr);
|
||||
syscalls::eval_ext_circuit(builder, local_values, next_values, yield_constr);
|
||||
}
|
||||
|
||||
@ -44,6 +44,7 @@ pub(crate) fn combined_kernel() -> Kernel {
|
||||
include_str!("asm/memory/memcpy.asm"),
|
||||
include_str!("asm/memory/metadata.asm"),
|
||||
include_str!("asm/memory/packing.asm"),
|
||||
include_str!("asm/memory/syscalls.asm"),
|
||||
include_str!("asm/memory/txn_fields.asm"),
|
||||
include_str!("asm/mpt/accounts.asm"),
|
||||
include_str!("asm/mpt/delete/delete.asm"),
|
||||
@ -67,8 +68,9 @@ pub(crate) fn combined_kernel() -> Kernel {
|
||||
include_str!("asm/ripemd/main.asm"),
|
||||
include_str!("asm/ripemd/memory.asm"),
|
||||
include_str!("asm/ripemd/update.asm"),
|
||||
include_str!("asm/rlp/encode.asm"),
|
||||
include_str!("asm/rlp/decode.asm"),
|
||||
include_str!("asm/rlp/encode.asm"),
|
||||
include_str!("asm/rlp/encode_rlp_string.asm"),
|
||||
include_str!("asm/rlp/num_bytes.asm"),
|
||||
include_str!("asm/rlp/read_to_memory.asm"),
|
||||
include_str!("asm/sha2/compression.asm"),
|
||||
|
||||
@ -4,7 +4,7 @@ global balance:
|
||||
// stack: account_ptr, retdest
|
||||
DUP1 ISZERO %jumpi(retzero) // If the account pointer is null, return 0.
|
||||
%add_const(1)
|
||||
// stack: balance_ptr
|
||||
// stack: balance_ptr, retdest
|
||||
%mload_trie_data
|
||||
// stack: balance, retdest
|
||||
SWAP1 JUMP
|
||||
|
||||
@ -45,7 +45,9 @@ global delegate_call:
|
||||
-> (0, 0, value, sender, self, address, gas)
|
||||
%jump(call_common)
|
||||
|
||||
call_common:
|
||||
// Pre stack: static, should_transfer_value, value, sender, address, code_addr, gas, args_offset, args_size, ret_offset, ret_size, retdest
|
||||
// Post stack: success, leftover_gas
|
||||
global call_common:
|
||||
// stack: static, should_transfer_value, value, sender, address, code_addr, gas, args_offset, args_size, ret_offset, ret_size, retdest
|
||||
%create_context
|
||||
// Store the static flag in metadata.
|
||||
@ -108,3 +110,4 @@ after_call:
|
||||
// stack: new_ctx, ret_offset, ret_size, retdest
|
||||
// TODO: Set RETURNDATA.
|
||||
// TODO: Return to caller w/ EXIT_KERNEL.
|
||||
// TODO: Return leftover gas
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
global get_nonce:
|
||||
// stack: address, retdest
|
||||
// TODO: Replace with actual implementation.
|
||||
POP
|
||||
JUMP
|
||||
|
||||
// Convenience macro to call get_nonce and return where we left off.
|
||||
|
||||
@ -3,39 +3,58 @@
|
||||
|
||||
// TODO: Save checkpoints in @CTX_METADATA_STATE_TRIE_CHECKPOINT_PTR and @SEGMENT_STORAGE_TRIE_CHECKPOINT_PTRS.
|
||||
|
||||
// Pre stack: retdest
|
||||
// Post stack: (empty)
|
||||
global process_normalized_txn:
|
||||
// stack: (empty)
|
||||
// stack: retdest
|
||||
PUSH validate
|
||||
%jump(intrinsic_gas)
|
||||
|
||||
validate:
|
||||
// stack: intrinsic_gas
|
||||
// TODO: Check gas >= intrinsic_gas.
|
||||
// TODO: Check sender_balance >= intrinsic_gas + value.
|
||||
global validate:
|
||||
// stack: intrinsic_gas, retdest
|
||||
// TODO: Check signature? (Or might happen in type_0.asm etc.)
|
||||
// TODO: Assert nonce is correct.
|
||||
// TODO: Assert sender has no code.
|
||||
POP // TODO: Assert gas_limit >= intrinsic_gas.
|
||||
// stack: retdest
|
||||
|
||||
buy_gas:
|
||||
// TODO: Deduct gas from sender (some may be refunded later).
|
||||
global charge_gas:
|
||||
// TODO: Deduct gas limit from sender (some gas may be refunded later).
|
||||
|
||||
increment_nonce:
|
||||
// TODO: Increment nonce.
|
||||
PUSH 0 // TODO: Push sender.
|
||||
%increment_nonce
|
||||
|
||||
process_based_on_type:
|
||||
global process_based_on_type:
|
||||
%is_contract_creation
|
||||
%jumpi(process_contract_creation_txn)
|
||||
%jump(process_message_txn)
|
||||
|
||||
process_contract_creation_txn:
|
||||
// stack: (empty)
|
||||
global process_contract_creation_txn:
|
||||
// stack: retdest
|
||||
// Push the code address & length onto the stack, then call `create`.
|
||||
%mload_txn_field(@TXN_FIELD_DATA_LEN)
|
||||
// stack: code_len
|
||||
// stack: code_len, retdest
|
||||
PUSH 0
|
||||
// stack: code_offset, code_len
|
||||
// stack: code_offset, code_len, retdest
|
||||
PUSH @SEGMENT_TXN_DATA
|
||||
// stack: code_segment, code_offset, code_len
|
||||
// stack: code_segment, code_offset, code_len, retdest
|
||||
PUSH 0 // context
|
||||
// stack: CODE_ADDR, code_len
|
||||
// stack: CODE_ADDR, code_len, retdest
|
||||
%jump(create)
|
||||
|
||||
process_message_txn:
|
||||
// TODO
|
||||
global process_message_txn:
|
||||
// stack: retdest
|
||||
%mload_txn_field(@TXN_FIELD_VALUE)
|
||||
%mload_txn_field(@TXN_FIELD_TO)
|
||||
%mload_txn_field(@TXN_FIELD_ORIGIN)
|
||||
// stack: from, to, amount, retdest
|
||||
%transfer_eth
|
||||
// stack: transfer_eth_status, retdest
|
||||
%jumpi(process_message_txn_insufficient_balance)
|
||||
// stack: retdest
|
||||
// TODO: If code is non-empty, execute it in a new context.
|
||||
JUMP
|
||||
|
||||
global process_message_txn_insufficient_balance:
|
||||
// stack: retdest
|
||||
PANIC // TODO
|
||||
|
||||
@ -7,7 +7,6 @@ global sys_signextend:
|
||||
global sys_slt:
|
||||
global sys_sgt:
|
||||
global sys_sar:
|
||||
global sys_keccak256:
|
||||
global sys_address:
|
||||
global sys_balance:
|
||||
global sys_origin:
|
||||
@ -33,9 +32,6 @@ global sys_gaslimit:
|
||||
global sys_chainid:
|
||||
global sys_selfbalance:
|
||||
global sys_basefee:
|
||||
global sys_mload:
|
||||
global sys_mstore:
|
||||
global sys_mstore8:
|
||||
global sys_sload:
|
||||
global sys_sstore:
|
||||
global sys_msize:
|
||||
|
||||
@ -1,15 +1,20 @@
|
||||
// Transfers some ETH from one address to another. The amount is given in wei.
|
||||
// Pre stack: from, to, amount, retdest
|
||||
// Post stack: (empty)
|
||||
// Post stack: status (0 indicates success)
|
||||
global transfer_eth:
|
||||
// stack: from, to, amount, retdest
|
||||
%stack (from, to, amount, retdest)
|
||||
-> (from, amount, to, amount)
|
||||
-> (from, amount, to, amount, retdest)
|
||||
%deduct_eth
|
||||
// TODO: Handle exception from %deduct_eth?
|
||||
// stack: deduct_eth_status, to, amount, retdest
|
||||
%jumpi(transfer_eth_failure)
|
||||
// stack: to, amount, retdest
|
||||
%add_eth
|
||||
// stack: retdest
|
||||
global transfer_eth_3:
|
||||
%stack (retdest) -> (retdest, 0)
|
||||
JUMP
|
||||
global transfer_eth_failure:
|
||||
%stack (to, amount, retdest) -> (retdest, 1)
|
||||
JUMP
|
||||
|
||||
// Convenience macro to call transfer_eth and return where we left off.
|
||||
@ -31,11 +36,31 @@ global transfer_eth:
|
||||
%%after:
|
||||
%endmacro
|
||||
|
||||
// Returns 0 on success, or 1 if addr has insufficient balance. Panics if addr isn't found in the trie.
|
||||
// Pre stack: addr, amount, retdest
|
||||
// Post stack: status (0 indicates success)
|
||||
global deduct_eth:
|
||||
// stack: addr, amount, retdest
|
||||
%jump(mpt_read_state_trie)
|
||||
deduct_eth_after_read:
|
||||
PANIC // TODO
|
||||
%mpt_read_state_trie
|
||||
// stack: account_ptr, amount, retdest
|
||||
DUP1 ISZERO %jumpi(panic) // If the account pointer is null, return 0.
|
||||
%add_const(1)
|
||||
// stack: balance_ptr, amount, retdest
|
||||
DUP1 %mload_trie_data
|
||||
// stack: balance, balance_ptr, amount, retdest
|
||||
DUP1 DUP4 GT
|
||||
// stack: amount > balance, balance, balance_ptr, amount, retdest
|
||||
%jumpi(deduct_eth_insufficient_balance)
|
||||
%stack (balance, balance_ptr, amount, retdest) -> (balance, amount, balance_ptr, retdest, 0)
|
||||
SUB
|
||||
SWAP1
|
||||
// stack: balance_ptr, balance - amount, retdest, 0
|
||||
%mstore_trie_data
|
||||
// stack: retdest, 0
|
||||
JUMP
|
||||
global deduct_eth_insufficient_balance:
|
||||
%stack (balance, balance_ptr, amount, retdest) -> (retdest, 1)
|
||||
JUMP
|
||||
|
||||
// Convenience macro to call deduct_eth and return where we left off.
|
||||
%macro deduct_eth
|
||||
@ -44,8 +69,40 @@ deduct_eth_after_read:
|
||||
%%after:
|
||||
%endmacro
|
||||
|
||||
// Pre stack: addr, amount, redest
|
||||
// Post stack: (empty)
|
||||
global add_eth:
|
||||
PANIC // TODO
|
||||
// stack: addr, amount, retdest
|
||||
DUP1 %mpt_read_state_trie
|
||||
// stack: account_ptr, addr, amount, retdest
|
||||
DUP1 ISZERO %jumpi(add_eth_new_account) // If the account pointer is null, we need to create the account.
|
||||
%add_const(1)
|
||||
// stack: balance_ptr, addr, amount, retdest
|
||||
DUP1 %mload_trie_data
|
||||
// stack: balance, balance_ptr, addr, amount, retdest
|
||||
%stack (balance, balance_ptr, addr, amount) -> (amount, balance, addr, balance_ptr)
|
||||
ADD
|
||||
// stack: new_balance, addr, balance_ptr, retdest
|
||||
SWAP1 %mstore_trie_data
|
||||
// stack: addr, retdest
|
||||
POP JUMP
|
||||
global add_eth_new_account:
|
||||
// TODO: Skip creation if amount == 0?
|
||||
// stack: null_account_ptr, addr, amount, retdest
|
||||
POP
|
||||
%get_trie_data_size // pointer to new account we're about to create
|
||||
// stack: new_account_ptr, addr, amount, retdest
|
||||
SWAP2
|
||||
// stack: amount, addr, new_account_ptr, retdest
|
||||
PUSH 0 %append_to_trie_data // nonce
|
||||
%append_to_trie_data // balance
|
||||
// stack: addr, new_account_ptr, retdest
|
||||
PUSH @EMPTY_NODE_HASH %append_to_trie_data // storage root
|
||||
PUSH @EMPTY_STRING_HASH %append_to_trie_data // code hash
|
||||
// stack: addr, new_account_ptr, retdest
|
||||
%addr_to_state_key
|
||||
// stack: key, new_account_ptr, retdest
|
||||
%jump(mpt_insert_state_trie)
|
||||
|
||||
// Convenience macro to call add_eth and return where we left off.
|
||||
%macro add_eth
|
||||
|
||||
@ -11,7 +11,7 @@ hash_initial_tries:
|
||||
%mpt_hash_txn_trie %mstore_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_BEFORE)
|
||||
%mpt_hash_receipt_trie %mstore_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_BEFORE)
|
||||
|
||||
txn_loop:
|
||||
global txn_loop:
|
||||
// If the prover has no more txns for us to process, halt.
|
||||
PROVER_INPUT(end_of_txns)
|
||||
%jumpi(hash_final_tries)
|
||||
@ -20,7 +20,7 @@ txn_loop:
|
||||
PUSH txn_loop
|
||||
%jump(route_txn)
|
||||
|
||||
hash_final_tries:
|
||||
global hash_final_tries:
|
||||
%mpt_hash_state_trie %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER)
|
||||
%mpt_hash_txn_trie %mstore_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER)
|
||||
%mpt_hash_receipt_trie %mstore_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_AFTER)
|
||||
|
||||
82
evm/src/cpu/kernel/asm/memory/syscalls.asm
Normal file
82
evm/src/cpu/kernel/asm/memory/syscalls.asm
Normal file
@ -0,0 +1,82 @@
|
||||
global sys_mload:
|
||||
// stack: kexit_info, offset
|
||||
PUSH 0 // acc = 0
|
||||
// stack: acc, kexit_info, offset
|
||||
DUP3 %add_const( 0) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xf8) ADD
|
||||
DUP3 %add_const( 1) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xf0) ADD
|
||||
DUP3 %add_const( 2) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xe8) ADD
|
||||
DUP3 %add_const( 3) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xe0) ADD
|
||||
DUP3 %add_const( 4) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xd8) ADD
|
||||
DUP3 %add_const( 5) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xd0) ADD
|
||||
DUP3 %add_const( 6) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xc8) ADD
|
||||
DUP3 %add_const( 7) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xc0) ADD
|
||||
DUP3 %add_const( 8) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xb8) ADD
|
||||
DUP3 %add_const( 9) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xb0) ADD
|
||||
DUP3 %add_const(10) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xa8) ADD
|
||||
DUP3 %add_const(11) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xa0) ADD
|
||||
DUP3 %add_const(12) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x98) ADD
|
||||
DUP3 %add_const(13) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x90) ADD
|
||||
DUP3 %add_const(14) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x88) ADD
|
||||
DUP3 %add_const(15) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x80) ADD
|
||||
DUP3 %add_const(16) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x78) ADD
|
||||
DUP3 %add_const(17) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x70) ADD
|
||||
DUP3 %add_const(18) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x68) ADD
|
||||
DUP3 %add_const(19) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x60) ADD
|
||||
DUP3 %add_const(20) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x58) ADD
|
||||
DUP3 %add_const(21) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x50) ADD
|
||||
DUP3 %add_const(22) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x48) ADD
|
||||
DUP3 %add_const(23) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x40) ADD
|
||||
DUP3 %add_const(24) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x38) ADD
|
||||
DUP3 %add_const(25) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x30) ADD
|
||||
DUP3 %add_const(26) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x28) ADD
|
||||
DUP3 %add_const(27) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x20) ADD
|
||||
DUP3 %add_const(28) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x18) ADD
|
||||
DUP3 %add_const(29) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x10) ADD
|
||||
DUP3 %add_const(30) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x08) ADD
|
||||
DUP3 %add_const(31) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x00) ADD
|
||||
%stack (acc, kexit_info, offset) -> (kexit_info, acc)
|
||||
EXIT_KERNEL
|
||||
|
||||
global sys_mstore:
|
||||
// stack: kexit_info, offset, value
|
||||
DUP3 PUSH 0 BYTE DUP3 %add_const( 0) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 1 BYTE DUP3 %add_const( 1) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 2 BYTE DUP3 %add_const( 2) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 3 BYTE DUP3 %add_const( 3) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 4 BYTE DUP3 %add_const( 4) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 5 BYTE DUP3 %add_const( 5) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 6 BYTE DUP3 %add_const( 6) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 7 BYTE DUP3 %add_const( 7) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 8 BYTE DUP3 %add_const( 8) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 9 BYTE DUP3 %add_const( 9) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 10 BYTE DUP3 %add_const(10) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 11 BYTE DUP3 %add_const(11) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 12 BYTE DUP3 %add_const(12) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 13 BYTE DUP3 %add_const(13) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 14 BYTE DUP3 %add_const(14) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 15 BYTE DUP3 %add_const(15) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 16 BYTE DUP3 %add_const(16) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 17 BYTE DUP3 %add_const(17) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 18 BYTE DUP3 %add_const(18) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 19 BYTE DUP3 %add_const(19) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 20 BYTE DUP3 %add_const(20) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 21 BYTE DUP3 %add_const(21) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 22 BYTE DUP3 %add_const(22) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 23 BYTE DUP3 %add_const(23) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 24 BYTE DUP3 %add_const(24) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 25 BYTE DUP3 %add_const(25) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 26 BYTE DUP3 %add_const(26) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 27 BYTE DUP3 %add_const(27) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 28 BYTE DUP3 %add_const(28) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 29 BYTE DUP3 %add_const(29) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 30 BYTE DUP3 %add_const(30) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
DUP3 PUSH 31 BYTE DUP3 %add_const(31) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
%stack (kexit_info, offset, value) -> (kexit_info)
|
||||
EXIT_KERNEL
|
||||
|
||||
global sys_mstore8:
|
||||
// stack: kexit_info, offset, value
|
||||
%stack (kexit_info, offset, value) -> (offset, value, kexit_info)
|
||||
%mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||
// stack: kexit_info
|
||||
EXIT_KERNEL
|
||||
@ -1,6 +1,8 @@
|
||||
// Insertion logic specific to a particular trie.
|
||||
|
||||
// Mutate the state trie, inserting the given key-value pair.
|
||||
// Pre stack: key, value_ptr, retdest
|
||||
// Post stack: (empty)
|
||||
global mpt_insert_state_trie:
|
||||
// stack: key, value_ptr, retdest
|
||||
%stack (key, value_ptr)
|
||||
|
||||
@ -8,7 +8,20 @@ global encode_rlp_scalar:
|
||||
%gt_const(0x7f)
|
||||
%jumpi(encode_rlp_scalar_medium)
|
||||
|
||||
// This is the "small" case, where the value is its own encoding.
|
||||
// Else, if scalar != 0, this is the "small" case, where the value is its own encoding.
|
||||
DUP2 %jumpi(encode_rlp_scalar_small)
|
||||
|
||||
// scalar = 0, so BE(scalar) is the empty string, which RLP encodes as a single byte 0x80.
|
||||
// stack: pos, scalar, retdest
|
||||
%stack (pos, scalar) -> (pos, 0x80, pos)
|
||||
%mstore_rlp
|
||||
// stack: pos, retdest
|
||||
%increment
|
||||
// stack: pos', retdest
|
||||
SWAP1
|
||||
JUMP
|
||||
|
||||
encode_rlp_scalar_small:
|
||||
// stack: pos, scalar, retdest
|
||||
%stack (pos, scalar) -> (pos, scalar, pos)
|
||||
// stack: pos, scalar, pos, retdest
|
||||
@ -127,14 +140,8 @@ encode_rlp_multi_byte_string_prefix_large:
|
||||
// stack: pos, len_of_len, str_len, retdest
|
||||
%increment
|
||||
// stack: pos', len_of_len, str_len, retdest
|
||||
%stack (pos, len_of_len, str_len)
|
||||
-> (pos, str_len, len_of_len,
|
||||
encode_rlp_multi_byte_string_prefix_large_done_writing_len)
|
||||
%stack (pos, len_of_len, str_len) -> (pos, str_len, len_of_len)
|
||||
%jump(mstore_unpacking_rlp)
|
||||
encode_rlp_multi_byte_string_prefix_large_done_writing_len:
|
||||
// stack: pos'', retdest
|
||||
SWAP1
|
||||
JUMP
|
||||
|
||||
%macro encode_rlp_multi_byte_string_prefix
|
||||
%stack (pos, str_len) -> (pos, str_len, %%after)
|
||||
|
||||
72
evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm
Normal file
72
evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm
Normal file
@ -0,0 +1,72 @@
|
||||
// Encodes an arbitrary string, given a pointer and length.
|
||||
// Pre stack: pos, ADDR: 3, len, retdest
|
||||
// Post stack: pos'
|
||||
global encode_rlp_string:
|
||||
// stack: pos, ADDR: 3, len, retdest
|
||||
DUP5 %eq_const(1)
|
||||
// stack: len == 1, pos, ADDR: 3, len, retdest
|
||||
DUP5 DUP5 DUP5 // ADDR: 3
|
||||
MLOAD_GENERAL
|
||||
// stack: first_byte, len == 1, pos, ADDR: 3, len, retdest
|
||||
%lt_const(128)
|
||||
MUL // cheaper than AND
|
||||
// stack: single_small_byte, pos, ADDR: 3, len, retdest
|
||||
%jumpi(encode_rlp_string_small_single_byte)
|
||||
|
||||
// stack: pos, ADDR: 3, len, retdest
|
||||
DUP5 %gt_const(55)
|
||||
// stack: len > 55, pos, ADDR: 3, len, retdest
|
||||
%jumpi(encode_rlp_string_large)
|
||||
|
||||
global encode_rlp_string_small:
|
||||
// stack: pos, ADDR: 3, len, retdest
|
||||
DUP5 // len
|
||||
%add_const(0x80)
|
||||
// stack: first_byte, pos, ADDR: 3, len, retdest
|
||||
DUP2
|
||||
// stack: pos, first_byte, pos, ADDR: 3, len, retdest
|
||||
%mstore_rlp
|
||||
// stack: pos, ADDR: 3, len, retdest
|
||||
%increment
|
||||
// stack: pos', ADDR: 3, len, retdest
|
||||
DUP5 DUP2 ADD // pos'' = pos' + len
|
||||
// stack: pos'', pos', ADDR: 3, len, retdest
|
||||
%stack (pos2, pos1, ADDR: 3, len, retdest)
|
||||
-> (0, @SEGMENT_RLP_RAW, pos1, ADDR, len, retdest, pos2)
|
||||
%jump(memcpy)
|
||||
|
||||
global encode_rlp_string_small_single_byte:
|
||||
// stack: pos, ADDR: 3, len, retdest
|
||||
%stack (pos, ADDR: 3, len) -> (ADDR, pos)
|
||||
MLOAD_GENERAL
|
||||
// stack: byte, pos, retdest
|
||||
DUP2
|
||||
%mstore_rlp
|
||||
// stack: pos, retdest
|
||||
%increment
|
||||
JUMP
|
||||
|
||||
global encode_rlp_string_large:
|
||||
// stack: pos, ADDR: 3, len, retdest
|
||||
DUP5 %num_bytes
|
||||
// stack: len_of_len, pos, ADDR: 3, len, retdest
|
||||
SWAP1
|
||||
DUP2 // len_of_len
|
||||
%add_const(0xb7)
|
||||
// stack: first_byte, pos, len_of_len, ADDR: 3, len, retdest
|
||||
DUP2
|
||||
// stack: pos, first_byte, pos, len_of_len, ADDR: 3, len, retdest
|
||||
%mstore_rlp
|
||||
// stack: pos, len_of_len, ADDR: 3, len, retdest
|
||||
%increment
|
||||
// stack: pos', len_of_len, ADDR: 3, len, retdest
|
||||
%stack (pos, len_of_len, ADDR: 3, len)
|
||||
-> (pos, len, len_of_len, encode_rlp_string_large_after_writing_len, ADDR, len)
|
||||
%jump(mstore_unpacking_rlp)
|
||||
global encode_rlp_string_large_after_writing_len:
|
||||
// stack: pos'', ADDR: 3, len, retdest
|
||||
DUP5 DUP2 ADD // pos''' = pos'' + len
|
||||
// stack: pos''', pos'', ADDR: 3, len, retdest
|
||||
%stack (pos3, pos2, ADDR: 3, len, retdest)
|
||||
-> (0, @SEGMENT_RLP_RAW, pos2, ADDR, len, retdest, pos3)
|
||||
%jump(memcpy)
|
||||
@ -127,7 +127,92 @@ parse_r:
|
||||
%mstore_txn_field(@TXN_FIELD_S)
|
||||
// stack: retdest
|
||||
|
||||
// TODO: Write the signed txn data to memory, where it can be hashed and
|
||||
// checked against the signature.
|
||||
type_0_compute_signed_data:
|
||||
// If a chain_id is present in v, the signed data is
|
||||
// keccak256(rlp([nonce, gas_price, gas_limit, to, value, data, chain_id, 0, 0]))
|
||||
// otherwise, it is
|
||||
// keccak256(rlp([nonce, gas_price, gas_limit, to, value, data]))
|
||||
|
||||
%mload_txn_field(@TXN_FIELD_NONCE)
|
||||
// stack: nonce, retdest
|
||||
PUSH 9 // We start at 9 to leave room to prepend the largest possible RLP list header.
|
||||
// stack: rlp_pos, nonce, retdest
|
||||
%encode_rlp_scalar
|
||||
// stack: rlp_pos, retdest
|
||||
|
||||
%mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS)
|
||||
SWAP1 %encode_rlp_scalar
|
||||
// stack: rlp_pos, retdest
|
||||
|
||||
%mload_txn_field(@TXN_FIELD_GAS_LIMIT)
|
||||
SWAP1 %encode_rlp_scalar
|
||||
// stack: rlp_pos, retdest
|
||||
|
||||
%mload_txn_field(@TXN_FIELD_TO)
|
||||
SWAP1 %encode_rlp_160
|
||||
// stack: rlp_pos, retdest
|
||||
|
||||
%mload_txn_field(@TXN_FIELD_VALUE)
|
||||
SWAP1 %encode_rlp_scalar
|
||||
// stack: rlp_pos, retdest
|
||||
|
||||
// Encode txn data.
|
||||
%mload_txn_field(@TXN_FIELD_DATA_LEN)
|
||||
PUSH 0 // ADDR.virt
|
||||
PUSH @SEGMENT_TXN_DATA
|
||||
PUSH 0 // ADDR.context
|
||||
// stack: ADDR: 3, len, rlp_pos, retdest
|
||||
PUSH after_serializing_txn_data
|
||||
// stack: after_serializing_txn_data, ADDR: 3, len, rlp_pos, retdest
|
||||
SWAP5
|
||||
// stack: rlp_pos, ADDR: 3, len, after_serializing_txn_data, retdest
|
||||
%jump(encode_rlp_string)
|
||||
|
||||
after_serializing_txn_data:
|
||||
// stack: rlp_pos, retdest
|
||||
%mload_txn_field(@TXN_FIELD_CHAIN_ID_PRESENT)
|
||||
ISZERO %jumpi(finish_rlp_list)
|
||||
// stack: rlp_pos, retdest
|
||||
|
||||
%mload_txn_field(@TXN_FIELD_CHAIN_ID)
|
||||
SWAP1 %encode_rlp_scalar
|
||||
// stack: rlp_pos, retdest
|
||||
|
||||
PUSH 0
|
||||
SWAP1 %encode_rlp_scalar
|
||||
// stack: rlp_pos, retdest
|
||||
|
||||
PUSH 0
|
||||
SWAP1 %encode_rlp_scalar
|
||||
// stack: rlp_pos, retdest
|
||||
|
||||
finish_rlp_list:
|
||||
%prepend_rlp_list_prefix
|
||||
// stack: start_pos, rlp_len, retdest
|
||||
PUSH @SEGMENT_RLP_RAW
|
||||
PUSH 0 // context
|
||||
// stack: ADDR: 3, rlp_len, retdest
|
||||
KECCAK_GENERAL
|
||||
// stack: hash, retdest
|
||||
|
||||
%mload_txn_field(@TXN_FIELD_S)
|
||||
%mload_txn_field(@TXN_FIELD_R)
|
||||
%mload_txn_field(@TXN_FIELD_Y_PARITY) %add_const(27) // ecrecover interprets v as y_parity + 27
|
||||
|
||||
PUSH store_origin
|
||||
// stack: store_origin, v, r, s, hash, retdest
|
||||
SWAP4
|
||||
// stack: hash, v, r, s, store_origin, retdest
|
||||
%jump(ecrecover)
|
||||
|
||||
store_origin:
|
||||
// stack: address, retdest
|
||||
// If ecrecover returned u256::MAX, that indicates failure.
|
||||
DUP1
|
||||
%eq_const(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
|
||||
%jumpi(panic)
|
||||
|
||||
// stack: address, retdest
|
||||
%mstore_txn_field(@TXN_FIELD_ORIGIN)
|
||||
// stack: retdest
|
||||
%jump(process_normalized_txn)
|
||||
|
||||
@ -1,3 +1,14 @@
|
||||
global sys_keccak256:
|
||||
// stack: kexit_info, offset, len
|
||||
%stack (kexit_info, offset, len) -> (offset, len, kexit_info)
|
||||
PUSH @SEGMENT_MAIN_MEMORY
|
||||
GET_CONTEXT
|
||||
// stack: ADDR: 3, len, kexit_info
|
||||
KECCAK_GENERAL
|
||||
// stack: hash, kexit_info
|
||||
SWAP1
|
||||
EXIT_KERNEL
|
||||
|
||||
// Computes Keccak256(input_word). Clobbers @SEGMENT_KERNEL_GENERAL.
|
||||
//
|
||||
// Pre stack: input_word
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ethereum_types::U256;
|
||||
use itertools::izip;
|
||||
use itertools::{izip, Itertools};
|
||||
use keccak_hash::keccak;
|
||||
use log::debug;
|
||||
|
||||
@ -28,6 +28,7 @@ pub struct Kernel {
|
||||
pub(crate) code_hash: [u32; 8],
|
||||
|
||||
pub(crate) global_labels: HashMap<String, usize>,
|
||||
pub(crate) ordered_labels: Vec<String>,
|
||||
|
||||
/// Map from `PROVER_INPUT` offsets to their corresponding `ProverInputFn`.
|
||||
pub(crate) prover_inputs: HashMap<usize, ProverInputFn>,
|
||||
@ -43,18 +44,30 @@ impl Kernel {
|
||||
let code_hash = std::array::from_fn(|i| {
|
||||
u32::from_le_bytes(std::array::from_fn(|j| code_hash_bytes[i * 4 + j]))
|
||||
});
|
||||
let ordered_labels = global_labels
|
||||
.keys()
|
||||
.cloned()
|
||||
.sorted_by_key(|label| global_labels[label])
|
||||
.collect();
|
||||
Self {
|
||||
code,
|
||||
code_hash,
|
||||
global_labels,
|
||||
ordered_labels,
|
||||
prover_inputs,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a string representation of the current offset for debugging purposes.
|
||||
pub(crate) fn offset_name(&self, offset: usize) -> String {
|
||||
self.offset_label(offset)
|
||||
.unwrap_or_else(|| offset.to_string())
|
||||
match self
|
||||
.ordered_labels
|
||||
.binary_search_by_key(&offset, |label| self.global_labels[label])
|
||||
{
|
||||
Ok(idx) => self.ordered_labels[idx].clone(),
|
||||
Err(0) => offset.to_string(),
|
||||
Err(idx) => format!("{}, below {}", offset, self.ordered_labels[idx - 1]),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn offset_label(&self, offset: usize) -> Option<String> {
|
||||
|
||||
@ -49,7 +49,12 @@ pub fn evm_constants() -> HashMap<String, U256> {
|
||||
c
|
||||
}
|
||||
|
||||
const HASH_CONSTANTS: [(&str, [u8; 32]); 1] = [
|
||||
const HASH_CONSTANTS: [(&str, [u8; 32]); 2] = [
|
||||
// Hash of an empty string: keccak(b'').hex()
|
||||
(
|
||||
"EMPTY_STRING_HASH",
|
||||
hex!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"),
|
||||
),
|
||||
// Hash of an empty node: keccak(rlp.encode(b'')).hex()
|
||||
(
|
||||
"EMPTY_NODE_HASH",
|
||||
|
||||
@ -17,10 +17,11 @@ pub(crate) enum NormalizedTxnField {
|
||||
YParity = 9,
|
||||
R = 10,
|
||||
S = 11,
|
||||
Origin = 12,
|
||||
}
|
||||
|
||||
impl NormalizedTxnField {
|
||||
pub(crate) const COUNT: usize = 12;
|
||||
pub(crate) const COUNT: usize = 13;
|
||||
|
||||
pub(crate) fn all() -> [Self; Self::COUNT] {
|
||||
[
|
||||
@ -36,6 +37,7 @@ impl NormalizedTxnField {
|
||||
Self::YParity,
|
||||
Self::R,
|
||||
Self::S,
|
||||
Self::Origin,
|
||||
]
|
||||
}
|
||||
|
||||
@ -54,6 +56,7 @@ impl NormalizedTxnField {
|
||||
NormalizedTxnField::YParity => "TXN_FIELD_Y_PARITY",
|
||||
NormalizedTxnField::R => "TXN_FIELD_R",
|
||||
NormalizedTxnField::S => "TXN_FIELD_S",
|
||||
NormalizedTxnField::Origin => "TXN_FIELD_ORIGIN",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,8 +72,8 @@ pub fn eval_packed<P: PackedField>(
|
||||
|
||||
// This memory channel is constrained in `stack.rs`.
|
||||
let output = lv.mem_channels[NUM_GP_CHANNELS - 1].value;
|
||||
// Push current PC to stack
|
||||
yield_constr.constraint(filter * (output[0] - lv.program_counter));
|
||||
// Push current PC + 1 to stack
|
||||
yield_constr.constraint(filter * (output[0] - (lv.program_counter + P::ONES)));
|
||||
// Push current kernel flag to stack (share register with PC)
|
||||
yield_constr.constraint(filter * (output[1] - lv.is_kernel_mode));
|
||||
// Zero the rest of that register
|
||||
@ -180,9 +180,10 @@ pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
|
||||
|
||||
// This memory channel is constrained in `stack.rs`.
|
||||
let output = lv.mem_channels[NUM_GP_CHANNELS - 1].value;
|
||||
// Push current PC to stack
|
||||
// Push current PC + 1 to stack
|
||||
{
|
||||
let diff = builder.sub_extension(output[0], lv.program_counter);
|
||||
let pc_plus_1 = builder.add_const_extension(lv.program_counter, F::ONE);
|
||||
let diff = builder.sub_extension(output[0], pc_plus_1);
|
||||
let constr = builder.mul_extension(filter, diff);
|
||||
yield_constr.constraint(builder, constr);
|
||||
}
|
||||
|
||||
@ -16,6 +16,17 @@ pub struct AccountRlp {
|
||||
pub code_hash: H256,
|
||||
}
|
||||
|
||||
impl Default for AccountRlp {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
nonce: U256::zero(),
|
||||
balance: U256::zero(),
|
||||
storage_root: PartialTrie::Empty.calc_hash(),
|
||||
code_hash: keccak([]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn all_mpt_prover_inputs_reversed(trie_inputs: &TrieInputs) -> Vec<U256> {
|
||||
let mut inputs = all_mpt_prover_inputs(trie_inputs);
|
||||
inputs.reverse();
|
||||
|
||||
@ -5,6 +5,7 @@ use plonky2::field::types::Field;
|
||||
|
||||
use crate::cpu::columns::CpuColumnsView;
|
||||
use crate::cpu::kernel::aggregator::KERNEL;
|
||||
use crate::cpu::kernel::assembler::BYTES_PER_OFFSET;
|
||||
use crate::cpu::membus::NUM_GP_CHANNELS;
|
||||
use crate::cpu::simple_logic::eq_iszero::generate_pinv_diff;
|
||||
use crate::generation::state::GenerationState;
|
||||
@ -20,9 +21,6 @@ use crate::{arithmetic, logic};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum Operation {
|
||||
Push(u8),
|
||||
Dup(u8),
|
||||
Swap(u8),
|
||||
Iszero,
|
||||
Not,
|
||||
Byte,
|
||||
@ -39,6 +37,9 @@ pub(crate) enum Operation {
|
||||
Pc,
|
||||
Gas,
|
||||
Jumpdest,
|
||||
Push(u8),
|
||||
Dup(u8),
|
||||
Swap(u8),
|
||||
GetContext,
|
||||
SetContext,
|
||||
ConsumeGas,
|
||||
@ -190,6 +191,10 @@ pub(crate) fn generate_jump<F: Field>(
|
||||
state.traces.push_memory(log_in0);
|
||||
state.traces.push_cpu(row);
|
||||
state.registers.program_counter = u256_saturating_cast_usize(dst);
|
||||
log::debug!(
|
||||
"Jumping to {}",
|
||||
KERNEL.offset_name(state.registers.program_counter)
|
||||
);
|
||||
// TODO: Set other cols like input0_upper_sum_inv.
|
||||
Ok(())
|
||||
}
|
||||
@ -206,12 +211,47 @@ pub(crate) fn generate_jumpi<F: Field>(
|
||||
state.registers.program_counter = if cond.is_zero() {
|
||||
state.registers.program_counter + 1
|
||||
} else {
|
||||
u256_saturating_cast_usize(dst)
|
||||
let dst_usize = u256_saturating_cast_usize(dst);
|
||||
log::debug!(
|
||||
"Jumping to {}",
|
||||
KERNEL.offset_name(state.registers.program_counter)
|
||||
);
|
||||
dst_usize
|
||||
};
|
||||
// TODO: Set other cols like input0_upper_sum_inv.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn generate_jumpdest<F: Field>(
|
||||
state: &mut GenerationState<F>,
|
||||
row: CpuColumnsView<F>,
|
||||
) -> Result<(), ProgramError> {
|
||||
state.traces.push_cpu(row);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn generate_get_context<F: Field>(
|
||||
state: &mut GenerationState<F>,
|
||||
mut row: CpuColumnsView<F>,
|
||||
) -> Result<(), ProgramError> {
|
||||
let ctx = state.registers.context.into();
|
||||
let write = stack_push_log_and_fill(state, &mut row, ctx)?;
|
||||
state.traces.push_memory(write);
|
||||
state.traces.push_cpu(row);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn generate_set_context<F: Field>(
|
||||
state: &mut GenerationState<F>,
|
||||
mut row: CpuColumnsView<F>,
|
||||
) -> Result<(), ProgramError> {
|
||||
let [(ctx, log_in)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?;
|
||||
state.registers.context = ctx.as_usize();
|
||||
state.traces.push_memory(log_in);
|
||||
state.traces.push_cpu(row);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn generate_push<F: Field>(
|
||||
n: u8,
|
||||
state: &mut GenerationState<F>,
|
||||
@ -386,7 +426,9 @@ pub(crate) fn generate_syscall<F: Field>(
|
||||
mut row: CpuColumnsView<F>,
|
||||
) -> Result<(), ProgramError> {
|
||||
let handler_jumptable_addr = KERNEL.global_labels["syscall_jumptable"];
|
||||
let handler_addr_addr = handler_jumptable_addr + (opcode as usize);
|
||||
let handler_addr_addr =
|
||||
handler_jumptable_addr + (opcode as usize) * (BYTES_PER_OFFSET as usize);
|
||||
assert_eq!(BYTES_PER_OFFSET, 3, "Code below assumes 3 bytes per offset");
|
||||
let (handler_addr0, log_in0) = mem_read_gp_with_log_and_fill(
|
||||
0,
|
||||
MemoryAddress::new(0, Segment::Code, handler_addr_addr),
|
||||
@ -409,11 +451,12 @@ pub(crate) fn generate_syscall<F: Field>(
|
||||
let handler_addr = (handler_addr0 << 16) + (handler_addr1 << 8) + handler_addr2;
|
||||
let new_program_counter = handler_addr.as_usize();
|
||||
|
||||
let syscall_info = U256::from(state.registers.program_counter)
|
||||
let syscall_info = U256::from(state.registers.program_counter + 1)
|
||||
+ (U256::from(u64::from(state.registers.is_kernel)) << 32);
|
||||
let log_out = stack_push_log_and_fill(state, &mut row, syscall_info)?;
|
||||
|
||||
state.registers.program_counter = new_program_counter;
|
||||
log::debug!("Syscall to {}", KERNEL.offset_name(new_program_counter));
|
||||
state.registers.is_kernel = true;
|
||||
|
||||
state.traces.push_memory(log_in0);
|
||||
@ -448,14 +491,19 @@ pub(crate) fn generate_exit_kernel<F: Field>(
|
||||
mut row: CpuColumnsView<F>,
|
||||
) -> Result<(), ProgramError> {
|
||||
let [(kexit_info, log_in)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?;
|
||||
let kexit_info_u64: [u64; 4] = kexit_info.0;
|
||||
let program_counter = kexit_info_u64[0] as usize;
|
||||
let is_kernel_mode_val = (kexit_info_u64[1] >> 32) as u32;
|
||||
let kexit_info_u64 = kexit_info.0[0];
|
||||
let program_counter = kexit_info_u64 as u32 as usize;
|
||||
let is_kernel_mode_val = (kexit_info_u64 >> 32) as u32;
|
||||
assert!(is_kernel_mode_val == 0 || is_kernel_mode_val == 1);
|
||||
let is_kernel_mode = is_kernel_mode_val != 0;
|
||||
|
||||
state.registers.program_counter = program_counter;
|
||||
state.registers.is_kernel = is_kernel_mode;
|
||||
log::debug!(
|
||||
"Exiting to {}, is_kernel={}",
|
||||
KERNEL.offset_name(program_counter),
|
||||
is_kernel_mode
|
||||
);
|
||||
|
||||
state.traces.push_memory(log_in);
|
||||
state.traces.push_cpu(row);
|
||||
|
||||
@ -113,6 +113,10 @@ fn decode(registers: RegistersState, opcode: u8) -> Result<Operation, ProgramErr
|
||||
(0xa2, _) => Ok(Operation::Syscall(opcode)),
|
||||
(0xa3, _) => Ok(Operation::Syscall(opcode)),
|
||||
(0xa4, _) => Ok(Operation::Syscall(opcode)),
|
||||
(0xa5, _) => panic!(
|
||||
"Kernel panic at {}",
|
||||
KERNEL.offset_name(registers.program_counter)
|
||||
),
|
||||
(0xf0, _) => Ok(Operation::Syscall(opcode)),
|
||||
(0xf1, _) => Ok(Operation::Syscall(opcode)),
|
||||
(0xf2, _) => Ok(Operation::Syscall(opcode)),
|
||||
@ -128,7 +132,10 @@ fn decode(registers: RegistersState, opcode: u8) -> Result<Operation, ProgramErr
|
||||
(0xfc, true) => Ok(Operation::MstoreGeneral),
|
||||
(0xfd, _) => Ok(Operation::Syscall(opcode)),
|
||||
(0xff, _) => Ok(Operation::Syscall(opcode)),
|
||||
_ => Err(ProgramError::InvalidOpcode),
|
||||
_ => {
|
||||
log::warn!("Invalid opcode: {}", opcode);
|
||||
Err(ProgramError::InvalidOpcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,9 +210,9 @@ fn perform_op<F: Field>(
|
||||
Operation::Jumpi => generate_jumpi(state, row)?,
|
||||
Operation::Pc => todo!(),
|
||||
Operation::Gas => todo!(),
|
||||
Operation::Jumpdest => todo!(),
|
||||
Operation::GetContext => todo!(),
|
||||
Operation::SetContext => todo!(),
|
||||
Operation::Jumpdest => generate_jumpdest(state, row)?,
|
||||
Operation::GetContext => generate_get_context(state, row)?,
|
||||
Operation::SetContext => generate_set_context(state, row)?,
|
||||
Operation::ConsumeGas => todo!(),
|
||||
Operation::ExitKernel => generate_exit_kernel(state, row)?,
|
||||
Operation::MloadGeneral => generate_mload_general(state, row)?,
|
||||
@ -219,12 +226,6 @@ fn perform_op<F: Field>(
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
if let Some(label) = KERNEL.offset_label(state.registers.program_counter) {
|
||||
if !label.starts_with("halt_pc") {
|
||||
log::debug!("At {label}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -239,19 +240,39 @@ fn try_perform_instruction<F: Field>(state: &mut GenerationState<F>) -> Result<(
|
||||
|
||||
let opcode = read_code_memory(state, &mut row);
|
||||
let op = decode(state.registers, opcode)?;
|
||||
let pc = state.registers.program_counter;
|
||||
|
||||
log::trace!("\nCycle {}", state.traces.clock());
|
||||
log::trace!(
|
||||
"Stack: {:?}",
|
||||
log_instruction(state, op);
|
||||
|
||||
fill_op_flag(op, &mut row);
|
||||
|
||||
perform_op(state, op, row)
|
||||
}
|
||||
|
||||
fn log_instruction<F: Field>(state: &mut GenerationState<F>, op: Operation) {
|
||||
let pc = state.registers.program_counter;
|
||||
let is_interesting_offset = KERNEL
|
||||
.offset_label(pc)
|
||||
.filter(|label| !label.starts_with("halt_pc"))
|
||||
.is_some();
|
||||
let level = if is_interesting_offset {
|
||||
log::Level::Debug
|
||||
} else {
|
||||
log::Level::Trace
|
||||
};
|
||||
log::log!(
|
||||
level,
|
||||
"Cycle {}, pc={}, instruction={:?}, stack={:?}",
|
||||
state.traces.clock(),
|
||||
KERNEL.offset_name(pc),
|
||||
op,
|
||||
(0..state.registers.stack_len)
|
||||
.map(|i| stack_peek(state, i).unwrap())
|
||||
.collect_vec()
|
||||
);
|
||||
log::trace!("Executing {:?} at {}", op, KERNEL.offset_name(pc));
|
||||
fill_op_flag(op, &mut row);
|
||||
|
||||
perform_op(state, op, row)
|
||||
if state.registers.is_kernel && pc >= KERNEL.code.len() {
|
||||
panic!("Kernel PC is out of range: {}", pc);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_error<F: Field>(_state: &mut GenerationState<F>) {
|
||||
@ -270,7 +291,8 @@ pub(crate) fn transition<F: Field>(state: &mut GenerationState<F>) {
|
||||
}
|
||||
Err(e) => {
|
||||
if state.registers.is_kernel {
|
||||
panic!("exception in kernel mode: {:?}", e);
|
||||
let offset_name = KERNEL.offset_name(state.registers.program_counter);
|
||||
panic!("exception in kernel mode at {}: {:?}", offset_name, e);
|
||||
}
|
||||
state.rollback(checkpoint);
|
||||
handle_error(state)
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use eth_trie_utils::partial_trie::PartialTrie;
|
||||
use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV};
|
||||
use eth_trie_utils::partial_trie::{Nibbles, PartialTrie};
|
||||
use ethereum_types::U256;
|
||||
use hex_literal::hex;
|
||||
use keccak_hash::keccak;
|
||||
use plonky2::field::goldilocks_field::GoldilocksField;
|
||||
use plonky2::plonk::config::PoseidonGoldilocksConfig;
|
||||
use plonky2::util::timing::TimingTree;
|
||||
use plonky2_evm::all_stark::AllStark;
|
||||
use plonky2_evm::config::StarkConfig;
|
||||
use plonky2_evm::generation::mpt::AccountRlp;
|
||||
use plonky2_evm::generation::{GenerationInputs, TrieInputs};
|
||||
use plonky2_evm::proof::BlockMetadata;
|
||||
use plonky2_evm::prover::prove;
|
||||
@ -18,28 +22,95 @@ type C = PoseidonGoldilocksConfig;
|
||||
|
||||
/// Test a simple token transfer to a new address.
|
||||
#[test]
|
||||
#[ignore] // TODO: Won't work until txn parsing, storage, etc. are implemented.
|
||||
fn test_simple_transfer() -> anyhow::Result<()> {
|
||||
init_logger();
|
||||
|
||||
let all_stark = AllStark::<F, D>::default();
|
||||
let config = StarkConfig::standard_fast_config();
|
||||
|
||||
let block_metadata = BlockMetadata::default();
|
||||
let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23");
|
||||
let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0");
|
||||
let sender_state_key = keccak(sender);
|
||||
let to_state_key = keccak(to);
|
||||
let sender_nibbles = Nibbles::from(sender_state_key);
|
||||
let to_nibbles = Nibbles::from(to_state_key);
|
||||
let value = U256::from(100u32);
|
||||
|
||||
let txn = hex!("f85f050a82520894000000000000000000000000000000000000000064801ca0fa56df5d988638fad8798e5ef75a1e1125dc7fb55d2ac4bce25776a63f0c2967a02cb47a5579eb5f83a1cabe4662501c0059f1b58e60ef839a1b0da67af6b9fb38");
|
||||
let sender_account_before = AccountRlp {
|
||||
nonce: 5.into(),
|
||||
balance: eth_to_wei(100_000.into()),
|
||||
storage_root: PartialTrie::Empty.calc_hash(),
|
||||
code_hash: keccak([]),
|
||||
};
|
||||
|
||||
let state_trie_before = PartialTrie::Leaf {
|
||||
nibbles: sender_nibbles,
|
||||
value: rlp::encode(&sender_account_before).to_vec(),
|
||||
};
|
||||
let tries_before = TrieInputs {
|
||||
state_trie: state_trie_before,
|
||||
transactions_trie: PartialTrie::Empty,
|
||||
receipts_trie: PartialTrie::Empty,
|
||||
storage_tries: vec![],
|
||||
};
|
||||
|
||||
// Generated using a little py-evm script.
|
||||
let txn = hex!("f861050a8255f094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0648242421ba02c89eb757d9deeb1f5b3859a9d4d679951ef610ac47ad4608dc142beb1b7e313a05af7e9fbab825455d36c36c7f4cfcafbeafa9a77bdff936b52afb36d4fe4bcdd");
|
||||
|
||||
let block_metadata = BlockMetadata::default();
|
||||
|
||||
let inputs = GenerationInputs {
|
||||
signed_txns: vec![txn.to_vec()],
|
||||
tries: TrieInputs {
|
||||
state_trie: PartialTrie::Empty,
|
||||
transactions_trie: PartialTrie::Empty,
|
||||
receipts_trie: PartialTrie::Empty,
|
||||
storage_tries: vec![],
|
||||
},
|
||||
tries: tries_before,
|
||||
contract_code: HashMap::new(),
|
||||
block_metadata,
|
||||
};
|
||||
|
||||
let proof = prove::<F, C, D>(&all_stark, &config, inputs, &mut TimingTree::default())?;
|
||||
let mut timing = TimingTree::new("prove", log::Level::Debug);
|
||||
let proof = prove::<F, C, D>(&all_stark, &config, inputs, &mut timing)?;
|
||||
timing.print();
|
||||
|
||||
let expected_state_trie_after = {
|
||||
let sender_account_after = AccountRlp {
|
||||
balance: sender_account_before.balance - value, // TODO: Also subtract gas_used * price.
|
||||
// nonce: sender_account_before.nonce + 1, // TODO
|
||||
..sender_account_before
|
||||
};
|
||||
let to_account_after = AccountRlp {
|
||||
balance: value,
|
||||
..AccountRlp::default()
|
||||
};
|
||||
|
||||
let mut children = std::array::from_fn(|_| PartialTrie::Empty.into());
|
||||
children[sender_nibbles.get_nibble(0) as usize] = PartialTrie::Leaf {
|
||||
nibbles: sender_nibbles.truncate_n_nibbles_front(1),
|
||||
value: rlp::encode(&sender_account_after).to_vec(),
|
||||
}
|
||||
.into();
|
||||
children[to_nibbles.get_nibble(0) as usize] = PartialTrie::Leaf {
|
||||
nibbles: to_nibbles.truncate_n_nibbles_front(1),
|
||||
value: rlp::encode(&to_account_after).to_vec(),
|
||||
}
|
||||
.into();
|
||||
PartialTrie::Branch {
|
||||
children,
|
||||
value: vec![],
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
proof.public_values.trie_roots_after.state_root,
|
||||
expected_state_trie_after.calc_hash()
|
||||
);
|
||||
|
||||
verify_proof(all_stark, proof, &config)
|
||||
}
|
||||
|
||||
fn eth_to_wei(eth: U256) -> U256 {
|
||||
// 1 ether = 2^18 wei.
|
||||
eth << 18
|
||||
}
|
||||
|
||||
fn init_logger() {
|
||||
let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "debug"));
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user