Merge pull request #768 from mir-protocol/mpt_insert_1

MPT insert logic, part 1
This commit is contained in:
Daniel Lubarov 2022-10-08 22:35:23 -07:00 committed by GitHub
commit fb7ef3197a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 620 additions and 149 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<U256> {
&mut self.memory.context_memory[0].segments[Segment::TrieData as usize].content
}
pub(crate) fn get_rlp_memory(&self) -> Vec<u8> {
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.")
}

View File

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

View File

@ -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(),
]
);

View File

@ -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<u8> {
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<u8> {
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<u8>) -> PartialTrie {
PartialTrie::Extension {

View File

@ -13,6 +13,17 @@ pub(crate) struct AccountRlp {
pub(crate) code_hash: H256,
}
impl AccountRlp {
pub(crate) fn to_vec(&self) -> Vec<U256> {
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<U256> {
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<U256> {
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| {