From 6bb1ad94e8acc9fb144cf8562f4c9b4d0bb74b48 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Sat, 8 Oct 2022 15:09:07 -0700 Subject: [PATCH] MPT insert logic, part 1 --- evm/src/cpu/kernel/aggregator.rs | 3 +- .../cpu/kernel/asm/mpt/hash_trie_specific.asm | 2 +- evm/src/cpu/kernel/asm/mpt/insert.asm | 118 +++++++++++ .../kernel/asm/mpt/insert_trie_specific.asm | 14 ++ evm/src/cpu/kernel/asm/mpt/load.asm | 159 +++++++------- evm/src/cpu/kernel/asm/mpt/write.asm | 47 ----- evm/src/cpu/kernel/interpreter.rs | 11 +- evm/src/cpu/kernel/tests/mpt/insert.rs | 173 ++++++++++++++++ evm/src/cpu/kernel/tests/mpt/load.rs | 194 ++++++++++++++++-- evm/src/cpu/kernel/tests/mpt/mod.rs | 30 +++ evm/src/generation/mpt.rs | 18 +- 11 files changed, 620 insertions(+), 149 deletions(-) create mode 100644 evm/src/cpu/kernel/asm/mpt/insert.asm create mode 100644 evm/src/cpu/kernel/asm/mpt/insert_trie_specific.asm delete mode 100644 evm/src/cpu/kernel/asm/mpt/write.asm create mode 100644 evm/src/cpu/kernel/tests/mpt/insert.rs diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index 2dcbd41c..6fb2231e 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -43,12 +43,13 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/mpt/hash.asm"), include_str!("asm/mpt/hash_trie_specific.asm"), include_str!("asm/mpt/hex_prefix.asm"), + include_str!("asm/mpt/insert.asm"), + include_str!("asm/mpt/insert_trie_specific.asm"), include_str!("asm/mpt/load.asm"), include_str!("asm/mpt/read.asm"), include_str!("asm/mpt/storage_read.asm"), include_str!("asm/mpt/storage_write.asm"), include_str!("asm/mpt/util.asm"), - include_str!("asm/mpt/write.asm"), include_str!("asm/ripemd/box.asm"), include_str!("asm/ripemd/compression.asm"), include_str!("asm/ripemd/constants.asm"), diff --git a/evm/src/cpu/kernel/asm/mpt/hash_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/hash_trie_specific.asm index 221c0f20..bf2c46f0 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash_trie_specific.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash_trie_specific.asm @@ -39,7 +39,7 @@ global mpt_hash_receipt_trie: %%after: %endmacro -encode_account: +global encode_account: // stack: rlp_pos, value_ptr, retdest // First, we compute the length of the RLP data we're about to write. // The nonce and balance fields are variable-length, so we need to load them diff --git a/evm/src/cpu/kernel/asm/mpt/insert.asm b/evm/src/cpu/kernel/asm/mpt/insert.asm new file mode 100644 index 00000000..9a5ed7f2 --- /dev/null +++ b/evm/src/cpu/kernel/asm/mpt/insert.asm @@ -0,0 +1,118 @@ +// Return a copy of the given node, with the given key set to the given value. +// +// Pre stack: node_ptr, num_nibbles, key, value_ptr, retdest +// Post stack: updated_node_ptr +global mpt_insert: + // stack: node_ptr, num_nibbles, key, value_ptr, retdest + DUP1 %mload_trie_data + // stack: node_type, node_ptr, num_nibbles, key, value_ptr, retdest + // Increment node_ptr, so it points to the node payload instead of its type. + SWAP1 %increment SWAP1 + // stack: node_type, node_payload_ptr, num_nibbles, key, value_ptr, retdest + + DUP1 %eq_const(@MPT_NODE_EMPTY) %jumpi(mpt_insert_empty) + DUP1 %eq_const(@MPT_NODE_BRANCH) %jumpi(mpt_insert_branch) + DUP1 %eq_const(@MPT_NODE_EXTENSION) %jumpi(mpt_insert_extension) + DUP1 %eq_const(@MPT_NODE_LEAF) %jumpi(mpt_insert_leaf) + + // There's still the MPT_NODE_HASH case, but if we hit a hash node, + // it means the prover failed to provide necessary Merkle data, so panic. + PANIC + +mpt_insert_empty: + // stack: node_type, node_payload_ptr, num_nibbles, key, value_ptr, retdest + %pop2 + // stack: num_nibbles, key, value_ptr, retdest + // We will append a new leaf node to our MPT tape and return a pointer to it. + %get_trie_data_size + // stack: leaf_ptr, num_nibbles, key, value_ptr, retdest + PUSH @MPT_NODE_LEAF %append_to_trie_data + // stack: leaf_ptr, num_nibbles, key, value_ptr, retdest + SWAP1 %append_to_trie_data + // stack: leaf_ptr, key, value_ptr, retdest + SWAP1 %append_to_trie_data + // stack: leaf_ptr, value_ptr, retdest + SWAP1 %append_to_trie_data + // stack: leaf_ptr, retdest + SWAP1 + JUMP + +mpt_insert_branch: + // stack: node_type, node_payload_ptr, num_nibbles, key, value_ptr, retdest + %get_trie_data_size + // stack: updated_branch_ptr, node_type, node_payload_ptr, num_nibbles, key, value_ptr, retdest + SWAP1 + %append_to_trie_data + // stack: updated_branch_ptr, node_payload_ptr, num_nibbles, key, value_ptr, retdest + SWAP1 + // stack: node_payload_ptr, updated_branch_ptr, num_nibbles, key, value_ptr, retdest + + // Copy the original node's data to our updated node. + DUP1 %mload_trie_data %append_to_trie_data // Copy child[0] + DUP1 %add_const(1) %mload_trie_data %append_to_trie_data // ... + DUP1 %add_const(2) %mload_trie_data %append_to_trie_data + DUP1 %add_const(3) %mload_trie_data %append_to_trie_data + DUP1 %add_const(4) %mload_trie_data %append_to_trie_data + DUP1 %add_const(5) %mload_trie_data %append_to_trie_data + DUP1 %add_const(6) %mload_trie_data %append_to_trie_data + DUP1 %add_const(7) %mload_trie_data %append_to_trie_data + DUP1 %add_const(8) %mload_trie_data %append_to_trie_data + DUP1 %add_const(9) %mload_trie_data %append_to_trie_data + DUP1 %add_const(10) %mload_trie_data %append_to_trie_data + DUP1 %add_const(11) %mload_trie_data %append_to_trie_data + DUP1 %add_const(12) %mload_trie_data %append_to_trie_data + DUP1 %add_const(13) %mload_trie_data %append_to_trie_data + DUP1 %add_const(14) %mload_trie_data %append_to_trie_data + DUP1 %add_const(15) %mload_trie_data %append_to_trie_data // Copy child[15] + %add_const(16) %mload_trie_data %append_to_trie_data // Copy value_ptr + + // At this point, we branch based on whether the key terminates with this branch node. + // stack: updated_branch_ptr, num_nibbles, key, value_ptr, retdest + DUP2 %jumpi(mpt_insert_branch_nonterminal) + + // The key terminates here, so the value will be placed right in our (updated) branch node. + // stack: updated_branch_ptr, num_nibbles, key, value_ptr, retdest + SWAP3 + // stack: value_ptr, num_nibbles, key, updated_branch_ptr, retdest + DUP4 %add_const(17) + // stack: updated_branch_value_ptr_ptr, value_ptr, num_nibbles, key, updated_branch_ptr, retdest + %mstore_trie_data + // stack: num_nibbles, key, updated_branch_ptr, retdest + %pop2 + // stack: updated_branch_ptr, retdest + SWAP1 + JUMP + +mpt_insert_branch_nonterminal: + // The key continues, so we split off the first (most significant) nibble, + // and recursively insert into the child associated with that nibble. + // stack: updated_branch_ptr, num_nibbles, key, value_ptr, retdest + %stack (updated_branch_ptr, num_nibbles, key) -> (num_nibbles, key, updated_branch_ptr) + %split_first_nibble + // stack: first_nibble, num_nibbles, key, updated_branch_ptr, value_ptr, retdest + DUP4 %increment ADD + // stack: child_ptr_ptr, num_nibbles, key, updated_branch_ptr, value_ptr, retdest + %stack (child_ptr_ptr, num_nibbles, key, updated_branch_ptr, value_ptr) + -> (child_ptr_ptr, num_nibbles, key, value_ptr, + mpt_insert_branch_nonterminal_after_recursion, + child_ptr_ptr, updated_branch_ptr) + %mload_trie_data // Deref child_ptr_ptr, giving child_ptr + %jump(mpt_insert) +mpt_insert_branch_nonterminal_after_recursion: + // stack: updated_child_ptr, child_ptr_ptr, updated_branch_ptr, retdest + SWAP1 %mstore_trie_data // Store the pointer to the updated child. + // stack: updated_branch_ptr, retdest + SWAP1 + JUMP + +mpt_insert_extension: + // stack: node_type, node_payload_ptr, num_nibbles, key, value_ptr, retdest + POP + // stack: node_payload_ptr, num_nibbles, key, value_ptr, retdest + PANIC // TODO + +mpt_insert_leaf: + // stack: node_type, node_payload_ptr, num_nibbles, key, value_ptr, retdest + POP + // stack: node_payload_ptr, num_nibbles, key, value_ptr, retdest + PANIC // TODO diff --git a/evm/src/cpu/kernel/asm/mpt/insert_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/insert_trie_specific.asm new file mode 100644 index 00000000..4c03d96c --- /dev/null +++ b/evm/src/cpu/kernel/asm/mpt/insert_trie_specific.asm @@ -0,0 +1,14 @@ +// Insertion logic specific to a particular trie. + +// Mutate the state trie, inserting the given key-value pair. +global mpt_insert_state_trie: + // stack: num_nibbles, key, value_ptr, retdest + %stack (num_nibbles, key, value_ptr) + -> (num_nibbles, key, value_ptr, mpt_insert_state_trie_save) + %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) + // stack: state_root_ptr, num_nibbles, key, value_ptr, mpt_insert_state_trie_save, retdest + %jump(mpt_insert) +mpt_insert_state_trie_save: + // stack: updated_node_ptr, retdest + %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) + JUMP diff --git a/evm/src/cpu/kernel/asm/mpt/load.asm b/evm/src/cpu/kernel/asm/mpt/load.asm index 62909f2d..73f58b95 100644 --- a/evm/src/cpu/kernel/asm/mpt/load.asm +++ b/evm/src/cpu/kernel/asm/mpt/load.asm @@ -9,9 +9,9 @@ global load_all_mpts: PUSH 1 %set_trie_data_size - %load_mpt_and_return_root_ptr %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) - %load_mpt_and_return_root_ptr %mstore_global_metadata(@GLOBAL_METADATA_TXN_TRIE_ROOT) - %load_mpt_and_return_root_ptr %mstore_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_ROOT) + %load_mpt %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) + %load_mpt %mstore_global_metadata(@GLOBAL_METADATA_TXN_TRIE_ROOT) + %load_mpt %mstore_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_ROOT) PROVER_INPUT(mpt) // stack: num_storage_tries, retdest @@ -30,7 +30,7 @@ storage_trie_loop: // stack: i, storage_trie_addr, i, num_storage_tries, retdest %mstore_kernel(@SEGMENT_STORAGE_TRIE_ADDRS) // stack: i, num_storage_tries, retdest - %load_mpt_and_return_root_ptr + %load_mpt // stack: root_ptr, i, num_storage_tries, retdest DUP2 // stack: i, root_ptr, i, num_storage_tries, retdest @@ -45,13 +45,11 @@ storage_trie_loop_end: // Load an MPT from prover inputs. // Pre stack: retdest -// Post stack: (empty) +// Post stack: node_ptr load_mpt: // stack: retdest PROVER_INPUT(mpt) // stack: node_type, retdest - DUP1 %append_to_trie_data - // stack: node_type, retdest DUP1 %eq_const(@MPT_NODE_EMPTY) %jumpi(load_mpt_empty) DUP1 %eq_const(@MPT_NODE_BRANCH) %jumpi(load_mpt_branch) @@ -61,94 +59,108 @@ load_mpt: PANIC // Invalid node type load_mpt_empty: - // stack: node_type, retdest - POP - // stack: retdest + // TRIE_DATA[0] = 0, and an empty node has type 0, so we can simply return the null pointer. + %stack (node_type, retdest) -> (retdest, 0) JUMP load_mpt_branch: // stack: node_type, retdest - POP - // stack: retdest + %get_trie_data_size + // stack: node_ptr, node_type, retdest + SWAP1 %append_to_trie_data + // stack: node_ptr, retdest // Save the offset of our 16 child pointers so we can write them later. // Then advance out current trie pointer beyond them, so we can load the // value and have it placed after our child pointers. %get_trie_data_size - // stack: ptr_children, retdest - DUP1 %add_const(16) - // stack: ptr_value, ptr_children, retdest + // stack: children_ptr, node_ptr, retdest + DUP1 %add_const(17) // Skip over 16 children plus the value pointer + // stack: value_ptr, children_ptr, node_ptr, retdest %set_trie_data_size - // stack: ptr_children, retdest - // We need to append a pointer to where the value will live. - // %load_leaf_value will append the value just after this pointer; - // we add 1 to account for the pointer itself. - %get_trie_data_size %increment %append_to_trie_data - // stack: ptr_children, retdest - %load_leaf_value + // stack: children_ptr, node_ptr, retdest + %load_value + // stack: children_ptr, value_ptr, node_ptr, retdest + SWAP1 // Load the 16 children. %rep 16 - %load_mpt_and_return_root_ptr - // stack: child_ptr, ptr_next_child, retdest + %load_mpt + // stack: child_ptr, next_child_ptr_ptr, value_ptr, node_ptr, retdest DUP2 - // stack: ptr_next_child, child_ptr, ptr_next_child, retdest + // stack: next_child_ptr_ptr, child_ptr, next_child_ptr_ptr, value_ptr, node_ptr, retdest %mstore_trie_data - // stack: ptr_next_child, retdest + // stack: next_child_ptr_ptr, value_ptr, node_ptr, retdest %increment - // stack: ptr_next_child, retdest + // stack: next_child_ptr_ptr, value_ptr, node_ptr, retdest %endrep - // stack: ptr_next_child, retdest - POP + // stack: value_ptr_ptr, value_ptr, node_ptr, retdest + %mstore_trie_data + // stack: node_ptr, retdest + SWAP1 JUMP load_mpt_extension: // stack: node_type, retdest - POP - // stack: retdest + %get_trie_data_size + // stack: node_ptr, node_type, retdest + SWAP1 %append_to_trie_data + // stack: node_ptr, retdest PROVER_INPUT(mpt) // read num_nibbles %append_to_trie_data PROVER_INPUT(mpt) // read packed_nibbles %append_to_trie_data - // stack: retdest + // stack: node_ptr, retdest - // Let i be the current trie data size. We still need to expand this node by - // one element, appending our child pointer. Thus our child node will start - // at i + 1. So we will set our child pointer to i + 1. %get_trie_data_size - %increment - %append_to_trie_data - // stack: retdest + // stack: child_ptr_ptr, node_ptr, retdest + // Increment trie_data_size, to leave room for child_ptr_ptr, before we load our child. + DUP1 %increment %set_trie_data_size + // stack: child_ptr_ptr, node_ptr, retdest %load_mpt - // stack: retdest + // stack: child_ptr, child_ptr_ptr, node_ptr, retdest + SWAP1 + %mstore_trie_data + // stack: node_ptr, retdest + SWAP1 JUMP load_mpt_leaf: // stack: node_type, retdest - POP - // stack: retdest + %get_trie_data_size + // stack: node_ptr, node_type, retdest + SWAP1 %append_to_trie_data + // stack: node_ptr, retdest PROVER_INPUT(mpt) // read num_nibbles %append_to_trie_data PROVER_INPUT(mpt) // read packed_nibbles %append_to_trie_data - // stack: retdest - // We need to append a pointer to where the value will live. - // %load_leaf_value will append the value just after this pointer; - // we add 1 to account for the pointer itself. - %get_trie_data_size %increment %append_to_trie_data - // stack: retdest - %load_leaf_value - // stack: retdest + // stack: node_ptr, retdest + // We save value_ptr_ptr = get_trie_data_size, then increment trie_data_size + // to skip over the slot for value_ptr. We will write value_ptr after the + // load_value call. + %get_trie_data_size + // stack: value_ptr_ptr, node_ptr, retdest + DUP1 %increment %set_trie_data_size + // stack: value_ptr_ptr, node_ptr, retdest + %load_value + // stack: value_ptr, value_ptr_ptr, node_ptr, retdest + SWAP1 %mstore_trie_data + // stack: node_ptr, retdest + SWAP1 JUMP load_mpt_digest: // stack: node_type, retdest - POP - // stack: retdest + %get_trie_data_size + // stack: node_ptr, node_type, retdest + SWAP1 %append_to_trie_data + // stack: node_ptr, retdest PROVER_INPUT(mpt) // read digest %append_to_trie_data - // stack: retdest + // stack: node_ptr, retdest + SWAP1 JUMP // Convenience macro to call load_mpt and return where we left off. @@ -158,34 +170,37 @@ load_mpt_digest: %%after: %endmacro -%macro load_mpt_and_return_root_ptr - // stack: (empty) - %get_trie_data_size - // stack: ptr - %load_mpt - // stack: ptr -%endmacro - -// Load a leaf from prover input, and append it to trie data. -%macro load_leaf_value +// Load a leaf from prover input, append it to trie data, and return a pointer to it. +%macro load_value // stack: (empty) PROVER_INPUT(mpt) - // stack: leaf_len + // stack: value_len + DUP1 ISZERO + %jumpi(%%return_null) + // stack: value_len + %get_trie_data_size + SWAP1 + // stack: value_len, value_ptr DUP1 %append_to_trie_data - // stack: leaf_len + // stack: value_len, value_ptr %%loop: DUP1 ISZERO - // stack: leaf_len == 0, leaf_len - %jumpi(%%finish) - // stack: leaf_len + // stack: value_len == 0, value_len, value_ptr + %jumpi(%%finish_loop) + // stack: value_len, value_ptr PROVER_INPUT(mpt) - // stack: leaf_part, leaf_len + // stack: leaf_part, value_len, value_ptr %append_to_trie_data - // stack: leaf_len + // stack: value_len, value_ptr %decrement - // stack: leaf_len' + // stack: value_len', value_ptr %jump(%%loop) -%%finish: +%%finish_loop: + // stack: value_len, value_ptr POP - // stack: (empty) + // stack: value_ptr + %jump(%%end) +%%return_null: + %stack (value_len) -> (0) +%%end: %endmacro diff --git a/evm/src/cpu/kernel/asm/mpt/write.asm b/evm/src/cpu/kernel/asm/mpt/write.asm deleted file mode 100644 index eab51d3e..00000000 --- a/evm/src/cpu/kernel/asm/mpt/write.asm +++ /dev/null @@ -1,47 +0,0 @@ -// TODO: Need a special case for deleting, if value = ''. -// Or canonicalize once, before final hashing, to remove empty leaves etc. - -// Return a copy of the given node, with the given key set to the given value. -// -// Pre stack: node_ptr, num_nibbles, key, value_ptr, retdest -// Post stack: updated_node_ptr -global mpt_insert: - // stack: node_ptr, num_nibbles, key, value_ptr, retdest - DUP1 %mload_trie_data - // stack: node_type, node_ptr, num_nibbles, key, value_ptr, retdest - // Increment node_ptr, so it points to the node payload instead of its type. - SWAP1 %increment SWAP1 - // stack: node_type, node_payload_ptr, num_nibbles, key, value_ptr, retdest - - DUP1 %eq_const(@MPT_NODE_EMPTY) %jumpi(mpt_insert_empty) - DUP1 %eq_const(@MPT_NODE_BRANCH) %jumpi(mpt_insert_branch) - DUP1 %eq_const(@MPT_NODE_EXTENSION) %jumpi(mpt_insert_extension) - DUP1 %eq_const(@MPT_NODE_LEAF) %jumpi(mpt_insert_leaf) - - // There's still the MPT_NODE_HASH case, but if we hit a hash node, - // it means the prover failed to provide necessary Merkle data, so panic. - PANIC - -mpt_insert_empty: - // stack: node_type, node_payload_ptr, num_nibbles, key, value_ptr, retdest - POP - // stack: node_payload_ptr, num_nibbles, key, value_ptr, retdest - PANIC // TODO - -mpt_insert_branch: - // stack: node_type, node_payload_ptr, num_nibbles, key, value_ptr, retdest - POP - // stack: node_payload_ptr, num_nibbles, key, value_ptr, retdest - PANIC // TODO - -mpt_insert_extension: - // stack: node_type, node_payload_ptr, num_nibbles, key, value_ptr, retdest - POP - // stack: node_payload_ptr, num_nibbles, key, value_ptr, retdest - PANIC // TODO - -mpt_insert_leaf: - // stack: node_type, node_payload_ptr, num_nibbles, key, value_ptr, retdest - POP - // stack: node_payload_ptr, num_nibbles, key, value_ptr, retdest - PANIC // TODO diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 589ba6b3..2eb9dcb9 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -168,10 +168,19 @@ impl<'a> Interpreter<'a> { self.memory.context_memory[0].segments[Segment::GlobalMetadata as usize].get(field as usize) } + pub(crate) fn set_global_metadata_field(&mut self, field: GlobalMetadata, value: U256) { + self.memory.context_memory[0].segments[Segment::GlobalMetadata as usize] + .set(field as usize, value) + } + pub(crate) fn get_trie_data(&self) -> &[U256] { &self.memory.context_memory[0].segments[Segment::TrieData as usize].content } + pub(crate) fn get_trie_data_mut(&mut self) -> &mut Vec { + &mut self.memory.context_memory[0].segments[Segment::TrieData as usize].content + } + pub(crate) fn get_rlp_memory(&self) -> Vec { self.memory.context_memory[0].segments[Segment::RlpRaw as usize] .content @@ -205,7 +214,7 @@ impl<'a> Interpreter<'a> { self.push(if x { U256::one() } else { U256::zero() }); } - fn pop(&mut self) -> U256 { + pub(crate) fn pop(&mut self) -> U256 { self.stack_mut().pop().expect("Pop on empty stack.") } diff --git a/evm/src/cpu/kernel/tests/mpt/insert.rs b/evm/src/cpu/kernel/tests/mpt/insert.rs new file mode 100644 index 00000000..7aeb4a1a --- /dev/null +++ b/evm/src/cpu/kernel/tests/mpt/insert.rs @@ -0,0 +1,173 @@ +use anyhow::Result; +use eth_trie_utils::partial_trie::{Nibbles, PartialTrie}; +use eth_trie_utils::trie_builder::InsertEntry; +use ethereum_types::{BigEndianHash, H256}; + +use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; +use crate::cpu::kernel::interpreter::Interpreter; +use crate::cpu::kernel::tests::mpt::{extension_to_leaf, test_account_1_rlp, test_account_2_rlp}; +use crate::generation::mpt::{all_mpt_prover_inputs_reversed, AccountRlp}; +use crate::generation::TrieInputs; + +#[test] +fn mpt_insert_empty() -> Result<()> { + let insert = InsertEntry { + nibbles: Nibbles { + count: 3, + packed: 0xABC.into(), + }, + v: test_account_2_rlp(), + }; + test_state_trie(Default::default(), insert) +} + +#[test] +#[ignore] // TODO: Enable when mpt_insert_leaf is done. +fn mpt_insert_leaf_same_key() -> Result<()> { + let key = Nibbles { + count: 3, + packed: 0xABC.into(), + }; + let state_trie = PartialTrie::Leaf { + nibbles: key, + value: test_account_1_rlp(), + }; + let insert = InsertEntry { + nibbles: key, + v: test_account_2_rlp(), + }; + + test_state_trie(state_trie, insert) +} + +#[test] +fn mpt_insert_branch_replacing_empty_child() -> Result<()> { + let children = std::array::from_fn(|_| Box::new(PartialTrie::Empty)); + let state_trie = PartialTrie::Branch { + children, + value: vec![], + }; + + let insert = InsertEntry { + nibbles: Nibbles { + count: 3, + packed: 0xABC.into(), + }, + v: test_account_2_rlp(), + }; + + test_state_trie(state_trie, insert) +} + +#[test] +#[ignore] // TODO: Enable when mpt_insert_extension is done. +fn mpt_insert_extension_to_leaf_same_key() -> Result<()> { + let state_trie = extension_to_leaf(test_account_1_rlp()); + + let insert = InsertEntry { + nibbles: Nibbles { + count: 3, + packed: 0xABCDEF.into(), + }, + v: test_account_2_rlp(), + }; + + test_state_trie(state_trie, insert) +} + +#[test] +#[ignore] // TODO: Enable when mpt_insert_leaf is done. +fn mpt_insert_branch_to_leaf_same_key() -> Result<()> { + let leaf = PartialTrie::Leaf { + nibbles: Nibbles { + count: 3, + packed: 0xBCD.into(), + }, + value: test_account_1_rlp(), + }; + let mut children = std::array::from_fn(|_| Box::new(PartialTrie::Empty)); + children[0xA] = Box::new(leaf); + let state_trie = PartialTrie::Branch { + children, + value: vec![], + }; + + let insert = InsertEntry { + nibbles: Nibbles { + count: 4, + packed: 0xABCD.into(), + }, + v: test_account_2_rlp(), + }; + + test_state_trie(state_trie, insert) +} + +fn test_state_trie(state_trie: PartialTrie, insert: InsertEntry) -> Result<()> { + let trie_inputs = TrieInputs { + state_trie: state_trie.clone(), + transactions_trie: Default::default(), + receipts_trie: Default::default(), + storage_tries: vec![], + }; + let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; + let mpt_insert_state_trie = KERNEL.global_labels["mpt_insert_state_trie"]; + let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; + + let initial_stack = vec![0xDEADBEEFu32.into()]; + let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); + interpreter.generation_state.mpt_prover_inputs = all_mpt_prover_inputs_reversed(&trie_inputs); + interpreter.run()?; + assert_eq!(interpreter.stack(), vec![]); + + // Next, execute mpt_insert_state_trie. + interpreter.offset = mpt_insert_state_trie; + let trie_data = interpreter.get_trie_data_mut(); + if trie_data.is_empty() { + // In the assembly we skip over 0, knowing trie_data[0] = 0 by default. + // Since we don't explicitly set it to 0, we need to do so here. + trie_data.push(0.into()); + } + let value_ptr = trie_data.len(); + let account: AccountRlp = rlp::decode(&insert.v).expect("Decoding failed"); + let account_data = account.to_vec(); + trie_data.push(account_data.len().into()); + trie_data.extend(account_data); + let trie_data_len = trie_data.len().into(); + interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, trie_data_len); + interpreter.push(0xDEADBEEFu32.into()); + interpreter.push(value_ptr.into()); // value_ptr + interpreter.push(insert.nibbles.packed); // key + interpreter.push(insert.nibbles.count.into()); // num_nibbles + + interpreter.run()?; + assert_eq!(interpreter.stack().len(), 0); + + // Now, execute mpt_hash_state_trie. + interpreter.offset = mpt_hash_state_trie; + interpreter.push(0xDEADBEEFu32.into()); + interpreter.run()?; + + assert_eq!( + interpreter.stack().len(), + 1, + "Expected 1 item on stack, found {:?}", + interpreter.stack() + ); + let hash = H256::from_uint(&interpreter.stack()[0]); + + let expected_state_trie_hash = apply_insert(state_trie, insert).calc_hash(); + assert_eq!(hash, expected_state_trie_hash); + + Ok(()) +} + +fn apply_insert(trie: PartialTrie, insert: InsertEntry) -> PartialTrie { + let mut trie = Box::new(trie); + if let Some(updated_trie) = PartialTrie::insert_into_trie(&mut trie, insert) { + *updated_trie + } else { + *trie + } +} diff --git a/evm/src/cpu/kernel/tests/mpt/load.rs b/evm/src/cpu/kernel/tests/mpt/load.rs index ca4f7071..ccf8353e 100644 --- a/evm/src/cpu/kernel/tests/mpt/load.rs +++ b/evm/src/cpu/kernel/tests/mpt/load.rs @@ -1,26 +1,19 @@ use anyhow::Result; -use ethereum_types::{BigEndianHash, H256, U256}; +use eth_trie_utils::partial_trie::{Nibbles, PartialTrie}; +use ethereum_types::{BigEndianHash, U256}; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::constants::trie_type::PartialTrieType; use crate::cpu::kernel::interpreter::Interpreter; -use crate::cpu::kernel::tests::mpt::extension_to_leaf; -use crate::generation::mpt::{all_mpt_prover_inputs_reversed, AccountRlp}; +use crate::cpu::kernel::tests::mpt::{extension_to_leaf, test_account_1, test_account_1_rlp}; +use crate::generation::mpt::all_mpt_prover_inputs_reversed; use crate::generation::TrieInputs; #[test] -fn load_all_mpts() -> Result<()> { - let account = AccountRlp { - nonce: U256::from(1111), - balance: U256::from(2222), - storage_root: H256::from_uint(&U256::from(3333)), - code_hash: H256::from_uint(&U256::from(4444)), - }; - let account_rlp = rlp::encode(&account); - +fn load_all_mpts_empty() -> Result<()> { let trie_inputs = TrieInputs { - state_trie: extension_to_leaf(account_rlp.to_vec()), + state_trie: Default::default(), transactions_trie: Default::default(), receipts_trie: Default::default(), storage_tries: vec![], @@ -28,13 +21,174 @@ fn load_all_mpts() -> Result<()> { let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; - let initial_stack = vec![0xdeadbeefu32.into()]; + let initial_stack = vec![0xDEADBEEFu32.into()]; + let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); + interpreter.generation_state.mpt_prover_inputs = all_mpt_prover_inputs_reversed(&trie_inputs); + interpreter.run()?; + assert_eq!(interpreter.stack(), vec![]); + + assert_eq!(interpreter.get_trie_data(), vec![]); + + assert_eq!( + interpreter.get_global_metadata_field(GlobalMetadata::StateTrieRoot), + 0.into() + ); + assert_eq!( + interpreter.get_global_metadata_field(GlobalMetadata::TransactionTrieRoot), + 0.into() + ); + assert_eq!( + interpreter.get_global_metadata_field(GlobalMetadata::ReceiptTrieRoot), + 0.into() + ); + + assert_eq!( + interpreter.get_global_metadata_field(GlobalMetadata::NumStorageTries), + trie_inputs.storage_tries.len().into() + ); + + Ok(()) +} + +#[test] +fn load_all_mpts_leaf() -> Result<()> { + let trie_inputs = TrieInputs { + state_trie: PartialTrie::Leaf { + nibbles: Nibbles { + count: 3, + packed: 0xABC.into(), + }, + value: test_account_1_rlp(), + }, + transactions_trie: Default::default(), + receipts_trie: Default::default(), + storage_tries: vec![], + }; + + let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; + + let initial_stack = vec![0xDEADBEEFu32.into()]; + let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); + interpreter.generation_state.mpt_prover_inputs = all_mpt_prover_inputs_reversed(&trie_inputs); + interpreter.run()?; + assert_eq!(interpreter.stack(), vec![]); + + let type_leaf = U256::from(PartialTrieType::Leaf as u32); + assert_eq!( + interpreter.get_trie_data(), + vec![ + 0.into(), + type_leaf, + 3.into(), + 0xABC.into(), + 5.into(), // value ptr + 4.into(), // value length + test_account_1().nonce, + test_account_1().balance, + test_account_1().storage_root.into_uint(), + test_account_1().code_hash.into_uint(), + ] + ); + + assert_eq!( + interpreter.get_global_metadata_field(GlobalMetadata::TransactionTrieRoot), + 0.into() + ); + assert_eq!( + interpreter.get_global_metadata_field(GlobalMetadata::ReceiptTrieRoot), + 0.into() + ); + + assert_eq!( + interpreter.get_global_metadata_field(GlobalMetadata::NumStorageTries), + trie_inputs.storage_tries.len().into() + ); + + Ok(()) +} + +#[test] +fn load_all_mpts_empty_branch() -> Result<()> { + let children = std::array::from_fn(|_| Box::new(PartialTrie::Empty)); + let state_trie = PartialTrie::Branch { + children, + value: vec![], + }; + let trie_inputs = TrieInputs { + state_trie, + transactions_trie: Default::default(), + receipts_trie: Default::default(), + storage_tries: vec![], + }; + + let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; + + let initial_stack = vec![0xDEADBEEFu32.into()]; + let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); + interpreter.generation_state.mpt_prover_inputs = all_mpt_prover_inputs_reversed(&trie_inputs); + interpreter.run()?; + assert_eq!(interpreter.stack(), vec![]); + + let type_branch = U256::from(PartialTrieType::Branch as u32); + assert_eq!( + interpreter.get_trie_data(), + vec![ + 0.into(), // First address is unused, so that 0 can be treated as a null pointer. + type_branch, + 0.into(), // child 0 + 0.into(), // ... + 0.into(), + 0.into(), + 0.into(), + 0.into(), + 0.into(), + 0.into(), + 0.into(), + 0.into(), + 0.into(), + 0.into(), + 0.into(), + 0.into(), + 0.into(), + 0.into(), // child 16 + 0.into(), // value_ptr + ] + ); + + assert_eq!( + interpreter.get_global_metadata_field(GlobalMetadata::TransactionTrieRoot), + 0.into() + ); + assert_eq!( + interpreter.get_global_metadata_field(GlobalMetadata::ReceiptTrieRoot), + 0.into() + ); + + assert_eq!( + interpreter.get_global_metadata_field(GlobalMetadata::NumStorageTries), + trie_inputs.storage_tries.len().into() + ); + + Ok(()) +} + +#[test] +fn load_all_mpts_ext_to_leaf() -> Result<()> { + let trie_inputs = TrieInputs { + state_trie: extension_to_leaf(test_account_1_rlp()), + transactions_trie: Default::default(), + receipts_trie: Default::default(), + storage_tries: vec![], + }; + + let load_all_mpts = KERNEL.global_labels["load_all_mpts"]; + + let initial_stack = vec![0xDEADBEEFu32.into()]; let mut interpreter = Interpreter::new_with_kernel(load_all_mpts, initial_stack); interpreter.generation_state.mpt_prover_inputs = all_mpt_prover_inputs_reversed(&trie_inputs); interpreter.run()?; assert_eq!(interpreter.stack(), vec![]); - let type_empty = U256::from(PartialTrieType::Empty as u32); let type_extension = U256::from(PartialTrieType::Extension as u32); let type_leaf = U256::from(PartialTrieType::Leaf as u32); assert_eq!( @@ -50,12 +204,10 @@ fn load_all_mpts() -> Result<()> { 0xDEF.into(), // key part 9.into(), // value pointer 4.into(), // value length - account.nonce, - account.balance, - account.storage_root.into_uint(), - account.code_hash.into_uint(), - type_empty, // txn trie - type_empty, // receipt trie + test_account_1().nonce, + test_account_1().balance, + test_account_1().storage_root.into_uint(), + test_account_1().code_hash.into_uint(), ] ); diff --git a/evm/src/cpu/kernel/tests/mpt/mod.rs b/evm/src/cpu/kernel/tests/mpt/mod.rs index 55a56653..e3414b38 100644 --- a/evm/src/cpu/kernel/tests/mpt/mod.rs +++ b/evm/src/cpu/kernel/tests/mpt/mod.rs @@ -1,10 +1,40 @@ use eth_trie_utils::partial_trie::{Nibbles, PartialTrie}; +use ethereum_types::{BigEndianHash, H256, U256}; + +use crate::generation::mpt::AccountRlp; mod hash; mod hex_prefix; +mod insert; mod load; mod read; +pub(crate) fn test_account_1() -> AccountRlp { + AccountRlp { + nonce: U256::from(1111), + balance: U256::from(2222), + storage_root: H256::from_uint(&U256::from(3333)), + code_hash: H256::from_uint(&U256::from(4444)), + } +} + +pub(crate) fn test_account_1_rlp() -> Vec { + rlp::encode(&test_account_1()).to_vec() +} + +pub(crate) fn test_account_2() -> AccountRlp { + AccountRlp { + nonce: U256::from(5555), + balance: U256::from(6666), + storage_root: H256::from_uint(&U256::from(7777)), + code_hash: H256::from_uint(&U256::from(8888)), + } +} + +pub(crate) fn test_account_2_rlp() -> Vec { + rlp::encode(&test_account_2()).to_vec() +} + /// A `PartialTrie` where an extension node leads to a leaf node containing an account. pub(crate) fn extension_to_leaf(value: Vec) -> PartialTrie { PartialTrie::Extension { diff --git a/evm/src/generation/mpt.rs b/evm/src/generation/mpt.rs index f6bc630d..e35364c6 100644 --- a/evm/src/generation/mpt.rs +++ b/evm/src/generation/mpt.rs @@ -13,6 +13,17 @@ pub(crate) struct AccountRlp { pub(crate) code_hash: H256, } +impl AccountRlp { + pub(crate) fn to_vec(&self) -> Vec { + vec![ + self.nonce, + self.balance, + self.storage_root.into_uint(), + self.code_hash.into_uint(), + ] + } +} + pub(crate) fn all_mpt_prover_inputs_reversed(trie_inputs: &TrieInputs) -> Vec { let mut inputs = all_mpt_prover_inputs(trie_inputs); inputs.reverse(); @@ -25,12 +36,7 @@ pub(crate) fn all_mpt_prover_inputs(trie_inputs: &TrieInputs) -> Vec { mpt_prover_inputs(&trie_inputs.state_trie, &mut prover_inputs, &|rlp| { let account: AccountRlp = rlp::decode(rlp).expect("Decoding failed"); - vec![ - account.nonce, - account.balance, - account.storage_root.into_uint(), - account.code_hash.into_uint(), - ] + account.to_vec() }); mpt_prover_inputs(&trie_inputs.transactions_trie, &mut prover_inputs, &|rlp| {