diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index f0c85638..7f5f4b3e 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -142,14 +142,14 @@ impl, const D: usize> Stark for CpuStark, const D: usize> Stark for CpuStark 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"), diff --git a/evm/src/cpu/kernel/asm/balance.asm b/evm/src/cpu/kernel/asm/balance.asm index fec26db9..6ce88cc4 100644 --- a/evm/src/cpu/kernel/asm/balance.asm +++ b/evm/src/cpu/kernel/asm/balance.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 diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index 1b8a535f..761ffc7d 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -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 diff --git a/evm/src/cpu/kernel/asm/core/nonce.asm b/evm/src/cpu/kernel/asm/core/nonce.asm index 6e57eb40..204620fb 100644 --- a/evm/src/cpu/kernel/asm/core/nonce.asm +++ b/evm/src/cpu/kernel/asm/core/nonce.asm @@ -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. diff --git a/evm/src/cpu/kernel/asm/core/process_txn.asm b/evm/src/cpu/kernel/asm/core/process_txn.asm index ac52c53d..a44fff27 100644 --- a/evm/src/cpu/kernel/asm/core/process_txn.asm +++ b/evm/src/cpu/kernel/asm/core/process_txn.asm @@ -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 diff --git a/evm/src/cpu/kernel/asm/core/syscall_stubs.asm b/evm/src/cpu/kernel/asm/core/syscall_stubs.asm index d39d8145..0f029143 100644 --- a/evm/src/cpu/kernel/asm/core/syscall_stubs.asm +++ b/evm/src/cpu/kernel/asm/core/syscall_stubs.asm @@ -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: diff --git a/evm/src/cpu/kernel/asm/core/transfer.asm b/evm/src/cpu/kernel/asm/core/transfer.asm index b12bc9de..930c29f1 100644 --- a/evm/src/cpu/kernel/asm/core/transfer.asm +++ b/evm/src/cpu/kernel/asm/core/transfer.asm @@ -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 diff --git a/evm/src/cpu/kernel/asm/main.asm b/evm/src/cpu/kernel/asm/main.asm index 3541d21b..c6e818b2 100644 --- a/evm/src/cpu/kernel/asm/main.asm +++ b/evm/src/cpu/kernel/asm/main.asm @@ -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) diff --git a/evm/src/cpu/kernel/asm/memory/syscalls.asm b/evm/src/cpu/kernel/asm/memory/syscalls.asm new file mode 100644 index 00000000..3b56a7fd --- /dev/null +++ b/evm/src/cpu/kernel/asm/memory/syscalls.asm @@ -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 diff --git a/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm index 61630753..225f3cc0 100644 --- a/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm +++ b/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm @@ -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) diff --git a/evm/src/cpu/kernel/asm/rlp/encode.asm b/evm/src/cpu/kernel/asm/rlp/encode.asm index dada98b0..bc69f444 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode.asm @@ -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) diff --git a/evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm b/evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm new file mode 100644 index 00000000..a19507d8 --- /dev/null +++ b/evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm @@ -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) diff --git a/evm/src/cpu/kernel/asm/transactions/type_0.asm b/evm/src/cpu/kernel/asm/transactions/type_0.asm index 7bc7a399..d1f00ed9 100644 --- a/evm/src/cpu/kernel/asm/transactions/type_0.asm +++ b/evm/src/cpu/kernel/asm/transactions/type_0.asm @@ -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) diff --git a/evm/src/cpu/kernel/asm/util/keccak.asm b/evm/src/cpu/kernel/asm/util/keccak.asm index 92ba8d38..7922e8ce 100644 --- a/evm/src/cpu/kernel/asm/util/keccak.asm +++ b/evm/src/cpu/kernel/asm/util/keccak.asm @@ -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 diff --git a/evm/src/cpu/kernel/assembler.rs b/evm/src/cpu/kernel/assembler.rs index a0ca7ef2..5d051b0d 100644 --- a/evm/src/cpu/kernel/assembler.rs +++ b/evm/src/cpu/kernel/assembler.rs @@ -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, + pub(crate) ordered_labels: Vec, /// Map from `PROVER_INPUT` offsets to their corresponding `ProverInputFn`. pub(crate) prover_inputs: HashMap, @@ -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 { diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index b10eced1..e762a643 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -49,7 +49,12 @@ pub fn evm_constants() -> HashMap { 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", diff --git a/evm/src/cpu/kernel/constants/txn_fields.rs b/evm/src/cpu/kernel/constants/txn_fields.rs index 141eee39..5b175e3b 100644 --- a/evm/src/cpu/kernel/constants/txn_fields.rs +++ b/evm/src/cpu/kernel/constants/txn_fields.rs @@ -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", } } } diff --git a/evm/src/cpu/syscalls.rs b/evm/src/cpu/syscalls.rs index 4033620e..d8020a7a 100644 --- a/evm/src/cpu/syscalls.rs +++ b/evm/src/cpu/syscalls.rs @@ -72,8 +72,8 @@ pub fn eval_packed( // 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, 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); } diff --git a/evm/src/generation/mpt.rs b/evm/src/generation/mpt.rs index 15b92f45..c26f5229 100644 --- a/evm/src/generation/mpt.rs +++ b/evm/src/generation/mpt.rs @@ -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 { let mut inputs = all_mpt_prover_inputs(trie_inputs); inputs.reverse(); diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs index 6d65f16c..8c04c071 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -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( 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( 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( + state: &mut GenerationState, + row: CpuColumnsView, +) -> Result<(), ProgramError> { + state.traces.push_cpu(row); + Ok(()) +} + +pub(crate) fn generate_get_context( + state: &mut GenerationState, + mut row: CpuColumnsView, +) -> 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( + state: &mut GenerationState, + mut row: CpuColumnsView, +) -> 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( n: u8, state: &mut GenerationState, @@ -386,7 +426,9 @@ pub(crate) fn generate_syscall( mut row: CpuColumnsView, ) -> 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( 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( mut row: CpuColumnsView, ) -> 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); diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index 39aac810..69be1f83 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -113,6 +113,10 @@ fn decode(registers: RegistersState, opcode: u8) -> Result 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 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( 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( _ => 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(state: &mut GenerationState) -> 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(state: &mut GenerationState, 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(_state: &mut GenerationState) { @@ -270,7 +291,8 @@ pub(crate) fn transition(state: &mut GenerationState) { } 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) diff --git a/evm/tests/transfer_to_new_addr.rs b/evm/tests/transfer_to_new_addr.rs index 1c74366e..67e9aefc 100644 --- a/evm/tests/transfer_to_new_addr.rs +++ b/evm/tests/transfer_to_new_addr.rs @@ -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::::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::(&all_stark, &config, inputs, &mut TimingTree::default())?; + let mut timing = TimingTree::new("prove", log::Level::Debug); + let proof = prove::(&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")); +}