Merge pull request #783 from mir-protocol/mpt_tweaks

MPT format tweaks
This commit is contained in:
Daniel Lubarov 2022-10-14 19:57:51 -07:00 committed by GitHub
commit 2aeb8c92c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 32 additions and 51 deletions

View File

@ -6,21 +6,21 @@
Withour our zkEVM's kernel memory, Withour our zkEVM's kernel memory,
\begin{enumerate} \begin{enumerate}
\item An empty node is encoded as $(\texttt{MPT\_NODE\_EMPTY})$. \item An empty node is encoded as $(\texttt{MPT\_NODE\_EMPTY})$.
\item A branch node is encoded as $(\texttt{MPT\_NODE\_BRANCH}, c_1, \dots, c_{16}, \abs{v}, v)$, where each $c_i$ is a pointer to a child node, and $v$ is a value of length $\abs{v}$.\footnote{If a branch node has no associated value, then $\abs{v} = 0$ and $v = ()$.} \item A branch node is encoded as $(\texttt{MPT\_NODE\_BRANCH}, c_1, \dots, c_{16}, v)$, where each $c_i$ is a pointer to a child node, and $v$ is a pointer to a value. If a branch node has no associated value, then $v = 0$, i.e. the null pointer.
\item An extension node is encoded as $(\texttt{MPT\_NODE\_EXTENSION}, k, c)$, $k$ represents the part of the key associated with this extension, and is encoded as a 2-tuple $(\texttt{packed\_nibbles}, \texttt{num\_nibbles})$. $c$ is a pointer to a child node. \item An extension node is encoded as $(\texttt{MPT\_NODE\_EXTENSION}, k, c)$, $k$ represents the part of the key associated with this extension, and is encoded as a 2-tuple $(\texttt{packed\_nibbles}, \texttt{num\_nibbles})$. $c$ is a pointer to a child node.
\item A leaf node is encoded as $(\texttt{MPT\_NODE\_LEAF}, k, \abs{v}, v)$, where $k$ is a 2-tuple as above, and $v$ is a leaf payload. \item A leaf node is encoded as $(\texttt{MPT\_NODE\_LEAF}, k, v)$, where $k$ is a 2-tuple as above, and $v$ is a pointer to a value.
\item A digest node is encoded as $(\texttt{MPT\_NODE\_DIGEST}, d)$, where $d$ is a Keccak256 digest. \item A digest node is encoded as $(\texttt{MPT\_NODE\_HASH}, d)$, where $d$ is a Keccak256 digest.
\end{enumerate} \end{enumerate}
\subsection{Prover input format} \subsection{Prover input format}
The initial state of each trie is given by the prover as a nondeterministic input tape. This tape has a similar format: The initial state of each trie is given by the prover as a nondeterministic input tape. This tape has a slightly different format:
\begin{enumerate} \begin{enumerate}
\item An empty node is encoded as $(\texttt{MPT\_NODE\_EMPTY})$. \item An empty node is encoded as $(\texttt{MPT\_NODE\_EMPTY})$.
\item A branch node is encoded as $(\texttt{MPT\_NODE\_BRANCH}, \abs{v}, v, c_1, \dots, c_{16})$, where $\abs{v}$ is the length of the value, and $v$ is the value itself. Each $c_i$ is the encoding of a child node. \item A branch node is encoded as $(\texttt{MPT\_NODE\_BRANCH}, v_?, c_1, \dots, c_{16})$. Here $v_?$ consists of a flag indicating whether a value is present,\todo{In the current implementation, we use a length prefix rather than a is-present prefix, but we plan to change that.} followed by the actual value payload if one is present. Each $c_i$ is the encoding of a child node.
\item An extension node is encoded as $(\texttt{MPT\_NODE\_EXTENSION}, k, c)$, $k$ represents the part of the key associated with this extension, and is encoded as a 2-tuple $(\texttt{packed\_nibbles}, \texttt{num\_nibbles})$. $c$ is a pointer to a child node. \item An extension node is encoded as $(\texttt{MPT\_NODE\_EXTENSION}, k, c)$, $k$ represents the part of the key associated with this extension, and is encoded as a 2-tuple $(\texttt{packed\_nibbles}, \texttt{num\_nibbles})$. $c$ is a pointer to a child node.
\item A leaf node is encoded as $(\texttt{MPT\_NODE\_LEAF}, k, \abs{v}, v)$, where $k$ is a 2-tuple as above, and $v$ is a leaf payload. \item A leaf node is encoded as $(\texttt{MPT\_NODE\_LEAF}, k, v)$, where $k$ is a 2-tuple as above, and $v$ is a value payload.
\item A digest node is encoded as $(\texttt{MPT\_NODE\_DIGEST}, d)$, where $d$ is a Keccak256 digest. \item A digest node is encoded as $(\texttt{MPT\_NODE\_HASH}, d)$, where $d$ is a Keccak256 digest.
\end{enumerate} \end{enumerate}
Nodes are thus given in depth-first order, leading to natural recursive methods for encoding and decoding this format. Nodes are thus given in depth-first order, enabling natural recursive methods for encoding and decoding this format.

