mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-03 14:23:07 +00:00
MPT deletion (#1025)
* First try * Fixes * Fix sstore * Comments * Clippy * Fix aggregator.rs * PR feedback
This commit is contained in:
parent
202985b24f
commit
74ba303255
@ -111,6 +111,8 @@ pub(crate) fn combined_kernel() -> Kernel {
|
||||
include_str!("asm/memory/txn_fields.asm"),
|
||||
include_str!("asm/mpt/accounts.asm"),
|
||||
include_str!("asm/mpt/delete/delete.asm"),
|
||||
include_str!("asm/mpt/delete/delete_branch.asm"),
|
||||
include_str!("asm/mpt/delete/delete_extension.asm"),
|
||||
include_str!("asm/mpt/hash/hash.asm"),
|
||||
include_str!("asm/mpt/hash/hash_trie_specific.asm"),
|
||||
include_str!("asm/mpt/hex_prefix.asm"),
|
||||
|
||||
@ -1,6 +1,24 @@
|
||||
// Return a copy of the given node with the given key deleted.
|
||||
// Assumes that the key is in the trie.
|
||||
//
|
||||
// Pre stack: node_ptr, num_nibbles, key, retdest
|
||||
// Post stack: updated_node_ptr
|
||||
global mpt_delete:
|
||||
PANIC // TODO
|
||||
// stack: node_ptr, num_nibbles, key, retdest
|
||||
DUP1 %mload_trie_data
|
||||
// stack: node_type, node_ptr, num_nibbles, key, 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, retdest
|
||||
|
||||
DUP1 %eq_const(@MPT_NODE_BRANCH) %jumpi(mpt_delete_branch)
|
||||
DUP1 %eq_const(@MPT_NODE_EXTENSION) %jumpi(mpt_delete_extension)
|
||||
DUP1 %eq_const(@MPT_NODE_LEAF) %jumpi(mpt_delete_leaf)
|
||||
%eq_const(@MPT_NODE_EMPTY) %jumpi(panic) // This should never happen.
|
||||
PANIC
|
||||
|
||||
mpt_delete_leaf:
|
||||
// stack: node_type, node_payload_ptr, num_nibbles, key, retdest
|
||||
%pop4
|
||||
PUSH 0 // empty node ptr
|
||||
SWAP1 JUMP
|
||||
|
||||
151
evm/src/cpu/kernel/asm/mpt/delete/delete_branch.asm
Normal file
151
evm/src/cpu/kernel/asm/mpt/delete/delete_branch.asm
Normal file
@ -0,0 +1,151 @@
|
||||
// Delete from a branch node.
|
||||
// Algorithm is roughly:
|
||||
// - Delete `(num_nibbles-1, key[1:])` from `branch[key[0]]`.
|
||||
// - If the returned node is non-empty, update the branch node and return it.
|
||||
// - Otherwise, count the number of non-empty children of the branch node.
|
||||
// - If there are more than one, update the branch node and return it.
|
||||
// - If there is exactly one, transform the branch node into an leaf/extension node and return it.
|
||||
// Assumes that `num_nibbles>0` and that the value of the branch node is zero.
|
||||
// TODO: May need to revisit these assumptions depending on how the receipt trie is implemented.
|
||||
global mpt_delete_branch:
|
||||
// stack: node_type, node_payload_ptr, num_nibbles, key, retdest
|
||||
POP
|
||||
// stack: node_payload_ptr, num_nibbles, key, retdest
|
||||
DUP2 ISZERO %jumpi(panic) // This should never happen.
|
||||
DUP3 DUP3
|
||||
// stack: num_nibbles, key, node_payload_ptr, num_nibbles, key, retdest
|
||||
%split_first_nibble
|
||||
%stack (first_nibble, num_nibbles, key, node_payload_ptr, old_num_nibbles, old_key) ->
|
||||
(node_payload_ptr, first_nibble, num_nibbles, key, after_mpt_delete_branch, first_nibble, node_payload_ptr)
|
||||
ADD
|
||||
// stack: child_ptr_ptr, num_nibbles, key, after_mpt_delete_branch, first_nibble, node_payload_ptr, retdest
|
||||
%mload_trie_data
|
||||
%jump(mpt_delete)
|
||||
|
||||
after_mpt_delete_branch:
|
||||
// stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
// If the updated child is empty, check if we need to normalize the branch node.
|
||||
DUP1 %mload_trie_data ISZERO %jumpi(maybe_normalize_branch)
|
||||
|
||||
// Make a copy of the branch node and set `branch[first_nibble] = updated_child_ptr`.
|
||||
update_branch:
|
||||
// stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
%get_trie_data_size
|
||||
// stack: updated_branch_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
PUSH @MPT_NODE_BRANCH %append_to_trie_data
|
||||
// stack: updated_branch_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
DUP4 %mload_trie_data %append_to_trie_data // Copy child[0]
|
||||
DUP4 %add_const(1) %mload_trie_data %append_to_trie_data // ...
|
||||
DUP4 %add_const(2) %mload_trie_data %append_to_trie_data
|
||||
DUP4 %add_const(3) %mload_trie_data %append_to_trie_data
|
||||
DUP4 %add_const(4) %mload_trie_data %append_to_trie_data
|
||||
DUP4 %add_const(5) %mload_trie_data %append_to_trie_data
|
||||
DUP4 %add_const(6) %mload_trie_data %append_to_trie_data
|
||||
DUP4 %add_const(7) %mload_trie_data %append_to_trie_data
|
||||
DUP4 %add_const(8) %mload_trie_data %append_to_trie_data
|
||||
DUP4 %add_const(9) %mload_trie_data %append_to_trie_data
|
||||
DUP4 %add_const(10) %mload_trie_data %append_to_trie_data
|
||||
DUP4 %add_const(11) %mload_trie_data %append_to_trie_data
|
||||
DUP4 %add_const(12) %mload_trie_data %append_to_trie_data
|
||||
DUP4 %add_const(13) %mload_trie_data %append_to_trie_data
|
||||
DUP4 %add_const(14) %mload_trie_data %append_to_trie_data
|
||||
DUP4 %add_const(15) %mload_trie_data %append_to_trie_data // Copy child[15]
|
||||
DUP4 %add_const(16) %mload_trie_data %append_to_trie_data // Copy value_ptr
|
||||
// stack: updated_branch_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
SWAP1
|
||||
// stack: updated_child_ptr, updated_branch_ptr, first_nibble, node_payload_ptr, retdest
|
||||
DUP2 %increment DUP4 ADD
|
||||
// stack: updated_branch_ptr+first_nibble+1, updated_child_ptr, updated_branch_ptr, first_nibble, node_payload_ptr, retdest
|
||||
%mstore_trie_data
|
||||
%stack (updated_branch_ptr, first_nibble, node_payload_ptr, retdest) -> (retdest, updated_branch_ptr)
|
||||
JUMP
|
||||
|
||||
// The updated child is empty. Count how many non-empty children the branch node has.
|
||||
// If it's one, transform the branch node into an leaf/extension node and return it.
|
||||
maybe_normalize_branch:
|
||||
// stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
PUSH 0 %mstore_kernel_general(0) PUSH 0 %mstore_kernel_general(1)
|
||||
// stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
PUSH 0
|
||||
// Loop from i=0..16 excluding `first_nibble` and store the number of non-empty children in
|
||||
// KernelGeneral[0]. Also store the last non-empty child in KernelGeneral[1].
|
||||
loop:
|
||||
// stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
DUP1 DUP4 EQ %jumpi(loop_eq_first_nibble)
|
||||
// stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
DUP1 %eq_const(16) %jumpi(loop_end)
|
||||
DUP1 DUP5 ADD %mload_trie_data %mload_trie_data ISZERO ISZERO %jumpi(loop_non_empty)
|
||||
// stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
%increment %jump(loop)
|
||||
loop_eq_first_nibble:
|
||||
// stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
%increment %jump(loop)
|
||||
loop_non_empty:
|
||||
// stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
%mload_kernel_general(0) %increment %mstore_kernel_general(0)
|
||||
DUP1 %mstore_kernel_general(1)
|
||||
%increment %jump(loop)
|
||||
loop_end:
|
||||
// stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
POP
|
||||
// stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
// If there's more than one non-empty child, simply update the branch node.
|
||||
%mload_kernel_general(0) %gt_const(1) %jumpi(update_branch)
|
||||
%mload_kernel_general(0) ISZERO %jumpi(panic) // This should never happen.
|
||||
// Otherwise, transform the branch node into a leaf/extension node.
|
||||
// stack: updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
%mload_kernel_general(1)
|
||||
// stack: i, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
DUP4 ADD %mload_trie_data
|
||||
// stack: only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
DUP1 %mload_trie_data %eq_const(@MPT_NODE_BRANCH) %jumpi(maybe_normalize_branch_branch)
|
||||
DUP1 %mload_trie_data %eq_const(@MPT_NODE_EXTENSION) %jumpi(maybe_normalize_branch_leafext)
|
||||
DUP1 %mload_trie_data %eq_const(@MPT_NODE_LEAF) %jumpi(maybe_normalize_branch_leafext)
|
||||
PANIC // This should never happen.
|
||||
|
||||
// The only child of the branch node is a branch node.
|
||||
// Transform the branch node into an extension node of length 1.
|
||||
maybe_normalize_branch_branch:
|
||||
// stack: only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
%get_trie_data_size // pointer to the extension node we're about to create
|
||||
// stack: extension_ptr, only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
PUSH @MPT_NODE_EXTENSION %append_to_trie_data
|
||||
// stack: extension_ptr, only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
PUSH 1 %append_to_trie_data // Append node_len to our node
|
||||
// stack: extension_ptr, only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
%mload_kernel_general(1) %append_to_trie_data // Append node_key to our node
|
||||
// stack: extension_ptr, only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
SWAP1 %append_to_trie_data // Append updated_child_node_ptr to our node
|
||||
%stack (extension_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest) -> (retdest, extension_ptr)
|
||||
JUMP
|
||||
|
||||
// The only child of the branch node is a leaf/extension node.
|
||||
// Transform the branch node into an leaf/extension node of length 1+len(child).
|
||||
maybe_normalize_branch_leafext:
|
||||
// stack: only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
DUP1 %mload_trie_data
|
||||
// stack: child_type, only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
DUP2 %increment %mload_trie_data
|
||||
// stack: child_len, child_type, only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
DUP3 %add_const(2) %mload_trie_data
|
||||
// stack: child_key, child_len, child_type, only_child_ptr, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
SWAP3 %add_const(3) %mload_trie_data
|
||||
// stack: child_value_ptr, child_len, child_type, child_key, updated_child_ptr, first_nibble, node_payload_ptr, retdest
|
||||
%mload_kernel_general(1)
|
||||
%stack (i, child_value_ptr, child_len, child_type, child_key, updated_child_ptr, first_nibble, node_payload_ptr) ->
|
||||
(1, i, child_len, child_key, child_type, child_value_ptr)
|
||||
%merge_nibbles
|
||||
// stack: len, key, child_type, value_ptr, retdest
|
||||
%get_trie_data_size // pointer to the leaf/extension node we're about to create
|
||||
// stack: node_ptr, len, key, child_type, value_ptr, retdest
|
||||
SWAP3
|
||||
// stack: child_type, len, key, node_ptr, value_ptr, retdest
|
||||
%append_to_trie_data // Append type to our node
|
||||
// stack: len, key, node_ptr, value_ptr, retdest
|
||||
%append_to_trie_data // Append len to our node
|
||||
// stack: key, node_ptr, value_ptr, retdest
|
||||
%append_to_trie_data // Append key to our node
|
||||
// stack: node_ptr, value_ptr, retdest
|
||||
SWAP1 %append_to_trie_data // Append value_ptr to our node
|
||||
// stack: node_ptr, retdest
|
||||
SWAP1 JUMP
|
||||
99
evm/src/cpu/kernel/asm/mpt/delete/delete_extension.asm
Normal file
99
evm/src/cpu/kernel/asm/mpt/delete/delete_extension.asm
Normal file
@ -0,0 +1,99 @@
|
||||
// Delete from an extension node.
|
||||
// Algorithm is roughly:
|
||||
// - Let `k = length(node)`
|
||||
// - Delete `(num_nibbles-k, key[k:])` from `node.child`.
|
||||
// - If the returned child node is a branch node, the current node is replaced with an extension node with updated child.
|
||||
// - If the returned child node is an extension node, we merge the two extension nodes into one extension node.
|
||||
// - If the returned child node is a leaf node, we merge the two nodes into one leaf node.
|
||||
global mpt_delete_extension:
|
||||
// stack: node_type, node_payload_ptr, num_nibbles, key, retdest
|
||||
POP
|
||||
// stack: node_payload_ptr, num_nibbles, key, retdest
|
||||
DUP1 %mload_trie_data
|
||||
// stack: node_len, node_payload_ptr, num_nibbles, key, retdest
|
||||
DUP2 %increment %mload_trie_data
|
||||
%stack (node_key, node_len, node_payload_ptr, num_nibbles, key, retdest) ->
|
||||
(node_len, num_nibbles, key, node_payload_ptr, node_len, node_key, retdest)
|
||||
%truncate_nibbles
|
||||
// stack: num_nibbles, key, node_payload_ptr, node_len, node_key, retdest
|
||||
SWAP2
|
||||
// stack: node_payload_ptr, key, num_nibbles, node_len, node_key, retdest
|
||||
%add_const(2) %mload_trie_data
|
||||
%stack (node_child_ptr, key, num_nibbles, node_len, node_key, retdest) ->
|
||||
(node_child_ptr, num_nibbles, key, after_mpt_delete_extension, node_len, node_key, retdest)
|
||||
%jump(mpt_delete)
|
||||
|
||||
after_mpt_delete_extension:
|
||||
// stack: updated_child_node_ptr, node_len, node_key, retdest
|
||||
DUP1 %mload_trie_data
|
||||
// stack: child_type, updated_child_node_ptr, node_len, node_key, retdest
|
||||
DUP1 %eq_const(@MPT_NODE_BRANCH) %jumpi(after_mpt_delete_extension_branch)
|
||||
DUP1 %eq_const(@MPT_NODE_EXTENSION) %jumpi(after_mpt_delete_extension_extension)
|
||||
DUP1 %eq_const(@MPT_NODE_LEAF) %jumpi(after_mpt_delete_extension_leaf)
|
||||
%eq_const(@MPT_NODE_EMPTY) %jumpi(panic) // This should never happen.
|
||||
PANIC
|
||||
|
||||
after_mpt_delete_extension_branch:
|
||||
// stack: child_type, updated_child_node_ptr, node_len, node_key, retdest
|
||||
POP
|
||||
// stack: updated_child_node_ptr, node_len, node_key, retdest
|
||||
%get_trie_data_size // pointer to the extension node we're about to create
|
||||
// stack: extension_ptr, updated_child_node_ptr, node_len, node_key, retdest
|
||||
PUSH @MPT_NODE_EXTENSION %append_to_trie_data
|
||||
// stack: extension_ptr, updated_child_node_ptr, node_len, node_key, retdest
|
||||
SWAP2 %append_to_trie_data // Append node_len to our node
|
||||
// stack: updated_child_node_ptr, extension_ptr, node_key, retdest
|
||||
SWAP2 %append_to_trie_data // Append node_key to our node
|
||||
// stack: extension_ptr, updated_child_node_ptr, retdest
|
||||
SWAP1 %append_to_trie_data // Append updated_child_node_ptr to our node
|
||||
// stack: extension_ptr, retdest
|
||||
SWAP1 JUMP
|
||||
|
||||
after_mpt_delete_extension_extension:
|
||||
// stack: child_type, updated_child_node_ptr, node_len, node_key, retdest
|
||||
POP
|
||||
// stack: updated_child_node_ptr, node_len, node_key, retdest
|
||||
DUP1 %increment %mload_trie_data
|
||||
// stack: child_len, updated_child_node_ptr, node_len, node_key, retdest
|
||||
DUP2 %add_const(2) %mload_trie_data
|
||||
// stack: child_key, child_len, updated_child_node_ptr, node_len, node_key, retdest
|
||||
SWAP2 %add_const(3) %mload_trie_data
|
||||
%stack (grandchild_ptr, child_len, child_key, node_len, node_key) -> (node_len, node_key, child_len, child_key, grandchild_ptr)
|
||||
%merge_nibbles
|
||||
// stack: len, key, grandchild_ptr, retdest
|
||||
%get_trie_data_size // pointer to the extension node we're about to create
|
||||
// stack: extension_ptr, len, key, grandchild_ptr, retdest
|
||||
PUSH @MPT_NODE_EXTENSION %append_to_trie_data
|
||||
// stack: extension_ptr, len, key, grandchild_ptr, retdest
|
||||
SWAP1 %append_to_trie_data // Append len to our node
|
||||
// stack: extension_ptr, key, grandchild_ptr, retdest
|
||||
SWAP1 %append_to_trie_data // Append key to our node
|
||||
// stack: extension_ptr, grandchild_ptr, retdest
|
||||
SWAP1 %append_to_trie_data // Append grandchild_ptr to our node
|
||||
// stack: extension_ptr, retdest
|
||||
SWAP1 JUMP
|
||||
|
||||
// Essentially the same as `after_mpt_delete_extension_extension`. TODO: Could merge in a macro or common function.
|
||||
after_mpt_delete_extension_leaf:
|
||||
// stack: child_type, updated_child_node_ptr, node_len, node_key, retdest
|
||||
POP
|
||||
// stack: updated_child_node_ptr, node_len, node_key, retdest
|
||||
DUP1 %increment %mload_trie_data
|
||||
// stack: child_len, updated_child_node_ptr, node_len, node_key, retdest
|
||||
DUP2 %add_const(2) %mload_trie_data
|
||||
// stack: child_key, child_len, updated_child_node_ptr, node_len, node_key, retdest
|
||||
SWAP2 %add_const(3) %mload_trie_data
|
||||
%stack (value_ptr, child_len, child_key, node_len, node_key) -> (node_len, node_key, child_len, child_key, value_ptr)
|
||||
%merge_nibbles
|
||||
// stack: len, key, value_ptr, retdest
|
||||
%get_trie_data_size // pointer to the leaf node we're about to create
|
||||
// stack: leaf_ptr, len, key, value_ptr, retdest
|
||||
PUSH @MPT_NODE_LEAF %append_to_trie_data
|
||||
// stack: leaf_ptr, len, key, value_ptr, retdest
|
||||
SWAP1 %append_to_trie_data // Append len to our node
|
||||
// stack: leaf_ptr, key, value_ptr, retdest
|
||||
SWAP1 %append_to_trie_data // Append key to our node
|
||||
// stack: leaf_ptr, value_ptr, retdest
|
||||
SWAP1 %append_to_trie_data // Append value_ptr to our node
|
||||
// stack: leaf_ptr, retdest
|
||||
SWAP1 JUMP
|
||||
@ -88,8 +88,9 @@ sstore_after_refund:
|
||||
%stack (kexit_info, current_value, slot, value) -> (value, current_value, slot, value, kexit_info)
|
||||
EQ %jumpi(sstore_noop)
|
||||
|
||||
// TODO: If value = 0, delete the key instead of inserting 0.
|
||||
// If the value is zero, delete the slot from the storage trie.
|
||||
// stack: slot, value, kexit_info
|
||||
DUP2 ISZERO %jumpi(sstore_delete)
|
||||
|
||||
// First we write the value to MPT data, and get a pointer to it.
|
||||
%get_trie_data_size
|
||||
@ -136,3 +137,16 @@ sstore_noop:
|
||||
// stack: slot, value, kexit_info
|
||||
%pop2
|
||||
EXIT_KERNEL
|
||||
|
||||
// Delete the slot from the storage trie.
|
||||
sstore_delete:
|
||||
// stack: slot, value, kexit_info
|
||||
SWAP1 POP
|
||||
PUSH after_storage_insert SWAP1
|
||||
// stack: slot, after_storage_insert, kexit_info
|
||||
%slot_to_storage_key
|
||||
// stack: storage_key, after_storage_insert, kexit_info
|
||||
PUSH 64 // storage_key has 64 nibbles
|
||||
%current_storage_trie
|
||||
// stack: storage_root_ptr, 64, storage_key, after_storage_insert, kexit_info
|
||||
%jump(mpt_delete)
|
||||
|
||||
@ -90,6 +90,26 @@
|
||||
// stack: first_nibble, num_nibbles, key
|
||||
%endmacro
|
||||
|
||||
// Remove the first `k` nibbles from a key part.
|
||||
// def truncate_nibbles(k, num_nibbles, key):
|
||||
// num_nibbles -= k
|
||||
// num_nibbles_x4 = num_nibbles * 4
|
||||
// lead_nibbles = key >> num_nibbles_x4
|
||||
// key -= (lead_nibbles << num_nibbles_x4)
|
||||
// return (num_nibbles, key)
|
||||
%macro truncate_nibbles
|
||||
// stack: k, num_nibbles, key
|
||||
SWAP1 SUB
|
||||
// stack: num_nibbles, key
|
||||
DUP1 %mul_const(4)
|
||||
%stack (num_nibbles_x4, num_nibbles, key) -> (num_nibbles_x4, key, num_nibbles_x4, num_nibbles, key)
|
||||
SHR
|
||||
%stack (lead_nibbles, num_nibbles_x4, num_nibbles, key) -> (num_nibbles_x4, lead_nibbles, key, num_nibbles)
|
||||
SHL SWAP1 SUB
|
||||
// stack: key, num_nibbles
|
||||
SWAP1
|
||||
%endmacro
|
||||
|
||||
// Split off the common prefix among two key parts.
|
||||
//
|
||||
// Pre stack: len_1, key_1, len_2, key_2
|
||||
@ -183,6 +203,17 @@
|
||||
// stack: len_common, key_common, len_1, key_1, len_2, key_2
|
||||
%endmacro
|
||||
|
||||
// Remove the first `k` nibbles from a key part.
|
||||
// def merge_nibbles(front_len, front_key, back_len, back_key):
|
||||
// return (front_len + back_len, (front_key<<(back_len*4)) + back_key)
|
||||
%macro merge_nibbles
|
||||
// stack: front_len, front_key, back_len, back_key
|
||||
%stack (front_len, front_key, back_len, back_key) -> (back_len, front_key, back_key, back_len, front_len)
|
||||
%mul_const(4) SHL ADD
|
||||
// stack: new_key, back_len, front_len
|
||||
SWAP2 ADD
|
||||
%endmacro
|
||||
|
||||
// Computes state_key = Keccak256(addr). Clobbers @SEGMENT_KERNEL_GENERAL.
|
||||
%macro addr_to_state_key
|
||||
%keccak256_word(20)
|
||||
|
||||
118
evm/src/cpu/kernel/tests/mpt/delete.rs
Normal file
118
evm/src/cpu/kernel/tests/mpt/delete.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use anyhow::Result;
|
||||
use eth_trie_utils::nibbles::Nibbles;
|
||||
use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie};
|
||||
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::{nibbles_64, test_account_1_rlp, test_account_2};
|
||||
use crate::generation::mpt::{all_mpt_prover_inputs_reversed, AccountRlp};
|
||||
use crate::generation::TrieInputs;
|
||||
use crate::Node;
|
||||
|
||||
#[test]
|
||||
fn mpt_delete_empty() -> Result<()> {
|
||||
test_state_trie(Default::default(), nibbles_64(0xABC), test_account_2())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mpt_delete_leaf_nonoverlapping_keys() -> Result<()> {
|
||||
let state_trie = Node::Leaf {
|
||||
nibbles: nibbles_64(0xABC),
|
||||
value: test_account_1_rlp(),
|
||||
}
|
||||
.into();
|
||||
test_state_trie(state_trie, nibbles_64(0x123), test_account_2())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mpt_delete_leaf_overlapping_keys() -> Result<()> {
|
||||
let state_trie = Node::Leaf {
|
||||
nibbles: nibbles_64(0xABC),
|
||||
value: test_account_1_rlp(),
|
||||
}
|
||||
.into();
|
||||
test_state_trie(state_trie, nibbles_64(0xADE), test_account_2())
|
||||
}
|
||||
|
||||
/// Note: The account's storage_root is ignored, as we can't insert a new storage_root without the
|
||||
/// accompanying trie data. An empty trie's storage_root is used instead.
|
||||
fn test_state_trie(
|
||||
state_trie: HashedPartialTrie,
|
||||
k: Nibbles,
|
||||
mut account: AccountRlp,
|
||||
) -> Result<()> {
|
||||
assert_eq!(k.count, 64);
|
||||
|
||||
// Ignore any storage_root; see documentation note.
|
||||
account.storage_root = HashedPartialTrie::from(Node::Empty).hash();
|
||||
|
||||
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_delete = KERNEL.global_labels["mpt_delete"];
|
||||
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.generation_state.registers.program_counter = 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();
|
||||
trie_data.push(account.nonce);
|
||||
trie_data.push(account.balance);
|
||||
// In memory, storage_root gets interpreted as a pointer to a storage trie,
|
||||
// so we have to ensure the pointer is valid. It's easiest to set it to 0,
|
||||
// which works as an empty node, since trie_data[0] = 0 = MPT_TYPE_EMPTY.
|
||||
trie_data.push(H256::zero().into_uint());
|
||||
trie_data.push(account.code_hash.into_uint());
|
||||
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(k.packed); // key
|
||||
interpreter.run()?;
|
||||
assert_eq!(
|
||||
interpreter.stack().len(),
|
||||
0,
|
||||
"Expected empty stack after insert, found {:?}",
|
||||
interpreter.stack()
|
||||
);
|
||||
|
||||
// Next, execute mpt_delete, deleting the account we just inserted.
|
||||
let state_trie_ptr = interpreter.get_global_metadata_field(GlobalMetadata::StateTrieRoot);
|
||||
interpreter.generation_state.registers.program_counter = mpt_delete;
|
||||
interpreter.push(0xDEADBEEFu32.into());
|
||||
interpreter.push(k.packed);
|
||||
interpreter.push(64.into());
|
||||
interpreter.push(state_trie_ptr);
|
||||
interpreter.run()?;
|
||||
let state_trie_ptr = interpreter.pop();
|
||||
interpreter.set_global_metadata_field(GlobalMetadata::StateTrieRoot, state_trie_ptr);
|
||||
|
||||
// Now, execute mpt_hash_state_trie.
|
||||
interpreter.generation_state.registers.program_counter = mpt_hash_state_trie;
|
||||
interpreter.push(0xDEADBEEFu32.into());
|
||||
interpreter.run()?;
|
||||
|
||||
let state_trie_hash = H256::from_uint(&interpreter.pop());
|
||||
let expected_state_trie_hash = state_trie.hash();
|
||||
assert_eq!(state_trie_hash, expected_state_trie_hash);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -5,6 +5,7 @@ use ethereum_types::{BigEndianHash, H256, U256};
|
||||
use crate::generation::mpt::AccountRlp;
|
||||
use crate::Node;
|
||||
|
||||
mod delete;
|
||||
mod hash;
|
||||
mod hex_prefix;
|
||||
mod insert;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user