diff --git a/evm/src/cpu/kernel/asm/mpt/insert_extension.asm b/evm/src/cpu/kernel/asm/mpt/insert_extension.asm index 36458165..3ead805b 100644 --- a/evm/src/cpu/kernel/asm/mpt/insert_extension.asm +++ b/evm/src/cpu/kernel/asm/mpt/insert_extension.asm @@ -1,5 +1,201 @@ +/* +Insert into an extension node. +The high-level logic can be expressed with the following pseudocode: + +common_len, common_key, node_len, node_key, insert_len, insert_key = + split_common_prefix(node_len, node_key, insert_len, insert_key) + +if node_len == 0: + new_node = insert(node_child, insert_len, insert_key, insert_value) +else: + new_node = [MPT_TYPE_BRANCH] + [0] * 17 + + // Process the node's child. + if node_len > 1: + // The node key continues with multiple nibbles left, so we can't place + // node_child directly in the branch, but need an extension for it. + node_key_first, node_len, node_key = split_first_nibble(node_len, node_key) + new_node[node_key_first + 1] = [MPT_TYPE_EXTENSION, node_len, node_key, node_child] + else: + // The remaining node_key is a single nibble, so we can place node_child directly in the branch. + new_node[node_key + 1] = node_child + + // Process the inserted entry. + if insert_len > 0: + // The insert key continues. Add a leaf node for it. + insert_key_first, insert_len, insert_key = split_first_nibble(insert_len, insert_key) + new_node[insert_key_first + 1] = [MPT_TYPE_LEAF, insert_len, insert_key, insert_value] + else: + new_node[17] = insert_value + +if common_len > 0: + return [MPT_TYPE_EXTENSION, common_len, common_key, new_node] +else: + return new_node +*/ + global mpt_insert_extension: - // stack: node_type, node_payload_ptr, insert_len, insert_key, value_ptr, retdest + // stack: node_type, node_payload_ptr, insert_len, insert_key, insert_value_ptr, retdest POP - // stack: node_payload_ptr, insert_len, insert_key, value_ptr, retdest - PANIC // TODO + // stack: node_payload_ptr, insert_len, insert_key, insert_value_ptr, retdest + + // We start by loading the extension node's three fields: node_len, node_key, node_child_ptr + DUP1 %add_const(2) %mload_trie_data + // stack: node_child_ptr, node_payload_ptr, insert_len, insert_key, insert_value_ptr, retdest + %stack (node_child_ptr, node_payload_ptr, insert_len, insert_key) + -> (node_payload_ptr, insert_len, insert_key, node_child_ptr) + // stack: node_payload_ptr, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + DUP1 %increment %mload_trie_data + // stack: node_key, node_payload_ptr, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + SWAP1 %mload_trie_data + // stack: node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + + // Next, we split off any key prefix which is common to the node's key and the inserted key. + %split_common_prefix + // stack: common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + + // Now we branch based on whether the node key continues beyond the common prefix. + DUP3 %jumpi(node_key_continues) + + // The node key does not continue. In this case we recurse. Pseudocode: + // new_node = insert(node_child, insert_len, insert_key, insert_value) + // and then proceed to maybe_add_extension_for_common_key. + // stack: common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + PUSH maybe_add_extension_for_common_key + DUP9 // insert_value_ptr + DUP8 // insert_key + DUP8 // insert_len + DUP11 // node_child_ptr + %jump(mpt_insert) + +node_key_continues: + // stack: common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + // Allocate new_node, a branch node which is initially empty + // Pseudocode: new_node = [MPT_TYPE_BRANCH] + [0] * 17 + %get_trie_data_size // pointer to the branch node we're about to create + PUSH @MPT_NODE_BRANCH %append_to_trie_data + %rep 17 + PUSH 0 %append_to_trie_data + %endrep + +process_node_child: + // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + // We want to check if node_len > 1. We already know node_len > 0 since we're in node_key_continues, + // so it suffices to check 1 - node_len != 0 + DUP4 // node_len + PUSH 1 SUB + %jumpi(node_key_continues_multiple_nibbles) + + // If we got here, node_len = 1. + // Pseudocode: new_node[node_key + 1] = node_child + // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + DUP8 // node_child_ptr + DUP2 // new_node_ptr + %increment + DUP7 // node_key + ADD + %mstore_trie_data + // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + %jump(process_inserted_entry) + +node_key_continues_multiple_nibbles: + // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + // Pseudocode: node_key_first, node_len, node_key = split_first_nibble(node_len, node_key) + // To minimize stack manipulation, we won't actually mutate the node_len, node_key variables in our stack. + // Instead we will duplicate them, and leave the old ones alone; they won't be used. + DUP5 DUP5 + // stack: node_len, node_key, new_node_ptr, ... + %split_first_nibble + // stack: node_key_first, node_len, node_key, new_node_ptr, ... + + // Pseudocode: new_node[node_key_first + 1] = [MPT_TYPE_EXTENSION, node_len, node_key, node_child] + %get_trie_data_size // pointer to the extension node we're about to create + // stack: ext_node_ptr, node_key_first, node_len, node_key, new_node_ptr, ... + PUSH @MPT_NODE_EXTENSION %append_to_trie_data + // stack: ext_node_ptr, node_key_first, node_len, node_key, new_node_ptr, ... + SWAP2 %append_to_trie_data // Append node_len + // stack: node_key_first, ext_node_ptr, node_key, new_node_ptr, ... + SWAP2 %append_to_trie_data // Append node_key + // stack: ext_node_ptr, node_key_first, new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + DUP10 %append_to_trie_data // Append node_child_ptr + + SWAP1 + // stack: node_key_first, ext_node_ptr, new_node_ptr, ... + DUP3 // new_node_ptr + ADD + %increment + // stack: new_node_ptr + node_key_first + 1, ext_node_ptr, new_node_ptr, ... + %mstore_trie_data + %jump(process_inserted_entry) + +process_inserted_entry: + // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + DUP6 // insert_len + %jumpi(insert_key_continues) + + // If we got here, insert_len = 0, so we store the inserted value directly in our new branch node. + // Pseudocode: new_node[17] = insert_value + DUP9 // insert_value_ptr + DUP2 // new_node_ptr + %add_const(17) + %mstore_trie_data + %jump(maybe_add_extension_for_common_key) + +insert_key_continues: + // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + // Pseudocode: insert_key_first, insert_len, insert_key = split_first_nibble(insert_len, insert_key) + // To minimize stack manipulation, we won't actually mutate the node_len, node_key variables in our stack. + // Instead we will duplicate them, and leave the old ones alone; they won't be used. + DUP7 DUP7 + // stack: insert_len, insert_key, new_node_ptr, ... + %split_first_nibble + // stack: insert_key_first, insert_len, insert_key, new_node_ptr, ... + + // Pseudocode: new_node[insert_key_first + 1] = [MPT_TYPE_LEAF, insert_len, insert_key, insert_value] + %get_trie_data_size // pointer to the leaf node we're about to create + // stack: leaf_node_ptr, insert_key_first, insert_len, insert_key, new_node_ptr, ... + PUSH @MPT_NODE_LEAF %append_to_trie_data + // stack: leaf_node_ptr, insert_key_first, insert_len, insert_key, new_node_ptr, ... + SWAP2 %append_to_trie_data // Append insert_len + // stack: insert_key_first, leaf_node_ptr, insert_key, new_node_ptr, ... + SWAP2 %append_to_trie_data // Append insert_key + // stack: leaf_node_ptr, insert_key_first, new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + DUP11 %append_to_trie_data // Append insert_value_ptr + + SWAP1 + // stack: insert_key_first, leaf_node_ptr, new_node_ptr, ... + DUP3 // new_node_ptr + ADD + %increment + // stack: new_node_ptr + insert_key_first + 1, leaf_node_ptr, new_node_ptr, ... + %mstore_trie_data + %jump(maybe_add_extension_for_common_key) + +maybe_add_extension_for_common_key: + // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + // If common_len > 0, we need to add an extension node. + DUP2 %jumpi(add_extension_for_common_key) + // Otherwise, we simply return new_node_ptr. + SWAP8 + %pop8 + // stack: new_node_ptr, retdest + SWAP1 + JUMP + +add_extension_for_common_key: + // stack: new_node_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + // Pseudocode: return [MPT_TYPE_EXTENSION, common_len, common_key, new_node] + %get_trie_data_size // pointer to the extension node we're about to create + // stack: extension_ptr, new_node_ptr, common_len, common_key, ... + PUSH @MPT_NODE_EXTENSION %append_to_trie_data + SWAP2 %append_to_trie_data // Append common_len to our node + // stack: new_node_ptr, extension_ptr, common_key, ... + SWAP2 %append_to_trie_data // Append common_key to our node + // stack: extension_ptr, new_node_ptr, ... + SWAP1 %append_to_trie_data // Append new_node_ptr to our node + // stack: extension_ptr, node_len, node_key, insert_len, insert_key, node_child_ptr, insert_value_ptr, retdest + SWAP6 + %pop6 + // stack: extension_ptr, retdest + SWAP1 + JUMP diff --git a/evm/src/cpu/kernel/asm/mpt/insert_leaf.asm b/evm/src/cpu/kernel/asm/mpt/insert_leaf.asm index eeb7612a..6afe2f14 100644 --- a/evm/src/cpu/kernel/asm/mpt/insert_leaf.asm +++ b/evm/src/cpu/kernel/asm/mpt/insert_leaf.asm @@ -1,29 +1,35 @@ -// The high-level logic can be expressed with the following pseudocode: -// -// if node_len == insert_len && node_key == insert_key: -// return Leaf[node_key, insert_value] -// -// common_len, common_key, node_len, node_key, insert_len, insert_key = -// consume_common_prefix(node_len, node_key, insert_len, insert_key) -// -// branch = [MPT_TYPE_BRANCH] + [0] * 17 -// -// if node_len > 0: -// node_key_first, node_len, node_key = split_first_nibble(node_len, node_key) -// branch[node_key_first + 1] = Leaf[node_len, node_key, node_value] -// else: -// branch[17] = node_value -// -// if insert_len > 0: -// insert_key_first, insert_len, insert_key = split_first_nibble(insert_len, insert_key) -// branch[insert_key_first + 1] = Leaf[insert_len, insert_key, insert_value] -// else: -// branch[17] = insert_value -// -// if common_len > 0: -// return Extension[common_len, common_key, branch] -// else: -// return branch +/* +Insert into a leaf node. +The high-level logic can be expressed with the following pseudocode: + +if node_len == insert_len && node_key == insert_key: + return Leaf[node_key, insert_value] + +common_len, common_key, node_len, node_key, insert_len, insert_key = + split_common_prefix(node_len, node_key, insert_len, insert_key) + +branch = [MPT_TYPE_BRANCH] + [0] * 17 + +// Process the node's entry. +if node_len > 0: + node_key_first, node_len, node_key = split_first_nibble(node_len, node_key) + branch[node_key_first + 1] = [MPT_TYPE_LEAF, node_len, node_key, node_value] +else: + branch[17] = node_value + +// Process the inserted entry. +if insert_len > 0: + insert_key_first, insert_len, insert_key = split_first_nibble(insert_len, insert_key) + branch[insert_key_first + 1] = [MPT_TYPE_LEAF, insert_len, insert_key, insert_value] +else: + branch[17] = insert_value + +// Add an extension node if there is a common prefix. +if common_len > 0: + return [MPT_TYPE_EXTENSION, common_len, common_key, branch] +else: + return branch +*/ global mpt_insert_leaf: // stack: node_type, node_payload_ptr, insert_len, insert_key, insert_value_ptr, retdest @@ -61,15 +67,17 @@ global mpt_insert_leaf: // For the remaining cases, we will need a new branch node since the two keys diverge. // We may also need an extension node above it (if common_len > 0); we will handle that later. // For now, we allocate the branch node, initially with no children or value. - %get_trie_data_size + %get_trie_data_size // pointer to the branch node we're about to create PUSH @MPT_NODE_BRANCH %append_to_trie_data %rep 17 PUSH 0 %append_to_trie_data %endrep // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest - // Here, we branch based on whether each key continues beyond the common + // Now, we branch based on whether each key continues beyond the common // prefix, starting with the node key. + +process_node_entry: DUP4 // node_len %jumpi(node_key_continues) @@ -80,7 +88,7 @@ global mpt_insert_leaf: %add_const(17) %mstore_trie_data -finished_processing_node_value: +process_inserted_entry: DUP6 // insert_len %jumpi(insert_key_continues) @@ -91,25 +99,27 @@ finished_processing_node_value: %add_const(17) %mstore_trie_data -finished_processing_insert_value: +maybe_add_extension_for_common_key: // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest // If common_len > 0, we need to add an extension node. - DUP2 %jumpi(extension_for_common_key) - // Otherwise, we simply return our branch node. + DUP2 %jumpi(add_extension_for_common_key) + // Otherwise, we simply return branch_ptr. SWAP8 %pop8 // stack: branch_ptr, retdest SWAP1 JUMP -extension_for_common_key: +add_extension_for_common_key: // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest - // return Extension[common_len, common_key, branch] - %get_trie_data_size + // Pseudocode: return [MPT_TYPE_EXTENSION, common_len, common_key, branch] + %get_trie_data_size // pointer to the extension node we're about to create // stack: extension_ptr, branch_ptr, common_len, common_key, ... PUSH @MPT_NODE_EXTENSION %append_to_trie_data SWAP2 %append_to_trie_data // Append common_len to our node + // stack: branch_ptr, extension_ptr, common_key, ... SWAP2 %append_to_trie_data // Append common_key to our node + // stack: extension_ptr, branch_ptr, ... SWAP1 %append_to_trie_data // Append branch_ptr to our node // stack: extension_ptr, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest SWAP6 @@ -121,11 +131,13 @@ extension_for_common_key: node_key_continues: // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest // branch[node_key_first + 1] = Leaf[node_len, node_key, node_value] + // To minimize stack manipulation, we won't actually mutate the node_len, node_key variables in our stack. + // Instead we will duplicate them, and leave the old ones alone; they won't be used. DUP5 DUP5 // stack: node_len, node_key, branch_ptr, ... %split_first_nibble // stack: node_key_first, node_len, node_key, branch_ptr, ... - %get_trie_data_size + %get_trie_data_size // pointer to the leaf node we're about to create // stack: leaf_ptr, node_key_first, node_len, node_key, branch_ptr, ... SWAP1 DUP5 // branch_ptr @@ -138,16 +150,18 @@ node_key_continues: %append_to_trie_data // Append node_key to our leaf node // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest DUP8 %append_to_trie_data // Append node_value_ptr to our leaf node - %jump(finished_processing_node_value) + %jump(process_inserted_entry) insert_key_continues: // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest // branch[insert_key_first + 1] = Leaf[insert_len, insert_key, insert_value] + // To minimize stack manipulation, we won't actually mutate the insert_len, insert_key variables in our stack. + // Instead we will duplicate them, and leave the old ones alone; they won't be used. DUP7 DUP7 // stack: insert_len, insert_key, branch_ptr, ... %split_first_nibble // stack: insert_key_first, insert_len, insert_key, branch_ptr, ... - %get_trie_data_size + %get_trie_data_size // pointer to the leaf node we're about to create // stack: leaf_ptr, insert_key_first, insert_len, insert_key, branch_ptr, ... SWAP1 DUP5 // branch_ptr @@ -160,7 +174,7 @@ insert_key_continues: %append_to_trie_data // Append insert_key to our leaf node // stack: branch_ptr, common_len, common_key, node_len, node_key, insert_len, insert_key, node_value_ptr, insert_value_ptr, retdest DUP9 %append_to_trie_data // Append insert_value_ptr to our leaf node - %jump(finished_processing_insert_value) + %jump(maybe_add_extension_for_common_key) keys_match: // The keys match exactly, so we simply create a new leaf node with the new value.xs @@ -168,7 +182,7 @@ keys_match: %stack (node_len, node_key, insert_len, insert_key, node_payload_ptr, insert_value_ptr) -> (node_len, node_key, insert_value_ptr) // stack: common_len, common_key, insert_value_ptr, retdest - %get_trie_data_size + %get_trie_data_size // pointer to the leaf node we're about to create // stack: updated_leaf_ptr, common_len, common_key, insert_value_ptr, retdest PUSH @MPT_NODE_LEAF %append_to_trie_data SWAP1 %append_to_trie_data // Append common_len to our leaf node diff --git a/evm/src/cpu/kernel/tests/mpt/insert.rs b/evm/src/cpu/kernel/tests/mpt/insert.rs index 872ef4af..103bdd2e 100644 --- a/evm/src/cpu/kernel/tests/mpt/insert.rs +++ b/evm/src/cpu/kernel/tests/mpt/insert.rs @@ -6,7 +6,7 @@ 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::cpu::kernel::tests::mpt::{test_account_1_rlp, test_account_2_rlp}; use crate::generation::mpt::{all_mpt_prover_inputs_reversed, AccountRlp}; use crate::generation::TrieInputs; @@ -36,7 +36,6 @@ fn mpt_insert_leaf_identical_keys() -> Result<()> { nibbles: key, v: test_account_2_rlp(), }; - test_state_trie(state_trie, insert) } @@ -56,7 +55,6 @@ fn mpt_insert_leaf_nonoverlapping_keys() -> Result<()> { }, v: test_account_2_rlp(), }; - test_state_trie(state_trie, insert) } @@ -76,7 +74,44 @@ fn mpt_insert_leaf_overlapping_keys() -> Result<()> { }, v: test_account_2_rlp(), }; + test_state_trie(state_trie, insert) +} +#[test] +fn mpt_insert_leaf_insert_key_extends_leaf_key() -> Result<()> { + let state_trie = PartialTrie::Leaf { + nibbles: Nibbles { + count: 3, + packed: 0xABC.into(), + }, + value: test_account_1_rlp(), + }; + let insert = InsertEntry { + nibbles: Nibbles { + count: 5, + packed: 0xABCDE.into(), + }, + v: test_account_2_rlp(), + }; + test_state_trie(state_trie, insert) +} + +#[test] +fn mpt_insert_leaf_leaf_key_extends_insert_key() -> Result<()> { + let state_trie = PartialTrie::Leaf { + nibbles: Nibbles { + count: 5, + packed: 0xABCDE.into(), + }, + value: test_account_1_rlp(), + }; + let insert = InsertEntry { + nibbles: Nibbles { + count: 3, + packed: 0xABC.into(), + }, + v: test_account_2_rlp(), + }; test_state_trie(state_trie, insert) } @@ -100,18 +135,32 @@ fn mpt_insert_branch_replacing_empty_child() -> Result<()> { } #[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 { + let mut children = std::array::from_fn(|_| Box::new(PartialTrie::Empty)); + children[0xD] = Box::new(PartialTrie::Leaf { + nibbles: Nibbles { + count: 2, + packed: 0xEF.into(), + }, + value: test_account_1_rlp(), + }); + let state_trie = PartialTrie::Extension { nibbles: Nibbles { count: 3, + packed: 0xABC.into(), + }, + child: Box::new(PartialTrie::Branch { + children, + value: test_account_1_rlp(), + }), + }; + let insert = InsertEntry { + nibbles: Nibbles { + count: 6, packed: 0xABCDEF.into(), }, v: test_account_2_rlp(), }; - test_state_trie(state_trie, insert) }