Binary file not shown.

View File

@ -51,7 +51,7 @@
\input{introduction} \input{introduction}
\input{framework} \input{framework}
\input{tables} \input{tables}
\input{tries} \input{mpts}
\input{instructions} \input{instructions}
\bibliography{bibliography}{} \bibliography{bibliography}{}

View File

@ -152,21 +152,17 @@ encode_node_branch:
%add_const(16) %add_const(16)
// stack: value_ptr_ptr, rlp_pos', encode_value, retdest // stack: value_ptr_ptr, rlp_pos', encode_value, retdest
%mload_trie_data %mload_trie_data
// stack: value_len_ptr, rlp_pos', encode_value, retdest // stack: value_ptr, rlp_pos', encode_value, retdest
DUP1 %mload_trie_data DUP1 %jumpi(encode_node_branch_with_value)
// stack: value_len, value_len_ptr, rlp_pos', encode_value, retdest
%jumpi(encode_node_branch_with_value)
// No value; append the empty string (0x80). // No value; append the empty string (0x80).
// stack: value_len_ptr, rlp_pos', encode_value, retdest // stack: value_ptr, rlp_pos', encode_value, retdest
%stack (value_len_ptr, rlp_pos, encode_value) -> (rlp_pos, 0x80, rlp_pos) %stack (value_ptr, rlp_pos, encode_value) -> (rlp_pos, 0x80, rlp_pos)
%mstore_rlp %mstore_rlp
// stack: rlp_pos', retdest // stack: rlp_pos', retdest
%increment %increment
// stack: rlp_pos'', retdest // stack: rlp_pos'', retdest
%jump(encode_node_branch_prepend_prefix) %jump(encode_node_branch_prepend_prefix)
encode_node_branch_with_value: encode_node_branch_with_value:
// stack: value_len_ptr, rlp_pos', encode_value, retdest
%increment
// stack: value_ptr, rlp_pos', encode_value, retdest // stack: value_ptr, rlp_pos', encode_value, retdest
%stack (value_ptr, rlp_pos, encode_value) %stack (value_ptr, rlp_pos, encode_value)
-> (encode_value, rlp_pos, value_ptr, encode_node_branch_prepend_prefix) -> (encode_value, rlp_pos, value_ptr, encode_node_branch_prepend_prefix)
@ -276,7 +272,6 @@ encode_node_leaf_after_hex_prefix:
%add_const(2) // The value pointer starts at index 3, after num_nibbles and packed_nibbles. %add_const(2) // The value pointer starts at index 3, after num_nibbles and packed_nibbles.
// stack: value_ptr_ptr, rlp_pos, encode_value, retdest // stack: value_ptr_ptr, rlp_pos, encode_value, retdest
%mload_trie_data %mload_trie_data
%increment // skip over length prefix
// stack: value_ptr, rlp_pos, encode_value, retdest // stack: value_ptr, rlp_pos, encode_value, retdest
%stack (value_ptr, rlp_pos, encode_value, retdest) %stack (value_ptr, rlp_pos, encode_value, retdest)
-> (encode_value, rlp_pos, value_ptr, encode_node_leaf_after_encode_value, retdest) -> (encode_value, rlp_pos, value_ptr, encode_node_leaf_after_encode_value, retdest)

