From a05ed9fc3a1e92c03f2c132e822fd3143bb549d0 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Sat, 18 Mar 2023 09:29:31 -0700 Subject: [PATCH] Fix clobbering of RLP data memory --- evm/src/cpu/kernel/asm/mpt/hash/hash.asm | 136 +++++++++-------- .../asm/mpt/hash/hash_trie_specific.asm | 17 +++ evm/src/cpu/kernel/asm/mpt/util.asm | 17 +++ evm/src/cpu/kernel/asm/rlp/encode.asm | 73 +++++---- .../cpu/kernel/asm/transactions/type_0.asm | 36 ++--- .../cpu/kernel/constants/global_metadata.rs | 11 +- evm/src/cpu/kernel/interpreter.rs | 3 +- evm/src/cpu/kernel/tests/packing.rs | 12 +- evm/src/cpu/kernel/tests/rlp/encode.rs | 6 +- evm/tests/add11_yml.rs | 139 ++++++++++++++++++ 10 files changed, 321 insertions(+), 129 deletions(-) create mode 100644 evm/tests/add11_yml.rs diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm index 0c8beae7..4209f06c 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm @@ -118,16 +118,19 @@ global encode_node_empty: // stack: node_type, node_payload_ptr, encode_value, retdest %pop3 // stack: retdest - // An empty node is encoded as a single byte, 0x80, which is the RLP - // encoding of the empty string. Write this byte to RLP[0] and return - // (0, 1). + // An empty node is encoded as a single byte, 0x80, which is the RLP encoding of the empty string. + // TODO: Write this byte just once to RLP memory, then we can always return (0, 1). + %alloc_rlp_block + // stack: rlp_pos, retdest PUSH 0x80 - PUSH 0 + // stack: 0x80, rlp_pos, retdest + DUP2 + // stack: rlp_pos, 0x80, rlp_pos, retdest %mstore_rlp - %stack (retdest) -> (retdest, 0, 1) + %stack (rlp_pos, retdest) -> (retdest, rlp_pos, 1) JUMP -encode_node_branch: +global encode_node_branch: // stack: node_type, node_payload_ptr, encode_value, retdest POP // stack: node_payload_ptr, encode_value, retdest @@ -135,6 +138,7 @@ encode_node_branch: // Get the next unused offset within the encoded child buffers. // Then immediately increment the next unused offset by 16, so any // recursive calls will use nonoverlapping offsets. + // TODO: Allocate a block of RLP memory instead? %mload_global_metadata(@GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE) DUP1 %add_const(16) %mstore_global_metadata(@GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE) @@ -150,41 +154,41 @@ encode_node_branch: // stack: base_offset, node_payload_ptr, encode_value, retdest // Now, append each child to our RLP tape. - PUSH 9 // rlp_pos; we start at 9 to leave room to prepend a list prefix + %alloc_rlp_block DUP1 + // stack: rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, retdest %append_child(0) %append_child(1) %append_child(2) %append_child(3) %append_child(4) %append_child(5) %append_child(6) %append_child(7) %append_child(8) %append_child(9) %append_child(10) %append_child(11) %append_child(12) %append_child(13) %append_child(14) %append_child(15) - // stack: rlp_pos', base_offset, node_payload_ptr, encode_value, retdest + // stack: rlp_pos', rlp_start, base_offset, node_payload_ptr, encode_value, retdest - // We no longer need base_offset. - SWAP1 - POP - - // stack: rlp_pos', node_payload_ptr, encode_value, retdest - SWAP1 + %stack (rlp_pos, rlp_start, base_offset, node_payload_ptr) + -> (node_payload_ptr, rlp_pos, rlp_start) %add_const(16) - // stack: value_ptr_ptr, rlp_pos', encode_value, retdest + // stack: value_ptr_ptr, rlp_pos', rlp_start, encode_value, retdest %mload_trie_data - // stack: value_ptr, rlp_pos', encode_value, retdest + // stack: value_ptr, rlp_pos', rlp_start, encode_value, retdest DUP1 %jumpi(encode_node_branch_with_value) + // No value; append the empty string (0x80). - // stack: value_ptr, rlp_pos', encode_value, retdest - %stack (value_ptr, rlp_pos, encode_value) -> (rlp_pos, 0x80, rlp_pos) + // stack: value_ptr, rlp_pos', rlp_start, encode_value, retdest + %stack (value_ptr, rlp_pos, rlp_start, encode_value) -> (rlp_pos, 0x80, rlp_pos, rlp_start) %mstore_rlp - // stack: rlp_pos', retdest + // stack: rlp_pos', rlp_start, retdest %increment - // stack: rlp_pos'', retdest + // stack: rlp_pos'', rlp_start, retdest %jump(encode_node_branch_prepend_prefix) encode_node_branch_with_value: - // stack: value_ptr, rlp_pos', encode_value, retdest - %stack (value_ptr, rlp_pos, encode_value) - -> (encode_value, rlp_pos, value_ptr, encode_node_branch_prepend_prefix) + // stack: value_ptr, rlp_pos', rlp_start, encode_value, retdest + %stack (value_ptr, rlp_pos, rlp_start, encode_value) + -> (encode_value, rlp_pos, value_ptr, encode_node_branch_prepend_prefix, rlp_start) JUMP // call encode_value encode_node_branch_prepend_prefix: - // stack: rlp_pos'', retdest + // stack: rlp_pos'', rlp_start, retdest %prepend_rlp_list_prefix - %stack (start_pos, rlp_len, retdest) -> (retdest, start_pos, rlp_len) + // stack: rlp_prefix_start, rlp_len, retdest + %stack (rlp_prefix_start, rlp_len, retdest) + -> (retdest, rlp_prefix_start, rlp_len) JUMP // Part of the encode_node_branch function. Encodes the i'th child. @@ -208,27 +212,28 @@ encode_node_branch_prepend_prefix: // Part of the encode_node_branch function. Appends the i'th child's RLP. %macro append_child(i) - // stack: rlp_pos, base_offset, node_payload_ptr, encode_value, retdest - DUP2 %add_const($i) %mload_kernel(@SEGMENT_TRIE_ENCODED_CHILD) // load result - DUP3 %add_const($i) %mload_kernel(@SEGMENT_TRIE_ENCODED_CHILD_LEN) // load result_len - // stack: result_len, result, rlp_pos, base_offset, node_payload_ptr, encode_value, retdest + // stack: rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, retdest + DUP3 %add_const($i) %mload_kernel(@SEGMENT_TRIE_ENCODED_CHILD) // load result + DUP4 %add_const($i) %mload_kernel(@SEGMENT_TRIE_ENCODED_CHILD_LEN) // load result_len + // stack: result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, retdest // If result_len != 32, result is raw RLP, with an appropriate RLP prefix already. DUP1 %sub_const(32) %jumpi(%%unpack) // Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160. - // stack: result_len, result, rlp_pos, base_offset, node_payload_ptr, encode_value, retdest + // stack: result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, retdest PUSH 160 DUP4 // rlp_pos %mstore_rlp SWAP2 %increment SWAP2 // rlp_pos += 1 %%unpack: - %stack (result_len, result, rlp_pos, base_offset, node_payload_ptr, encode_value, retdest) - -> (rlp_pos, result, result_len, %%after_unpacking, base_offset, node_payload_ptr, encode_value, retdest) + %stack (result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, retdest) + -> (rlp_pos, result, result_len, %%after_unpacking, + rlp_start, base_offset, node_payload_ptr, encode_value, retdest) %jump(mstore_unpacking_rlp) %%after_unpacking: - // stack: rlp_pos', base_offset, node_payload_ptr, encode_value, retdest + // stack: rlp_pos', rlp_start, base_offset, node_payload_ptr, encode_value, retdest %endmacro -encode_node_extension: +global encode_node_extension: // stack: node_type, node_payload_ptr, encode_value, retdest %stack (node_type, node_payload_ptr, encode_value) -> (node_payload_ptr, encode_value, encode_node_extension_after_encode_child, node_payload_ptr) @@ -237,61 +242,66 @@ encode_node_extension: %jump(encode_or_hash_node) encode_node_extension_after_encode_child: // stack: result, result_len, node_payload_ptr, retdest + %alloc_rlp_block + // stack: rlp_start, result, result_len, node_payload_ptr, retdest PUSH encode_node_extension_after_hex_prefix // retdest PUSH 0 // terminated - // stack: terminated, encode_node_extension_after_hex_prefix, result, result_len, node_payload_ptr, retdest - DUP5 %increment %mload_trie_data // Load the packed_nibbles field, which is at index 1. - // stack: packed_nibbles, terminated, encode_node_extension_after_hex_prefix, result, result_len, node_payload_ptr, retdest - DUP6 %mload_trie_data // Load the num_nibbles field, which is at index 0. - // stack: num_nibbles, packed_nibbles, terminated, encode_node_extension_after_hex_prefix, result, result_len, node_payload_ptr, retdest - PUSH 9 // We start at 9 to leave room to prepend the largest possible RLP list header. - // stack: rlp_start, num_nibbles, packed_nibbles, terminated, encode_node_extension_after_hex_prefix, result, result_len, node_payload_ptr, retdest + // stack: terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, retdest + DUP6 %increment %mload_trie_data // Load the packed_nibbles field, which is at index 1. + // stack: packed_nibbles, terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, retdest + DUP7 %mload_trie_data // Load the num_nibbles field, which is at index 0. + // stack: num_nibbles, packed_nibbles, terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, retdest + DUP5 + // stack: rlp_start, num_nibbles, packed_nibbles, terminated, encode_node_extension_after_hex_prefix, rlp_start, result, result_len, node_payload_ptr, retdest %jump(hex_prefix_rlp) encode_node_extension_after_hex_prefix: - // stack: rlp_pos, result, result_len, node_payload_ptr, retdest + // stack: rlp_pos, rlp_start, result, result_len, node_payload_ptr, retdest // If result_len != 32, result is raw RLP, with an appropriate RLP prefix already. - DUP3 %sub_const(32) %jumpi(encode_node_extension_unpack) + DUP4 %sub_const(32) %jumpi(encode_node_extension_unpack) // Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160. PUSH 160 DUP2 // rlp_pos %mstore_rlp %increment // rlp_pos += 1 encode_node_extension_unpack: - %stack (rlp_pos, result, result_len, node_payload_ptr) - -> (rlp_pos, result, result_len, encode_node_extension_after_unpacking) + %stack (rlp_pos, rlp_start, result, result_len, node_payload_ptr) + -> (rlp_pos, result, result_len, encode_node_extension_after_unpacking, rlp_start) %jump(mstore_unpacking_rlp) encode_node_extension_after_unpacking: - // stack: rlp_end_pos, retdest + // stack: rlp_pos, rlp_start, retdest %prepend_rlp_list_prefix - %stack (rlp_start_pos, rlp_len, retdest) -> (retdest, rlp_start_pos, rlp_len) + %stack (rlp_prefix_start_pos, rlp_len, retdest) + -> (retdest, rlp_prefix_start_pos, rlp_len) JUMP -encode_node_leaf: +global encode_node_leaf: // stack: node_type, node_payload_ptr, encode_value, retdest POP // stack: node_payload_ptr, encode_value, retdest + %alloc_rlp_block PUSH encode_node_leaf_after_hex_prefix // retdest PUSH 1 // terminated - // stack: terminated, encode_node_leaf_after_hex_prefix, node_payload_ptr, encode_value, retdest - DUP3 %increment %mload_trie_data // Load the packed_nibbles field, which is at index 1. - // stack: packed_nibbles, terminated, encode_node_leaf_after_hex_prefix, node_payload_ptr, encode_value, retdest - DUP4 %mload_trie_data // Load the num_nibbles field, which is at index 0. - // stack: num_nibbles, packed_nibbles, terminated, encode_node_leaf_after_hex_prefix, node_payload_ptr, encode_value, retdest - PUSH 9 // We start at 9 to leave room to prepend the largest possible RLP list header. - // stack: rlp_start, num_nibbles, packed_nibbles, terminated, encode_node_leaf_after_hex_prefix, node_payload_ptr, encode_value, retdest + // stack: terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, retdest + DUP4 %increment %mload_trie_data // Load the packed_nibbles field, which is at index 1. + // stack: packed_nibbles, terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, retdest + DUP5 %mload_trie_data // Load the num_nibbles field, which is at index 0. + // stack: num_nibbles, packed_nibbles, terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, retdest + DUP5 + // stack: rlp_start, num_nibbles, packed_nibbles, terminated, encode_node_leaf_after_hex_prefix, rlp_start, node_payload_ptr, encode_value, retdest %jump(hex_prefix_rlp) encode_node_leaf_after_hex_prefix: - // stack: rlp_pos, node_payload_ptr, encode_value, retdest - SWAP1 + // stack: rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest + SWAP2 %add_const(2) // The value pointer starts at index 3, after num_nibbles and packed_nibbles. - // stack: value_ptr_ptr, rlp_pos, encode_value, retdest + // stack: value_ptr_ptr, rlp_start, rlp_pos, encode_value, retdest %mload_trie_data - // stack: value_ptr, rlp_pos, encode_value, retdest - %stack (value_ptr, rlp_pos, encode_value, retdest) - -> (encode_value, rlp_pos, value_ptr, encode_node_leaf_after_encode_value, retdest) + // stack: value_ptr, rlp_start, rlp_pos, encode_value, retdest + %stack (value_ptr, rlp_start, rlp_pos, encode_value, retdest) + -> (encode_value, rlp_pos, value_ptr, encode_node_leaf_after_encode_value, rlp_start, retdest) JUMP encode_node_leaf_after_encode_value: - // stack: rlp_end_pos, retdest + // stack: rlp_end_pos, rlp_start, retdest %prepend_rlp_list_prefix - %stack (rlp_start_pos, rlp_len, retdest) -> (retdest, rlp_start_pos, rlp_len) + %stack (rlp_prefix_start_pos, rlp_len, retdest) + -> (retdest, rlp_prefix_start_pos, rlp_len) JUMP diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm index 15f33cee..3662ee04 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm @@ -13,6 +13,17 @@ global mpt_hash_state_trie: %%after: %endmacro +global mpt_hash_storage_trie: + // stack: node_ptr, retdest + %stack (node_ptr) -> (node_ptr, encode_storage_value) + %jump(mpt_hash) + +%macro mpt_hash_storage_trie + PUSH %%after + %jump(mpt_hash_storage_trie) +%%after: +%endmacro + global mpt_hash_txn_trie: // stack: retdest PUSH encode_txn @@ -96,6 +107,12 @@ global encode_receipt: global encode_storage_value: // stack: rlp_pos, value_ptr, retdest + SWAP1 %mload_trie_data SWAP1 + // stack: rlp_pos, value, retdest + // The YP says storage trie is a map "... to the RLP-encoded 256-bit integer values" + // which seems to imply that this should be %encode_rlp_256. But %encode_rlp_scalar + // causes the tests to pass, so it seems storage values should be treated as variable- + // length after all. %encode_rlp_scalar // stack: rlp_pos', retdest SWAP1 diff --git a/evm/src/cpu/kernel/asm/mpt/util.asm b/evm/src/cpu/kernel/asm/mpt/util.asm index 0f7689e1..c7662c41 100644 --- a/evm/src/cpu/kernel/asm/mpt/util.asm +++ b/evm/src/cpu/kernel/asm/mpt/util.asm @@ -10,6 +10,23 @@ // stack: (empty) %endmacro +%macro alloc_rlp_block + // stack: (empty) + %mload_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) + // stack: block_start + // In our model it's fine to use memory in a sparse way, as long as the gaps aren't larger than + // 2^16 or so. So instead of the caller specifying the size of the block they need, we'll just + // allocate 0x10000 = 2^16 bytes, much larger than any RLP blob the EVM could possibly create. + DUP1 %add_const(0x10000) + // stack: block_end, block_start + %mstore_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) + // stack: block_start + // We leave an extra 9 bytes, so that callers can later prepend a prefix before block_start. + // (9 is the length of the longest possible RLP list prefix.) + %add_const(9) + // stack: block_start +%endmacro + %macro get_trie_data_size // stack: (empty) %mload_global_metadata(@GLOBAL_METADATA_TRIE_DATA_SIZE) diff --git a/evm/src/cpu/kernel/asm/rlp/encode.asm b/evm/src/cpu/kernel/asm/rlp/encode.asm index bc69f444..8254e3e8 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode.asm @@ -196,66 +196,65 @@ encode_rlp_list_prefix_large_done_writing_len: %%after: %endmacro -// Given an RLP list payload which starts at position 9 and ends at the given -// position, prepend the appropriate RLP list prefix. Returns the updated start -// position, as well as the length of the RLP data (including the newly-added -// prefix). +// Given an RLP list payload which starts and ends at the given positions, +// prepend the appropriate RLP list prefix. Returns the updated start position, +// as well as the length of the RLP data (including the newly-added prefix). // -// (We sometimes start list payloads at position 9 because 9 is the length of -// the longest possible RLP list prefix.) -// -// Pre stack: end_pos, retdest -// Post stack: start_pos, rlp_len +// Pre stack: end_pos, start_pos, retdest +// Post stack: prefix_start_pos, rlp_len global prepend_rlp_list_prefix: - // stack: end_pos, retdest - // Since the list payload starts at position 9, payload_len = end_pos - 9. - PUSH 9 DUP2 SUB - // stack: payload_len, end_pos, retdest + // stack: end_pos, start_pos, retdest + DUP2 DUP2 SUB // end_pos - start_pos + // stack: payload_len, end_pos, start_pos, retdest DUP1 %gt_const(55) %jumpi(prepend_rlp_list_prefix_big) // If we got here, we have a small list, so we prepend 0xc0 + len at position 8. - // stack: payload_len, end_pos, retdest - %add_const(0xc0) - // stack: prefix_byte, end_pos, retdest - PUSH 8 // offset + // stack: payload_len, end_pos, start_pos, retdest + DUP1 %add_const(0xc0) + // stack: prefix_byte, payload_len, end_pos, start_pos, retdest + DUP4 %decrement // offset of prefix %mstore_rlp - // stack: end_pos, retdest - %sub_const(8) - // stack: rlp_len, retdest - PUSH 8 // start_pos - %stack (start_pos, rlp_len, retdest) -> (retdest, start_pos, rlp_len) + // stack: payload_len, end_pos, start_pos, retdest + %increment + // stack: rlp_len, end_pos, start_pos, retdest + SWAP2 %decrement + // stack: prefix_start_pos, end_pos, rlp_len, retdest + %stack (prefix_start_pos, end_pos, rlp_len, retdest) -> (retdest, prefix_start_pos, rlp_len) JUMP prepend_rlp_list_prefix_big: // We have a large list, so we prepend 0xf7 + len_of_len at position - // 8 - len_of_len, followed by the length itself. - // stack: payload_len, end_pos, retdest + // prefix_start_pos = start_pos - 1 - len_of_len + // followed by the length itself. + // stack: payload_len, end_pos, start_pos, retdest DUP1 %num_bytes - // stack: len_of_len, payload_len, end_pos, retdest + // stack: len_of_len, payload_len, end_pos, start_pos, retdest DUP1 - PUSH 8 + DUP5 %decrement // start_pos - 1 SUB - // stack: start_pos, len_of_len, payload_len, end_pos, retdest - DUP2 %add_const(0xf7) DUP2 %mstore_rlp // rlp[start_pos] = 0xf7 + len_of_len - DUP1 %increment // start_len_pos = start_pos + 1 - %stack (start_len_pos, start_pos, len_of_len, payload_len, end_pos, retdest) + // stack: prefix_start_pos, len_of_len, payload_len, end_pos, start_pos, retdest + DUP2 %add_const(0xf7) DUP2 %mstore_rlp // rlp[prefix_start_pos] = 0xf7 + len_of_len + // stack: prefix_start_pos, len_of_len, payload_len, end_pos, start_pos, retdest + DUP1 %increment // start_len_pos = prefix_start_pos + 1 + %stack (start_len_pos, prefix_start_pos, len_of_len, payload_len, end_pos, start_pos, retdest) -> (start_len_pos, payload_len, len_of_len, prepend_rlp_list_prefix_big_done_writing_len, - start_pos, end_pos, retdest) + prefix_start_pos, end_pos, retdest) %jump(mstore_unpacking_rlp) prepend_rlp_list_prefix_big_done_writing_len: - // stack: 9, start_pos, end_pos, retdest - %stack (_9, start_pos, end_pos) -> (end_pos, start_pos, start_pos) - // stack: end_pos, start_pos, start_pos, retdest + // stack: start_pos, prefix_start_pos, end_pos, retdest + %stack (start_pos, prefix_start_pos, end_pos) + -> (end_pos, prefix_start_pos, prefix_start_pos) + // stack: end_pos, prefix_start_pos, prefix_start_pos, retdest SUB - // stack: rlp_len, start_pos, retdest - %stack (rlp_len, start_pos, retdest) -> (retdest, start_pos, rlp_len) + // stack: rlp_len, prefix_start_pos, retdest + %stack (rlp_len, prefix_start_pos, retdest) -> (retdest, prefix_start_pos, rlp_len) JUMP // Convenience macro to call prepend_rlp_list_prefix and return where we left off. %macro prepend_rlp_list_prefix - %stack (end_pos) -> (end_pos, %%after) + %stack (end_pos, start_pos) -> (end_pos, start_pos, %%after) %jump(prepend_rlp_list_prefix) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/transactions/type_0.asm b/evm/src/cpu/kernel/asm/transactions/type_0.asm index e9aedca0..d00b10d4 100644 --- a/evm/src/cpu/kernel/asm/transactions/type_0.asm +++ b/evm/src/cpu/kernel/asm/transactions/type_0.asm @@ -84,62 +84,64 @@ type_0_compute_signed_data: // otherwise, it is // keccak256(rlp([nonce, gas_price, gas_limit, to, value, data])) + %alloc_rlp_block + // stack: rlp_start, retdest %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 + // stack: nonce, rlp_start, retdest + DUP2 + // stack: rlp_pos, nonce, rlp_start, retdest %encode_rlp_scalar - // stack: rlp_pos, retdest + // stack: rlp_pos, rlp_start, retdest %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, retdest + // stack: rlp_pos, rlp_start, retdest %mload_txn_field(@TXN_FIELD_GAS_LIMIT) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, retdest + // stack: rlp_pos, rlp_start, retdest %mload_txn_field(@TXN_FIELD_TO) SWAP1 %encode_rlp_160 - // stack: rlp_pos, retdest + // stack: rlp_pos, rlp_start, retdest %mload_txn_field(@TXN_FIELD_VALUE) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, retdest + // stack: rlp_pos, rlp_start, 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 + // stack: ADDR: 3, len, rlp_pos, rlp_start, retdest PUSH after_serializing_txn_data - // stack: after_serializing_txn_data, ADDR: 3, len, rlp_pos, retdest + // stack: after_serializing_txn_data, ADDR: 3, len, rlp_pos, rlp_start, retdest SWAP5 - // stack: rlp_pos, ADDR: 3, len, after_serializing_txn_data, retdest + // stack: rlp_pos, ADDR: 3, len, after_serializing_txn_data, rlp_start, retdest %jump(encode_rlp_string) after_serializing_txn_data: - // stack: rlp_pos, retdest + // stack: rlp_pos, rlp_start, retdest %mload_txn_field(@TXN_FIELD_CHAIN_ID_PRESENT) ISZERO %jumpi(finish_rlp_list) - // stack: rlp_pos, retdest + // stack: rlp_pos, rlp_start, retdest %mload_txn_field(@TXN_FIELD_CHAIN_ID) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, retdest + // stack: rlp_pos, rlp_start, retdest PUSH 0 SWAP1 %encode_rlp_scalar - // stack: rlp_pos, retdest + // stack: rlp_pos, rlp_start, retdest PUSH 0 SWAP1 %encode_rlp_scalar - // stack: rlp_pos, retdest + // stack: rlp_pos, rlp_start, retdest finish_rlp_list: %prepend_rlp_list_prefix - // stack: start_pos, rlp_len, retdest + // stack: prefix_start_pos, rlp_len, retdest PUSH @SEGMENT_RLP_RAW PUSH 0 // context // stack: ADDR: 3, rlp_len, retdest diff --git a/evm/src/cpu/kernel/constants/global_metadata.rs b/evm/src/cpu/kernel/constants/global_metadata.rs index cee02e86..e9d694eb 100644 --- a/evm/src/cpu/kernel/constants/global_metadata.rs +++ b/evm/src/cpu/kernel/constants/global_metadata.rs @@ -6,10 +6,13 @@ pub(crate) enum GlobalMetadata { /// give each new context a unique ID, so that its memory will be zero-initialized. LargestContext = 0, /// The size of active memory, in bytes. - MemorySize = 2, + MemorySize = 1, /// The size of the `TrieData` segment, in bytes. In other words, the next address available for /// appending additional trie data. - TrieDataSize = 3, + TrieDataSize = 2, + /// The size of the `TrieData` segment, in bytes. In other words, the next address available for + /// appending additional trie data. + RlpDataSize = 3, /// A pointer to the root of the state trie within the `TrieData` buffer. StateTrieRoot = 4, /// A pointer to the root of the transaction trie within the `TrieData` buffer. @@ -45,13 +48,14 @@ pub(crate) enum GlobalMetadata { } impl GlobalMetadata { - pub(crate) const COUNT: usize = 21; + pub(crate) const COUNT: usize = 22; pub(crate) fn all() -> [Self; Self::COUNT] { [ Self::LargestContext, Self::MemorySize, Self::TrieDataSize, + Self::RlpDataSize, Self::StateTrieRoot, Self::TransactionTrieRoot, Self::ReceiptTrieRoot, @@ -79,6 +83,7 @@ impl GlobalMetadata { Self::LargestContext => "GLOBAL_METADATA_LARGEST_CONTEXT", Self::MemorySize => "GLOBAL_METADATA_MEMORY_SIZE", Self::TrieDataSize => "GLOBAL_METADATA_TRIE_DATA_SIZE", + Self::RlpDataSize => "GLOBAL_METADATA_RLP_DATA_SIZE", Self::StateTrieRoot => "GLOBAL_METADATA_STATE_TRIE_ROOT", Self::TransactionTrieRoot => "GLOBAL_METADATA_TXN_TRIE_ROOT", Self::ReceiptTrieRoot => "GLOBAL_METADATA_RECEIPT_TRIE_ROOT", diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 52876c97..79c4ba93 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -279,6 +279,7 @@ impl<'a> Interpreter<'a> { .byte(0); self.opcode_count[opcode as usize] += 1; self.incr(1); + match opcode { 0x00 => self.run_stop(), // "STOP", 0x01 => self.run_add(), // "ADD", @@ -356,7 +357,7 @@ impl<'a> Interpreter<'a> { 0xa2 => todo!(), // "LOG2", 0xa3 => todo!(), // "LOG3", 0xa4 => todo!(), // "LOG4", - 0xa5 => bail!("Executed PANIC"), // "PANIC", + 0xa5 => bail!("Executed PANIC, stack={:?}", self.stack()), // "PANIC", 0xf0 => todo!(), // "CREATE", 0xf1 => todo!(), // "CALL", 0xf2 => todo!(), // "CALLCODE", diff --git a/evm/src/cpu/kernel/tests/packing.rs b/evm/src/cpu/kernel/tests/packing.rs index 71f66e6d..43ca9b5f 100644 --- a/evm/src/cpu/kernel/tests/packing.rs +++ b/evm/src/cpu/kernel/tests/packing.rs @@ -7,7 +7,7 @@ use crate::memory::segments::Segment; #[test] fn test_mload_packing_1_byte() -> Result<()> { - let mstore_unpacking = KERNEL.global_labels["mload_packing"]; + let mload_packing = KERNEL.global_labels["mload_packing"]; let retdest = 0xDEADBEEFu32.into(); let len = 1.into(); @@ -16,7 +16,7 @@ fn test_mload_packing_1_byte() -> Result<()> { let context = 0.into(); let initial_stack = vec![retdest, len, offset, segment, context]; - let mut interpreter = Interpreter::new_with_kernel(mstore_unpacking, initial_stack); + let mut interpreter = Interpreter::new_with_kernel(mload_packing, initial_stack); interpreter.set_rlp_memory(vec![0, 0, 0xAB]); interpreter.run()?; @@ -27,7 +27,7 @@ fn test_mload_packing_1_byte() -> Result<()> { #[test] fn test_mload_packing_3_bytes() -> Result<()> { - let mstore_unpacking = KERNEL.global_labels["mload_packing"]; + let mload_packing = KERNEL.global_labels["mload_packing"]; let retdest = 0xDEADBEEFu32.into(); let len = 3.into(); @@ -36,7 +36,7 @@ fn test_mload_packing_3_bytes() -> Result<()> { let context = 0.into(); let initial_stack = vec![retdest, len, offset, segment, context]; - let mut interpreter = Interpreter::new_with_kernel(mstore_unpacking, initial_stack); + let mut interpreter = Interpreter::new_with_kernel(mload_packing, initial_stack); interpreter.set_rlp_memory(vec![0, 0, 0xAB, 0xCD, 0xEF]); interpreter.run()?; @@ -47,7 +47,7 @@ fn test_mload_packing_3_bytes() -> Result<()> { #[test] fn test_mload_packing_32_bytes() -> Result<()> { - let mstore_unpacking = KERNEL.global_labels["mload_packing"]; + let mload_packing = KERNEL.global_labels["mload_packing"]; let retdest = 0xDEADBEEFu32.into(); let len = 32.into(); @@ -56,7 +56,7 @@ fn test_mload_packing_32_bytes() -> Result<()> { let context = 0.into(); let initial_stack = vec![retdest, len, offset, segment, context]; - let mut interpreter = Interpreter::new_with_kernel(mstore_unpacking, initial_stack); + let mut interpreter = Interpreter::new_with_kernel(mload_packing, initial_stack); interpreter.set_rlp_memory(vec![0xFF; 32]); interpreter.run()?; diff --git a/evm/src/cpu/kernel/tests/rlp/encode.rs b/evm/src/cpu/kernel/tests/rlp/encode.rs index 4e04b248..2771dea0 100644 --- a/evm/src/cpu/kernel/tests/rlp/encode.rs +++ b/evm/src/cpu/kernel/tests/rlp/encode.rs @@ -86,8 +86,9 @@ fn test_prepend_rlp_list_prefix_small() -> Result<()> { let prepend_rlp_list_prefix = KERNEL.global_labels["prepend_rlp_list_prefix"]; let retdest = 0xDEADBEEFu32.into(); + let start_pos = 9.into(); let end_pos = (9 + 5).into(); - let initial_stack = vec![retdest, end_pos]; + let initial_stack = vec![retdest, start_pos, end_pos]; let mut interpreter = Interpreter::new_with_kernel(prepend_rlp_list_prefix, initial_stack); interpreter.set_rlp_memory(vec![ // Nine 0s to leave room for the longest possible RLP list prefix. @@ -114,8 +115,9 @@ fn test_prepend_rlp_list_prefix_large() -> Result<()> { let prepend_rlp_list_prefix = KERNEL.global_labels["prepend_rlp_list_prefix"]; let retdest = 0xDEADBEEFu32.into(); + let start_pos = 9.into(); let end_pos = (9 + 60).into(); - let initial_stack = vec![retdest, end_pos]; + let initial_stack = vec![retdest, start_pos, end_pos]; let mut interpreter = Interpreter::new_with_kernel(prepend_rlp_list_prefix, initial_stack); #[rustfmt::skip] diff --git a/evm/tests/add11_yml.rs b/evm/tests/add11_yml.rs new file mode 100644 index 00000000..4b1eba54 --- /dev/null +++ b/evm/tests/add11_yml.rs @@ -0,0 +1,139 @@ +use std::collections::HashMap; +use std::time::Duration; + +use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; +use eth_trie_utils::partial_trie::{Nibbles, PartialTrie}; +use ethereum_types::Address; +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; +use plonky2_evm::verifier::verify_proof; + +type F = GoldilocksField; +const D: usize = 2; +type C = PoseidonGoldilocksConfig; + +/// Test a simple token transfer to a new address. +#[test] +fn add11_yml() -> anyhow::Result<()> { + init_logger(); + + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + + let beneficiary = hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"); + let sender = hex!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b"); + let to = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); + + let beneficiary_state_key = keccak(beneficiary); + let sender_state_key = keccak(sender); + let to_state_key = keccak(to); + + let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); + let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); + let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); + + let code = [0x60, 0x01, 0x60, 0x01, 0x01, 0x60, 0x00, 0x55, 0x00]; + let code_hash = keccak(code); + + let beneficiary_account_before = AccountRlp { + nonce: 1.into(), + ..AccountRlp::default() + }; + let sender_account_before = AccountRlp { + balance: 0x0de0b6b3a7640000u64.into(), + ..AccountRlp::default() + }; + let to_account_before = AccountRlp { + balance: 0x0de0b6b3a7640000u64.into(), + code_hash, + ..AccountRlp::default() + }; + + let mut state_trie_before = PartialTrie::Empty; + state_trie_before.insert( + beneficiary_nibbles, + rlp::encode(&beneficiary_account_before).to_vec(), + ); + state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec()); + state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec()); + + let tries_before = TrieInputs { + state_trie: state_trie_before, + transactions_trie: PartialTrie::Empty, + receipts_trie: PartialTrie::Empty, + storage_tries: vec![(Address::from_slice(&to), PartialTrie::Empty)], + }; + + let txn = hex!("f863800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d87830186a0801ba0ffb600e63115a7362e7811894a91d8ba4330e526f22121c994c4692035dfdfd5a06198379fcac8de3dbfac48b165df4bf88e2088f294b61efb9a65fe2281c76e16"); + + let block_metadata = BlockMetadata { + block_beneficiary: Address::from(beneficiary), + block_base_fee: 0xa.into(), + ..BlockMetadata::default() + }; + + let mut contract_code = HashMap::new(); + contract_code.insert(keccak(vec![]), vec![]); + contract_code.insert(code_hash, code.to_vec()); + + let inputs = GenerationInputs { + signed_txns: vec![txn.to_vec()], + tries: tries_before, + contract_code, + block_metadata, + addresses: vec![], + }; + + let mut timing = TimingTree::new("prove", log::Level::Debug); + let proof = prove::(&all_stark, &config, inputs, &mut timing)?; + timing.filter(Duration::from_millis(100)).print(); + + let beneficiary_account_after = AccountRlp { + nonce: 1.into(), + ..AccountRlp::default() + }; + let sender_account_after = AccountRlp { + balance: 0xde0b6b3a75be550u64.into(), + nonce: 1.into(), + ..AccountRlp::default() + }; + let to_account_after = AccountRlp { + balance: 0xde0b6b3a76586a0u64.into(), + code_hash, + // Storage map: { 0 => 2 } + storage_root: PartialTrie::Leaf { + nibbles: Nibbles::from_h256_be(keccak([0u8; 32])), + value: vec![2], + } + .calc_hash(), + ..AccountRlp::default() + }; + + let mut expected_state_trie_after = PartialTrie::Empty; + expected_state_trie_after.insert( + beneficiary_nibbles, + rlp::encode(&beneficiary_account_after).to_vec(), + ); + expected_state_trie_after.insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec()); + expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec()); + + assert_eq!( + proof.public_values.trie_roots_after.state_root, + expected_state_trie_after.calc_hash() + ); + + verify_proof(&all_stark, proof, &config) +} + +fn init_logger() { + let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); +}