Merge pull request #787 from mir-protocol/mpt_storage

MPT storage logic
This commit is contained in:
Daniel Lubarov 2022-10-17 22:46:58 -07:00 committed by GitHub
commit 076fe521b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 156 additions and 21 deletions

View File

@ -39,6 +39,7 @@ pub(crate) fn combined_kernel() -> Kernel {
include_str!("asm/memory/metadata.asm"),
include_str!("asm/memory/packing.asm"),
include_str!("asm/memory/txn_fields.asm"),
include_str!("asm/mpt/accounts.asm"),
include_str!("asm/mpt/delete/delete.asm"),
include_str!("asm/mpt/hash/hash.asm"),
include_str!("asm/mpt/hash/hash_trie_specific.asm"),
@ -50,8 +51,8 @@ pub(crate) fn combined_kernel() -> Kernel {
include_str!("asm/mpt/load/load.asm"),
include_str!("asm/mpt/load/load_trie_specific.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/storage/storage_read.asm"),
include_str!("asm/mpt/storage/storage_write.asm"),
include_str!("asm/mpt/util.asm"),
include_str!("asm/ripemd/box.asm"),
include_str!("asm/ripemd/compression.asm"),
@ -77,6 +78,7 @@ pub(crate) fn combined_kernel() -> Kernel {
include_str!("asm/transactions/type_2.asm"),
include_str!("asm/util/assertions.asm"),
include_str!("asm/util/basic_macros.asm"),
include_str!("asm/util/keccak.asm"),
];
let parsed_files = files.iter().map(|f| parse(f)).collect_vec();

View File

@ -0,0 +1,53 @@
// Return a pointer to the current account's data in the state trie.
%macro current_account_data
ADDRESS %mpt_read_state_trie
// stack: account_ptr
// account_ptr should be non-null as long as the prover provided the proper
// Merkle data. But a bad prover may not have, and we don't want return a
// null pointer for security reasons.
DUP1 ISZERO %jumpi(panic)
// stack: account_ptr
%endmacro
// Returns a pointer to the root of the storage trie associated with the current account.
%macro current_storage_trie
// stack: (empty)
%current_account_data
// stack: account_ptr
%add_const(2)
// stack: storage_root_ptr_ptr
%mload_trie_data
// stack: storage_root_ptr
%endmacro
global make_default_account:
PANIC // TODO
// Create a copy of the given account. The copy can then safely be mutated as
// needed, while leaving the original account data untouched.
//
// This writes the new account's data to MPT data, but does not register the new
// account in the state trie.
//
// Pre stack: old_account_ptr, retdest
// Post stack: new_account_ptr
global make_account_copy:
// stack: old_account_ptr, retdest
%get_trie_data_size // pointer to new account we're about to create
// stack: new_account_ptr, old_account_ptr, retdest
DUP2 %mload_trie_data %append_to_trie_data
DUP2 %add_const(1) %mload_trie_data %append_to_trie_data
DUP2 %add_const(3) %mload_trie_data %append_to_trie_data
SWAP1 %add_const(4) %mload_trie_data %append_to_trie_data
// stack: new_account_ptr, retdest
SWAP1
JUMP
// Convenience macro to call make_account_copy and return where we left off.
%macro make_account_copy
%stack (old_account_ptr) -> (old_account_ptr, %%after)
%jump(make_account_copy)
%%after:
%endmacro

View File

@ -95,4 +95,4 @@ encode_receipt:
PANIC // TODO
encode_storage_value:
PANIC // TODO
PANIC // TODO: RLP encode as variable-len scalar?

View File

@ -3,26 +3,26 @@
// state trie. Returns null if the address is not found.
global mpt_read_state_trie:
// stack: addr, retdest
// The key is the hash of the address. Since KECCAK_GENERAL takes input from
// memory, we will write addr bytes to SEGMENT_KERNEL_GENERAL[0..20] first.
%stack (addr) -> (0, @SEGMENT_KERNEL_GENERAL, 0, addr, 20, mpt_read_state_trie_after_mstore)
%jump(mstore_unpacking)
mpt_read_state_trie_after_mstore:
// stack: retdest
%stack () -> (0, @SEGMENT_KERNEL_GENERAL, 0, 20) // context, segment, offset, len
KECCAK_GENERAL
%addr_to_state_key
// stack: key, retdest
PUSH 64 // num_nibbles
%mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) // node_ptr
// stack: node_ptr, num_nibbles, key, retdest
%jump(mpt_read)
// Convenience macro to call mpt_read_state_trie and return where we left off.
%macro mpt_read_state_trie
%stack (addr) -> (addr, %%after)
%jump(mpt_read_state_trie)
%%after:
%endmacro
// Read a value from a MPT.
//
// Arguments:
// - the virtual address of the trie to search in
// - the key, as a U256
// - the number of nibbles in the key (should start at 64)
// - the key, as a U256
//
// This function returns a pointer to the value, or 0 if the key is not found.
global mpt_read:

View File

@ -0,0 +1,28 @@
// Read a word from the current account's storage trie.
//
// Pre stack: slot, retdest
// Post stack: value
global storage_read:
// stack: slot, retdest
%stack (slot) -> (slot, after_storage_read)
%slot_to_storage_key
// stack: storage_key, after_storage_read, retdest
PUSH 64 // storage_key has 64 nibbles
%current_storage_trie
// stack: storage_root_ptr, 64, storage_key, after_storage_read, retdest
%jump(mpt_read)
after_storage_read:
// stack: value_ptr, retdest
DUP1 %jumpi(storage_key_exists)
// Storage key not found; return default value of 0.
%stack (value_ptr, retdest) -> (retdest, 0)
JUMP
storage_key_exists:
// stack: value_ptr, retdest
%mload_trie_data // TODO: If we end up not using value pointers in storage tries, remove this.
SWAP1
JUMP

View File

@ -0,0 +1,33 @@
// Write a word to the current account's storage trie.
//
// Pre stack: slot, value, retdest
// Post stack: (empty)
global storage_write:
// stack: slot, value, retdest
// TODO: If value = 0, delete the key instead of inserting 0?
// TODO: Do we need to write value to MPT data and insert value_ptr? Currently some logic assumes all values are pointers, but could be relaxed so a value is any single word.
%stack (slot, value) -> (slot, value, after_storage_insert)
%slot_to_storage_key
// stack: storage_key, value, after_storage_write, retdest
PUSH 64 // storage_key has 64 nibbles
%current_storage_trie
// stack: storage_root_ptr, 64, storage_key, value, after_storage_insert, retdest
%jump(mpt_insert)
after_storage_insert:
// stack: new_storage_root_ptr, retdest
%current_account_data
// stack: old_account_ptr, new_storage_root_ptr, retdest
%make_account_copy
// stack: new_account_ptr, new_storage_root_ptr, retdest
// Update the copied account with our new storage root pointer.
%stack (new_account_ptr, new_storage_root_ptr) -> (new_account_ptr, new_storage_root_ptr, new_account_ptr)
%add_const(2)
// stack: new_account_storage_root_ptr_ptr, new_storage_root_ptr, new_account_ptr, retdest
%mstore_trie_data
// stack: new_account_ptr, retdest
SWAP1
JUMP

View File

@ -1,2 +0,0 @@
global storage_read:
// TODO

View File

@ -1,2 +0,0 @@
global storage_write:
// TODO

View File

@ -165,3 +165,14 @@
SWAP4 %div_const(4) SWAP4 // bits_2 -> len_2 (in nibbles)
// stack: len_common, key_common, len_1, key_1, len_2, key_2
%endmacro
// Computes state_key = Keccak256(addr). Clobbers @SEGMENT_KERNEL_GENERAL.
%macro addr_to_state_key
%keccak256_word(20)
%endmacro
// Given a storage slot (a 256-bit integer), computes storage_key = Keccak256(slot).
// Clobbers @SEGMENT_KERNEL_GENERAL.
%macro slot_to_storage_key
%keccak256_word(32)
%endmacro

View File

@ -0,0 +1,14 @@
// Computes Keccak256(input_word). Clobbers @SEGMENT_KERNEL_GENERAL.
//
// Pre stack: input_word
// Post stack: hash
%macro keccak256_word(num_bytes)
// Since KECCAK_GENERAL takes its input from memory, we will first write
// input_word's bytes to @SEGMENT_KERNEL_GENERAL[0..$num_bytes].
%stack (word) -> (0, @SEGMENT_KERNEL_GENERAL, 0, word, $num_bytes, %%after_mstore)
%jump(mstore_unpacking)
%%after_mstore:
// stack: (empty)
%stack () -> (0, @SEGMENT_KERNEL_GENERAL, 0, $num_bytes) // context, segment, offset, len
KECCAK_GENERAL
%endmacro

View File

@ -68,11 +68,10 @@ pub(crate) fn mpt_prover_inputs<F>(
PartialTrie::Hash(h) => prover_inputs.push(U256::from_big_endian(h.as_bytes())),
PartialTrie::Branch { children, value } => {
if value.is_empty() {
// There's no value, so value_len = 0.
prover_inputs.push(U256::zero());
prover_inputs.push(U256::zero()); // value_present = 0
} else {
let parsed_value = parse_value(value);
prover_inputs.push(parsed_value.len().into());
prover_inputs.push(U256::one()); // value_present = 1
prover_inputs.extend(parsed_value);
}
for child in children {
@ -107,8 +106,7 @@ pub(crate) fn mpt_prover_inputs_state_trie(
PartialTrie::Hash(h) => prover_inputs.push(U256::from_big_endian(h.as_bytes())),
PartialTrie::Branch { children, value } => {
assert!(value.is_empty(), "State trie should not have branch values");
// There's no value, so value_len = 0.
prover_inputs.push(U256::zero());
prover_inputs.push(U256::zero()); // value_present = 0
for (i, child) in children.iter().enumerate() {
let extended_key = key.merge(&Nibbles {