View File

@ -70,7 +70,7 @@ load_mpt_branch:
SWAP1 %append_to_trie_data SWAP1 %append_to_trie_data
// stack: node_ptr, retdest // stack: node_ptr, retdest
// Save the offset of our 16 child pointers so we can write them later. // 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 // Then advance our current trie pointer beyond them, so we can load the
// value and have it placed after our child pointers. // value and have it placed after our child pointers.
%get_trie_data_size %get_trie_data_size
// stack: children_ptr, node_ptr, retdest // stack: children_ptr, node_ptr, retdest
@ -79,8 +79,8 @@ load_mpt_branch:
%set_trie_data_size %set_trie_data_size
// stack: children_ptr, node_ptr, retdest // stack: children_ptr, node_ptr, retdest
%load_value %load_value
// stack: children_ptr, value_ptr, node_ptr, retdest
SWAP1 SWAP1
// stack: children_ptr, value_ptr, node_ptr, retdest
// Load the 16 children. // Load the 16 children.
%rep 16 %rep 16
@ -170,26 +170,28 @@ load_mpt_digest:
%%after: %%after:
%endmacro %endmacro
// Load a leaf from prover input, append it to trie data, and return a pointer to it. // Load a value from prover input, append it to trie data, and return a pointer to it.
// Return null if the value is empty.
%macro load_value %macro load_value
// stack: (empty) // stack: (empty)
PROVER_INPUT(mpt) PROVER_INPUT(mpt)
// stack: value_len // stack: value_len
DUP1 ISZERO DUP1 %jumpi(%%has_value)
%jumpi(%%return_null) %stack (value_len) -> (0)
%jump(%%end)
%%has_value:
// stack: value_len // stack: value_len
%get_trie_data_size %get_trie_data_size
// stack: value_ptr, value_len
SWAP1 SWAP1
// stack: value_len, value_ptr // stack: value_len, value_ptr
DUP1 %append_to_trie_data
// stack: value_len, value_ptr
%%loop: %%loop:
DUP1 ISZERO DUP1 ISZERO
// stack: value_len == 0, value_len, value_ptr // stack: value_len == 0, value_len, value_ptr
%jumpi(%%finish_loop) %jumpi(%%finish_loop)
// stack: value_len, value_ptr // stack: value_len, value_ptr
PROVER_INPUT(mpt) PROVER_INPUT(mpt)
// stack: leaf_part, value_len, value_ptr // stack: value_part, value_len, value_ptr
%append_to_trie_data %append_to_trie_data
// stack: value_len, value_ptr // stack: value_len, value_ptr
%decrement %decrement
@ -199,8 +201,5 @@ load_mpt_digest:
// stack: value_len, value_ptr // stack: value_len, value_ptr
POP POP
// stack: value_ptr // stack: value_ptr
%jump(%%end)
%%return_null:
%stack (value_len) -> (0)
%%end: %%end:
%endmacro %endmacro

View File

