MPT deletion (#1025)

* First try

* Fixes

* Fix sstore

* Comments

* Clippy

* Fix aggregator.rs

* PR feedback
This commit is contained in:
wborgeaud 2023-05-11 14:56:10 +02:00 committed by GitHub
parent 202985b24f
commit 74ba303255
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 436 additions and 2 deletions

View File

@ -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"),

View File

@ -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

View 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

View 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

View File

@ -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)

View File

@ -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)

View 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(())
}

View File

@ -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;