@ -1,6 +1,6 @@
// Given an address, return a pointer to the associated (length-prefixed) // Given an address, return a pointer to the associated account data, which
// account data, which consists of four words (nonce, balance, storage_root, // consists of four words (nonce, balance, storage_root, code_hash), in the
// code_hash), in the state trie. Returns 0 if the address is not found. // state trie. Returns null if the address is not found.
global mpt_read_state_trie: global mpt_read_state_trie:
// stack: addr, retdest // stack: addr, retdest
// The key is the hash of the address. Since KECCAK_GENERAL takes input from // The key is the hash of the address. Since KECCAK_GENERAL takes input from
@ -24,7 +24,7 @@ mpt_read_state_trie_after_mstore:
// - the key, as a U256 // - the key, as a U256
// - the number of nibbles in the key (should start at 64) // - the number of nibbles in the key (should start at 64)
// //
// This function returns a pointer to the length-prefixed leaf, or 0 if the key is not found. // This function returns a pointer to the value, or 0 if the key is not found.
global mpt_read: global mpt_read:
// stack: node_ptr, num_nibbles, key, retdest // stack: node_ptr, num_nibbles, key, retdest
DUP1 DUP1
@ -77,15 +77,6 @@ mpt_read_branch_end_of_key:
%add_const(16) // skip over the 16 child nodes %add_const(16) // skip over the 16 child nodes
// stack: value_ptr_ptr, retdest // stack: value_ptr_ptr, retdest
%mload_trie_data %mload_trie_data
// stack: value_len_ptr, retdest
DUP1 %mload_trie_data
// stack: value_len, value_len_ptr, retdest
%jumpi(mpt_read_branch_found_value)
// This branch node contains no value, so return null.
%stack (value_len_ptr, retdest) -> (retdest, 0)
mpt_read_branch_found_value:
// stack: value_len_ptr, retdest
%increment
// stack: value_ptr, retdest // stack: value_ptr, retdest
SWAP1 SWAP1
JUMP JUMP

View File

@ -157,7 +157,6 @@ fn test_state_trie(state_trie: PartialTrie, k: Nibbles, v: Vec<u8>) -> Result<()
let value_ptr = trie_data.len(); let value_ptr = trie_data.len();
let account: AccountRlp = rlp::decode(&v).expect("Decoding failed"); let account: AccountRlp = rlp::decode(&v).expect("Decoding failed");
let account_data = account.to_vec(); let account_data = account.to_vec();
trie_data.push(account_data.len().into());
trie_data.extend(account_data); trie_data.extend(account_data);
let trie_data_len = trie_data.len().into(); let trie_data_len = trie_data.len().into();
interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, trie_data_len); interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, trie_data_len);

View File

@ -79,7 +79,6 @@ fn load_all_mpts_leaf() -> Result<()> {
3.into(), 3.into(),
0xABC.into(), 0xABC.into(),
5.into(), // value ptr 5.into(), // value ptr
4.into(), // value length
test_account_1().nonce, test_account_1().nonce,
test_account_1().balance, test_account_1().balance,
test_account_1().storage_root.into_uint(), test_account_1().storage_root.into_uint(),
@ -200,7 +199,6 @@ fn load_all_mpts_ext_to_leaf() -> Result<()> {
3.into(), // 3 nibbles 3.into(), // 3 nibbles
0xDEF.into(), // key part 0xDEF.into(), // key part
9.into(), // value pointer 9.into(), // value pointer
4.into(), // value length
test_account_1().nonce, test_account_1().nonce,
test_account_1().balance, test_account_1().balance,
test_account_1().storage_root.into_uint(), test_account_1().storage_root.into_uint(),

View File

@ -44,12 +44,11 @@ fn mpt_read() -> Result<()> {
assert_eq!(interpreter.stack().len(), 1); assert_eq!(interpreter.stack().len(), 1);
let result_ptr = interpreter.stack()[0].as_usize(); let result_ptr = interpreter.stack()[0].as_usize();
let result = &interpreter.get_trie_data()[result_ptr..][..5]; let result = &interpreter.get_trie_data()[result_ptr..][..4];
assert_eq!(result[0], 4.into()); assert_eq!(result[0], account.nonce);
assert_eq!(result[1], account.nonce); assert_eq!(result[1], account.balance);
assert_eq!(result[2], account.balance); assert_eq!(result[2], account.storage_root.into_uint());
assert_eq!(result[3], account.storage_root.into_uint()); assert_eq!(result[3], account.code_hash.into_uint());
assert_eq!(result[4], account.code_hash.into_uint());
Ok(()) Ok(())
} }