Merge branch 'main' of github.com:mir-protocol/plonky2 into ripeMD

This commit is contained in:
Dmitry Vagner 2022-10-03 12:09:10 -07:00
commit 0dc1a40306
31 changed files with 947 additions and 137 deletions

View File

@ -17,7 +17,6 @@ use crate::cpu::kernel::aggregator::KERNEL;
use crate::cpu::kernel::keccak_util::keccakf_u32s;
use crate::generation::state::GenerationState;
use crate::memory::segments::Segment;
use crate::memory::NUM_CHANNELS;
use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars};
/// The Keccak rate (1088 bits), measured in bytes.
@ -47,8 +46,7 @@ pub(crate) fn generate_bootstrap_kernel<F: Field>(state: &mut GenerationState<F>
// Write this chunk to memory, while simultaneously packing its bytes into a u32 word.
let mut packed_bytes: u32 = 0;
for (addr, byte) in chunk {
let channel = addr % NUM_CHANNELS;
for (channel, (addr, byte)) in chunk.enumerate() {
state.set_mem_cpu_current(channel, Segment::Code, addr, byte.into());
packed_bytes = (packed_bytes << 8) | byte as u32;

View File

@ -34,6 +34,7 @@ pub struct OpsColumnsView<T> {
pub shr: T,
pub sar: T,
pub keccak256: T,
pub keccak_general: T,
pub address: T,
pub balance: T,
pub origin: T,

View File

@ -22,7 +22,7 @@ use crate::cpu::columns::{CpuColumnsView, COL_MAP};
/// behavior.
/// Note: invalid opcodes are not represented here. _Any_ opcode is permitted to decode to
/// `is_invalid`. The kernel then verifies that the opcode was _actually_ invalid.
const OPCODES: [(u8, usize, bool, usize); 92] = [
const OPCODES: [(u8, usize, bool, usize); 93] = [
// (start index of block, number of top bits to check (log2), kernel-only, flag column)
(0x00, 0, false, COL_MAP.op.stop),
(0x01, 0, false, COL_MAP.op.add),
@ -51,6 +51,7 @@ const OPCODES: [(u8, usize, bool, usize); 92] = [
(0x1c, 0, false, COL_MAP.op.shr),
(0x1d, 0, false, COL_MAP.op.sar),
(0x20, 0, false, COL_MAP.op.keccak256),
(0x21, 0, true, COL_MAP.op.keccak_general),
(0x30, 0, false, COL_MAP.op.address),
(0x31, 0, false, COL_MAP.op.balance),
(0x32, 0, false, COL_MAP.op.origin),

View File

@ -49,6 +49,8 @@ pub(crate) fn combined_kernel() -> Kernel {
include_str!("asm/rlp/decode.asm"),
include_str!("asm/rlp/read_to_memory.asm"),
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/load.asm"),
include_str!("asm/mpt/read.asm"),
include_str!("asm/mpt/storage_read.asm"),

View File

@ -1,11 +1,15 @@
// Transfers some ETH from one address to another. The amount is given in wei.
// Pre stack: from, to, amount, retdest
// Post stack: (empty)
global transfer_eth:
// stack: from, to, amount, retdest
// TODO: Replace with actual implementation.
%pop3
%stack (from, to, amount, retdest)
-> (from, amount, to, amount)
%deduct_eth
// TODO: Handle exception from %deduct_eth?
// stack: to, amount, retdest
%add_eth
// stack: retdest
JUMP
// Convenience macro to call transfer_eth and return where we left off.
@ -26,3 +30,26 @@ global transfer_eth:
%transfer_eth
%%after:
%endmacro
global deduct_eth:
// stack: addr, amount, retdest
%jump(mpt_read_state_trie)
deduct_eth_after_read:
PANIC // TODO
// Convenience macro to call deduct_eth and return where we left off.
%macro deduct_eth
%stack (addr, amount) -> (addr, amount, %%after)
%jump(deduct_eth)
%%after:
%endmacro
global add_eth:
PANIC // TODO
// Convenience macro to call add_eth and return where we left off.
%macro add_eth
%stack (addr, amount) -> (addr, amount, %%after)
%jump(add_eth)
%%after:
%endmacro

View File

@ -1,8 +1,24 @@
global main:
// First, load all MPT data from the prover.
PUSH txn_loop
%jump(load_all_mpts)
hash_initial_tries:
%mpt_hash_state_trie %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_BEFORE)
%mpt_hash_txn_trie %mstore_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_BEFORE)
%mpt_hash_receipt_trie %mstore_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_BEFORE)
txn_loop:
// If the prover has no more txns for us to process, halt.
PROVER_INPUT(end_of_txns)
%jumpi(halt)
%jumpi(hash_final_tries)
// Call route_txn, returning to main to continue the loop.
PUSH main
// Call route_txn. When we return, continue the txn loop.
PUSH txn_loop
%jump(route_txn)
hash_final_tries:
%mpt_hash_state_trie %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER)
%mpt_hash_txn_trie %mstore_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER)
%mpt_hash_receipt_trie %mstore_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_AFTER)
%jump(halt)

View File

@ -118,3 +118,10 @@
%mstore_kernel(@SEGMENT_CODE)
// stack: (empty)
%endmacro
// Store a single byte to @SEGMENT_RLP_RAW.
%macro mstore_rlp
// stack: offset, value
%mstore_kernel(@SEGMENT_RLP_RAW)
// stack: (empty)
%endmacro

View File

@ -7,7 +7,7 @@ global mload_packing:
// stack: value
// Pre stack: context, segment, offset, value, len, retdest
// Post stack: (empty)
// Post stack: offset'
global mstore_unpacking:
// stack: context, segment, offset, value, len, retdest
// We will enumerate i in (32 - len)..32.
@ -42,6 +42,6 @@ mstore_unpacking_loop:
mstore_unpacking_finish:
// stack: i, context, segment, offset, value, len, retdest
%pop6
// stack: retdest
%pop3
%stack (offset, value, len, retdest) -> (retdest, offset)
JUMP

View File

@ -1,2 +1,82 @@
global mpt_hash:
// TODO
// Computes the Merkle root of the given trie node.
//
// The encode_value function should take as input
// - the position withing @SEGMENT_RLP_RAW to write to,
// - the offset of a value within @SEGMENT_TRIE_DATA, and
// - a return address.
// It should serialize the value, write it to @SEGMENT_RLP_RAW starting at the
// given position, and return an updated position (the next unused offset).
%macro mpt_hash(encode_value)
// stack: node_ptr, retdest
DUP1
%mload_trie_data
// stack: node_type, node_ptr, retdest
// Increment node_ptr, so it points to the node payload instead of its type.
SWAP1 %add_const(1) SWAP1
// stack: node_type, node_payload_ptr, retdest
DUP1 %eq_const(@MPT_NODE_EMPTY) %jumpi(mpt_hash_empty)
DUP1 %eq_const(@MPT_NODE_HASH) %jumpi(mpt_hash_hash)
DUP1 %eq_const(@MPT_NODE_BRANCH) %jumpi(%%mpt_hash_branch)
DUP1 %eq_const(@MPT_NODE_EXTENSION) %jumpi(%%mpt_hash_extension)
DUP1 %eq_const(@MPT_NODE_LEAF) %jumpi(%%mpt_hash_leaf)
PANIC // Invalid node type? Shouldn't get here.
%%mpt_hash_branch:
// stack: node_type, node_payload_ptr, retdest
POP
// stack: node_payload_ptr, retdest
PANIC // TODO
%%mpt_hash_extension:
// stack: node_type, node_payload_ptr, retdest
POP
// stack: node_payload_ptr, retdest
PANIC // TODO
%%mpt_hash_leaf:
// stack: node_type, node_payload_ptr, retdest
POP
// stack: node_payload_ptr, retdest
PUSH %%mpt_hash_leaf_after_hex_prefix // retdest
PUSH 1 // terminated
// stack: terminated, %%mpt_hash_leaf_after_hex_prefix, node_payload_ptr, retdest
DUP3 %add_const(1) %mload_trie_data // Load the packed_nibbles field, which is at index 1.
// stack: packed_nibbles, terminated, %%mpt_hash_leaf_after_hex_prefix, node_payload_ptr, retdest
DUP4 %mload_trie_data // Load the num_nibbles field, which is at index 0.
// stack: num_nibbles, packed_nibbles, terminated, %%mpt_hash_leaf_after_hex_prefix, node_payload_ptr, retdest
PUSH 9 // We start at 9 to leave room to prepend the largest possible RLP list header.
// stack: rlp_start, num_nibbles, packed_nibbles, terminated, %%mpt_hash_leaf_after_hex_prefix, node_payload_ptr, retdest
%jump(hex_prefix_rlp)
%%mpt_hash_leaf_after_hex_prefix:
// stack: rlp_pos, node_payload_ptr, retdest
SWAP1
%add_const(2) // The value starts at index 2.
%stack (value_ptr, rlp_pos, retdest)
-> (rlp_pos, value_ptr, %%mpt_hash_leaf_after_encode_value, retdest)
%jump($encode_value)
%%mpt_hash_leaf_after_encode_value:
// stack: rlp_end_pos, retdest
%prepend_rlp_list_prefix
// stack: rlp_start_pos, rlp_len, retdest
PUSH @SEGMENT_RLP_RAW
PUSH 0 // kernel context
// stack: rlp_start_addr: 3, rlp_len, retdest
KECCAK_GENERAL
// stack: hash, retdest
SWAP1
JUMP
%endmacro
global mpt_hash_empty:
%stack (node_type, node_payload_ptr, retdest) -> (retdest, @EMPTY_NODE_HASH)
JUMP
global mpt_hash_hash:
// stack: node_type, node_payload_ptr, retdest
POP
// stack: node_payload_ptr, retdest
%mload_trie_data
// stack: hash, retdest
SWAP1
JUMP

View File

@ -0,0 +1,80 @@
// Hashing logic specific to a particular trie.
global mpt_hash_state_trie:
// stack: retdest
%mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT)
// stack: node_ptr, retdest
%mpt_hash(encode_account)
%macro mpt_hash_state_trie
PUSH %%after
%jump(mpt_hash_state_trie)
%%after:
%endmacro
global mpt_hash_txn_trie:
// stack: retdest
%mload_global_metadata(@GLOBAL_METADATA_TXN_TRIE_ROOT)
// stack: node_ptr, retdest
%mpt_hash(encode_txn)
%macro mpt_hash_txn_trie
PUSH %%after
%jump(mpt_hash_txn_trie)
%%after:
%endmacro
global mpt_hash_receipt_trie:
// stack: retdest
%mload_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_ROOT)
// stack: node_ptr, retdest
%mpt_hash(encode_receipt)
%macro mpt_hash_receipt_trie
PUSH %%after
%jump(mpt_hash_receipt_trie)
%%after:
%endmacro
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
// to determine their contribution, while the other two fields are fixed
// 32-bytes integers.
DUP2 %mload_trie_data // nonce = value[0]
%scalar_rlp_len
// stack: nonce_rlp_len, rlp_pos, value_ptr, retdest
DUP3 %add_const(1) %mload_trie_data // balance = value[1]
%scalar_rlp_len
// stack: balance_rlp_lenm, nonce_rlp_len, rlp_pos, value_ptr, retdest
PUSH 66 // storage_root and code_hash fields each take 1 + 32 bytes
ADD ADD
// stack: payload_len, rlp_pos, value_ptr, retdest
SWAP1
%encode_rlp_list_prefix
// stack: rlp_pos', value_ptr, retdest
DUP2 %mload_trie_data // nonce = value[0]
// stack: nonce, rlp_pos', value_ptr, retdest
SWAP1 %encode_rlp_scalar
// stack: rlp_pos'', value_ptr, retdest
DUP2 %add_const(1) %mload_trie_data // balance = value[1]
// stack: balance, rlp_pos'', value_ptr, retdest
SWAP1 %encode_rlp_scalar
// stack: rlp_pos''', value_ptr, retdest
DUP2 %add_const(2) %mload_trie_data // storage_root = value[2]
// stack: storage_root, rlp_pos''', value_ptr, retdest
SWAP1 %encode_rlp_256
// stack: rlp_pos'''', value_ptr, retdest
SWAP1 %add_const(3) %mload_trie_data // code_hash = value[3]
// stack: code_hash, rlp_pos'''', retdest
SWAP1 %encode_rlp_256
// stack: rlp_pos''''', retdest
SWAP1
JUMP
encode_txn:
PANIC // TODO
encode_receipt:
PANIC // TODO

View File

@ -0,0 +1,104 @@
// Computes the RLP encoding of the hex-prefix encoding of the given nibble list
// and termination flag. Writes the result to @SEGMENT_RLP_RAW starting at the
// given position, and returns the updated position, i.e. a pointer to the next
// unused offset.
//
// Pre stack: rlp_start_pos, num_nibbles, packed_nibbles, terminated, retdest
// Post stack: rlp_end_pos
global hex_prefix_rlp:
// stack: rlp_pos, num_nibbles, packed_nibbles, terminated, retdest
// We will iterate backwards, from i = num_nibbles / 2 to i = 0, so that we
// can take nibbles from the least-significant end of packed_nibbles.
PUSH 2 DUP3 DIV // i = num_nibbles / 2
// stack: i, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest
// Compute the length of the hex-prefix string, in bytes:
// hp_len = num_nibbles / 2 + 1 = i + 1
DUP1 %add_const(1)
// stack: hp_len, i, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest
// Write the RLP header.
DUP1 %gt_const(55) %jumpi(rlp_header_large)
DUP1 %gt_const(1) %jumpi(rlp_header_medium)
// The hex-prefix is a single byte. It must be <= 127, since its first
// nibble only has two bits. So this is the "small" RLP string case, where
// the byte is its own RLP encoding.
// stack: hp_len, i, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest
%jump(start_loop)
rlp_header_medium:
// stack: hp_len, i, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest
DUP1 %add_const(0x80) // value = 0x80 + hp_len
DUP4 // offset = rlp_pos
%mstore_rlp
// rlp_pos += 1
SWAP2 %add_const(1) SWAP2
%jump(start_loop)
rlp_header_large:
// stack: hp_len, i, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest
// In practice hex-prefix length will never exceed 256, so the length of the
// length will always be 1 byte in this case.
PUSH 0xb8 // value = 0xb7 + len_of_len = 0xb8
DUP4 // offset = rlp_pos
%mstore_rlp
DUP1 // value = hp_len
DUP4 %add_const(1) // offset = rlp_pos + 1
%mstore_rlp
// rlp_pos += 2
SWAP2 %add_const(2) SWAP2
start_loop:
// stack: hp_len, i, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest
SWAP1
loop:
// stack: i, hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest
// If i == 0, break to first_byte.
DUP1 ISZERO %jumpi(first_byte)
// stack: i, hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest
DUP5 // packed_nibbles
%and_const(0xFF)
// stack: byte_i, i, hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest
DUP4 // rlp_pos
DUP3 // i
ADD // We'll write to offset rlp_pos + i
%mstore_rlp
// stack: i, hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest
%sub_const(1)
SWAP4 %shr_const(8) SWAP4 // packed_nibbles >>= 8
%jump(loop)
first_byte:
// stack: 0, hp_len, rlp_pos, num_nibbles, first_nibble_or_zero, terminated, retdest
POP
// stack: hp_len, rlp_pos, num_nibbles, first_nibble_or_zero, terminated, retdest
DUP2 ADD
// stack: rlp_end_pos, rlp_pos, num_nibbles, first_nibble_or_zero, terminated, retdest
SWAP4
// stack: terminated, rlp_pos, num_nibbles, first_nibble_or_zero, rlp_end_pos, retdest
%mul_const(2)
// stack: terminated * 2, rlp_pos, num_nibbles, first_nibble_or_zero, rlp_end_pos, retdest
%stack (terminated_x2, rlp_pos, num_nibbles, first_nibble_or_zero)
-> (num_nibbles, terminated_x2, first_nibble_or_zero, rlp_pos)
// stack: num_nibbles, terminated * 2, first_nibble_or_zero, rlp_pos, rlp_end_pos, retdest
%mod_const(2) // parity
ADD
// stack: parity + terminated * 2, first_nibble_or_zero, rlp_pos, rlp_end_pos, retdest
%mul_const(16)
ADD
// stack: first_byte, rlp_pos, rlp_end_pos, retdest
SWAP1
%mstore_rlp
// stack: rlp_end_pos, retdest
SWAP1
JUMP

View File

@ -9,8 +9,7 @@ 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_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)
@ -39,7 +38,6 @@ storage_trie_loop:
// stack: i, num_storage_tries, retdest
%jump(storage_trie_loop)
storage_trie_loop_end:
// TODO: Hash tries and set @GLOBAL_METADATA_STATE_TRIE_DIGEST_BEFORE, etc.
// stack: i, num_storage_tries, retdest
%pop2
// stack: retdest

View File

@ -1,3 +1,22 @@
// Given an address, return a pointer to the associated account data, which
// consists of four words (nonce, balance, storage_root, code_hash), in the
// state trie. Returns 0 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
// 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)
// Read a value from a MPT.
//
// Arguments:
@ -6,7 +25,6 @@
// - the number of nibbles in the key (should start at 64)
//
// This function returns a pointer to the leaf, or 0 if the key is not found.
global mpt_read:
// stack: node_ptr, num_nibbles, key, retdest
DUP1

View File

@ -1,2 +1,3 @@
global mpt_write:
// stack: node_ptr, num_nibbles, key, retdest
// TODO

View File

@ -78,22 +78,129 @@ encode_rlp_fixed:
SWAP1
%add_const(1) // increment pos
// stack: pos, len, string, retdest
%stack (pos, len, string) -> (@SEGMENT_RLP_RAW, pos, string, len, encode_rlp_fixed_finish, pos, len)
GET_CONTEXT
// stack: context, segment, pos, string, len, encode_rlp_fixed, pos, retdest
%jump(mstore_unpacking)
%stack (pos, len, string) -> (pos, string, len, encode_rlp_fixed_finish)
// stack: context, segment, pos, string, len, encode_rlp_fixed_finish, retdest
%jump(mstore_unpacking_rlp)
encode_rlp_fixed_finish:
// stack: pos, len, retdest
ADD
// stack: pos', retdest
SWAP1
JUMP
// Pre stack: pos, payload_len, retdest
// Post stack: pos'
global encode_rlp_list_prefix:
// stack: pos, payload_len, retdest
DUP2 %gt_const(55)
%jumpi(encode_rlp_list_prefix_large)
// Small case: prefix is just 0xc0 + length.
// stack: pos, payload_len, retdest
SWAP1
%add_const(0xc0)
// stack: prefix, pos, retdest
DUP2
// stack: pos, prefix, pos, retdest
%mstore_rlp
// stack: pos, retdest
%add_const(1)
SWAP1
JUMP
encode_rlp_list_prefix_large:
// Write 0xf7 + len_of_len.
// stack: pos, payload_len, retdest
DUP2 %num_bytes
// stack: len_of_len, pos, payload_len, retdest
DUP1 %add_const(0xf7)
// stack: first_byte, len_of_len, pos, payload_len, retdest
DUP3 // pos
%mstore_rlp
// stack: len_of_len, pos, payload_len, retdest
SWAP1 %add_const(1)
// stack: pos', len_of_len, payload_len, retdest
%stack (pos, len_of_len, payload_len, retdest)
-> (pos, payload_len, len_of_len,
encode_rlp_list_prefix_large_done_writing_len,
retdest)
%jump(mstore_unpacking_rlp)
encode_rlp_list_prefix_large_done_writing_len:
// stack: pos'', retdest
SWAP1
JUMP
%macro encode_rlp_list_prefix
%stack (pos, payload_len) -> (pos, payload_len, %%after)
%jump(encode_rlp_list_prefix)
%%after:
%endmacro
// Given an RLP list payload which starts at position 9 and ends at the given
// position, prepend the appropriate RLP list prefix. Returns the updated start
// position, as well as the length of the RLP data (including the newly-added
// prefix).
//
// (We sometimes start list payloads at position 9 because 9 is the length of
// the longest possible RLP list prefix.)
//
// Pre stack: end_pos, retdest
// Post stack: start_pos, rlp_len
global prepend_rlp_list_prefix:
// stack: end_pos, retdest
// Since the list payload starts at position 9, payload_len = end_pos - 9.
PUSH 9 DUP2 SUB
// stack: payload_len, end_pos, retdest
DUP1 %gt_const(55)
%jumpi(prepend_rlp_list_prefix_big)
// If we got here, we have a small list, so we prepend 0xc0 + len at position 8.
// stack: payload_len, end_pos, retdest
%add_const(0xc0)
// stack: prefix_byte, end_pos, retdest
PUSH 8 // offset
%mstore_rlp
// stack: end_pos, retdest
%sub_const(8)
// stack: rlp_len, retdest
PUSH 8 // start_pos
%stack (start_pos, rlp_len, retdest) -> (retdest, start_pos, rlp_len)
JUMP
prepend_rlp_list_prefix_big:
// We have a large list, so we prepend 0xf7 + len_of_len at position
// 8 - len_of_len, followed by the length itself.
// stack: payload_len, end_pos, retdest
DUP1 %num_bytes
// stack: len_of_len, payload_len, end_pos, retdest
DUP1
PUSH 8
SUB
// stack: start_pos, len_of_len, payload_len, end_pos, retdest
DUP2 %add_const(0xf7) DUP2 %mstore_rlp // rlp[start_pos] = 0xf7 + len_of_len
DUP1 %add_const(1) // start_len_pos = start_pos + 1
%stack (start_len_pos, start_pos, len_of_len, payload_len, end_pos, retdest)
-> (start_len_pos, payload_len, len_of_len,
prepend_rlp_list_prefix_big_done_writing_len,
start_pos, end_pos, retdest)
%jump(mstore_unpacking_rlp)
prepend_rlp_list_prefix_big_done_writing_len:
// stack: 9, start_pos, end_pos, retdest
%stack (_9, start_pos, end_pos) -> (end_pos, start_pos, start_pos)
// stack: end_pos, start_pos, start_pos, retdest
SUB
// stack: rlp_len, start_pos, retdest
%stack (rlp_len, start_pos, retdest) -> (retdest, start_pos, rlp_len)
JUMP
// Convenience macro to call prepend_rlp_list_prefix and return where we left off.
%macro prepend_rlp_list_prefix
%stack (start_pos) -> (start_pos, %%after)
%jump(prepend_rlp_list_prefix)
%%after:
%endmacro
// Get the number of bytes required to represent the given scalar.
// The scalar is assumed to be non-zero, as small scalars like zero should
// have already been handled with the small-scalar encoding.
num_bytes:
// TODO: Should probably unroll the loop
global num_bytes:
// stack: x, retdest
PUSH 0 // i
// stack: i, x, retdest
@ -125,3 +232,31 @@ num_bytes_finish:
%jump(num_bytes)
%%after:
%endmacro
// Given some scalar, compute the number of bytes used in its RLP encoding,
// including any length prefix.
%macro scalar_rlp_len
// stack: scalar
// Since the scalar fits in a word, we can't hit the large (>55 byte)
// case, so we just check for small vs medium.
DUP1 %gt_const(0x7f)
// stack: is_medium, scalar
%jumpi(%%medium)
// Small case; result is 1.
%stack (scalar) -> (1)
%%medium:
// stack: scalar
%num_bytes
// stack: scalar_bytes
%add_const(1) // Account for the length prefix.
// stack: rlp_len
%endmacro
// Like mstore_unpacking, but specifically for the RLP segment.
// Pre stack: offset, value, len, retdest
// Post stack: offset'
mstore_unpacking_rlp:
// stack: offset, value, len, retdest
PUSH @SEGMENT_RLP_RAW
PUSH 0 // context
%jump(mstore_unpacking)

View File

@ -18,6 +18,9 @@ pub fn evm_constants() -> HashMap<String, U256> {
for (name, value) in EC_CONSTANTS {
c.insert(name.into(), U256::from_big_endian(&value));
}
for (name, value) in HASH_CONSTANTS {
c.insert(name.into(), U256::from_big_endian(&value));
}
for (name, value) in GAS_CONSTANTS {
c.insert(name.into(), U256::from(value));
}
@ -43,6 +46,14 @@ pub fn evm_constants() -> HashMap<String, U256> {
c
}
const HASH_CONSTANTS: [(&str, [u8; 32]); 1] = [
// Hash of an empty node: keccak(rlp.encode(b'')).hex()
(
"EMPTY_NODE_HASH",
hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"),
),
];
const EC_CONSTANTS: [(&str, [u8; 32]); 3] = [
(
"BN_BASE",

View File

@ -24,13 +24,13 @@ pub(crate) enum GlobalMetadata {
// The root digests of each Merkle trie before these transactions.
StateTrieRootDigestBefore = 8,
TransactionsTrieRootDigestBefore = 9,
ReceiptsTrieRootDigestBefore = 10,
TransactionTrieRootDigestBefore = 9,
ReceiptTrieRootDigestBefore = 10,
// The root digests of each Merkle trie after these transactions.
StateTrieRootDigestAfter = 11,
TransactionsTrieRootDigestAfter = 12,
ReceiptsTrieRootDigestAfter = 13,
TransactionTrieRootDigestAfter = 12,
ReceiptTrieRootDigestAfter = 13,
}
impl GlobalMetadata {
@ -47,11 +47,11 @@ impl GlobalMetadata {
Self::ReceiptTrieRoot,
Self::NumStorageTries,
Self::StateTrieRootDigestBefore,
Self::TransactionsTrieRootDigestBefore,
Self::ReceiptsTrieRootDigestBefore,
Self::TransactionTrieRootDigestBefore,
Self::ReceiptTrieRootDigestBefore,
Self::StateTrieRootDigestAfter,
Self::TransactionsTrieRootDigestAfter,
Self::ReceiptsTrieRootDigestAfter,
Self::TransactionTrieRootDigestAfter,
Self::ReceiptTrieRootDigestAfter,
]
}
@ -67,18 +67,18 @@ impl GlobalMetadata {
GlobalMetadata::ReceiptTrieRoot => "GLOBAL_METADATA_RECEIPT_TRIE_ROOT",
GlobalMetadata::NumStorageTries => "GLOBAL_METADATA_NUM_STORAGE_TRIES",
GlobalMetadata::StateTrieRootDigestBefore => "GLOBAL_METADATA_STATE_TRIE_DIGEST_BEFORE",
GlobalMetadata::TransactionsTrieRootDigestBefore => {
"GLOBAL_METADATA_TXNS_TRIE_DIGEST_BEFORE"
GlobalMetadata::TransactionTrieRootDigestBefore => {
"GLOBAL_METADATA_TXN_TRIE_DIGEST_BEFORE"
}
GlobalMetadata::ReceiptsTrieRootDigestBefore => {
"GLOBAL_METADATA_RECEIPTS_TRIE_DIGEST_BEFORE"
GlobalMetadata::ReceiptTrieRootDigestBefore => {
"GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_BEFORE"
}
GlobalMetadata::StateTrieRootDigestAfter => "GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER",
GlobalMetadata::TransactionsTrieRootDigestAfter => {
"GLOBAL_METADATA_TXNS_TRIE_DIGEST_AFTER"
GlobalMetadata::TransactionTrieRootDigestAfter => {
"GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER"
}
GlobalMetadata::ReceiptsTrieRootDigestAfter => {
"GLOBAL_METADATA_RECEIPTS_TRIE_DIGEST_AFTER"
GlobalMetadata::ReceiptTrieRootDigestAfter => {
"GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_AFTER"
}
}
}

View File

@ -47,10 +47,21 @@ impl InterpreterMemory {
impl InterpreterMemory {
fn mload_general(&self, context: usize, segment: Segment, offset: usize) -> U256 {
self.context_memory[context].segments[segment as usize].get(offset)
let value = self.context_memory[context].segments[segment as usize].get(offset);
assert!(
value.bits() <= segment.bit_range(),
"Value read from memory exceeds expected range of {:?} segment",
segment
);
value
}
fn mstore_general(&mut self, context: usize, segment: Segment, offset: usize, value: U256) {
assert!(
value.bits() <= segment.bit_range(),
"Value written to memory exceeds expected range of {:?} segment",
segment
);
self.context_memory[context].segments[segment as usize].set(offset, value)
}
}
@ -162,7 +173,7 @@ impl<'a> Interpreter<'a> {
}
pub(crate) fn get_rlp_memory(&self) -> Vec<u8> {
self.memory.context_memory[self.context].segments[Segment::RlpRaw as usize]
self.memory.context_memory[0].segments[Segment::RlpRaw as usize]
.content
.iter()
.map(|x| x.as_u32() as u8)
@ -170,7 +181,7 @@ impl<'a> Interpreter<'a> {
}
pub(crate) fn set_rlp_memory(&mut self, rlp: Vec<u8>) {
self.memory.context_memory[self.context].segments[Segment::RlpRaw as usize].content =
self.memory.context_memory[0].segments[Segment::RlpRaw as usize].content =
rlp.into_iter().map(U256::from).collect();
}
@ -229,6 +240,7 @@ impl<'a> Interpreter<'a> {
0x1c => self.run_shr(), // "SHR",
0x1d => todo!(), // "SAR",
0x20 => self.run_keccak256(), // "KECCAK256",
0x21 => self.run_keccak_general(), // "KECCAK_GENERAL",
0x30 => todo!(), // "ADDRESS",
0x31 => todo!(), // "BALANCE",
0x32 => todo!(), // "ORIGIN",
@ -447,6 +459,18 @@ impl<'a> Interpreter<'a> {
self.push(U256::from_big_endian(hash.as_bytes()));
}
fn run_keccak_general(&mut self) {
let context = self.pop().as_usize();
let segment = Segment::all()[self.pop().as_usize()];
let offset = self.pop().as_usize();
let size = self.pop().as_usize();
let bytes = (offset..offset + size)
.map(|i| self.memory.mload_general(context, segment, i).byte(0))
.collect::<Vec<_>>();
let hash = keccak(bytes);
self.push(U256::from_big_endian(hash.as_bytes()));
}
fn run_prover_input(&mut self) -> anyhow::Result<()> {
let prover_input_fn = self
.prover_inputs_map
@ -567,7 +591,6 @@ impl<'a> Interpreter<'a> {
let segment = Segment::all()[self.pop().as_usize()];
let offset = self.pop().as_usize();
let value = self.memory.mload_general(context, segment, offset);
assert!(value.bits() <= segment.bit_range());
self.push(value);
}
@ -576,7 +599,6 @@ impl<'a> Interpreter<'a> {
let segment = Segment::all()[self.pop().as_usize()];
let offset = self.pop().as_usize();
let value = self.pop();
assert!(value.bits() <= segment.bit_range());
self.memory.mstore_general(context, segment, offset, value);
}
}

View File

@ -35,6 +35,7 @@ pub(crate) fn get_opcode(mnemonic: &str) -> u8 {
"SHR" => 0x1c,
"SAR" => 0x1d,
"KECCAK256" => 0x20,
"KECCAK_GENERAL" => 0x21,
"ADDRESS" => 0x30,
"BALANCE" => 0x31,
"ORIGIN" => 0x32,

View File

@ -80,9 +80,9 @@ fn no_op_jumps(code: &mut Vec<Item>) {
replace_windows(code, |window| {
if let [Push(Label(l)), StandardOp(jump), decl] = window
&& &jump == "JUMP"
&& (decl == LocalLabelDeclaration(l.clone()) || decl == GlobalLabelDeclaration(l.clone()))
&& (decl == LocalLabelDeclaration(l.clone()) || decl == GlobalLabelDeclaration(l))
{
Some(vec![LocalLabelDeclaration(l)])
Some(vec![decl])
} else {
None
}

View File

@ -0,0 +1,62 @@
use anyhow::Result;
use eth_trie_utils::partial_trie::{Nibbles, PartialTrie};
use ethereum_types::{BigEndianHash, H256, U256};
use hex_literal::hex;
use crate::cpu::kernel::aggregator::KERNEL;
use crate::cpu::kernel::interpreter::Interpreter;
use crate::generation::mpt::all_mpt_prover_inputs_reversed;
use crate::generation::TrieInputs;
#[test]
fn mpt_hash() -> Result<()> {
let nonce = U256::from(1111);
let balance = U256::from(2222);
let storage_root = U256::from(3333);
let code_hash = U256::from(4444);
let account = &[nonce, balance, storage_root, code_hash];
let account_rlp = rlp::encode_list(account);
// TODO: Try this more "advanced" trie.
// let state_trie = state_trie_ext_to_account_leaf(account_rlp.to_vec());
let state_trie = PartialTrie::Leaf {
nibbles: Nibbles {
count: 3,
packed: 0xABC.into(),
},
value: account_rlp.to_vec(),
};
// TODO: It seems like calc_hash isn't giving the expected hash yet, so for now, I'm using a
// hardcoded hash obtained from py-evm.
// let state_trie_hash = state_trie.calc_hash();
let state_trie_hash =
hex!("e38d6053838fe057c865ec0c74a8f0de21865d74fac222a2d3241fe57c9c3a0f").into();
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 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![]);
// Now, execute mpt_hash_state_trie.
interpreter.offset = mpt_hash_state_trie;
interpreter.push(0xDEADBEEFu32.into());
interpreter.run()?;
assert_eq!(interpreter.stack().len(), 1);
let hash = H256::from_uint(&interpreter.stack()[0]);
assert_eq!(hash, state_trie_hash);
Ok(())
}

View File

@ -0,0 +1,87 @@
use anyhow::Result;
use crate::cpu::kernel::aggregator::KERNEL;
use crate::cpu::kernel::interpreter::Interpreter;
#[test]
fn hex_prefix_even_nonterminated() -> Result<()> {
let hex_prefix = KERNEL.global_labels["hex_prefix_rlp"];
let retdest = 0xDEADBEEFu32.into();
let terminated = 0.into();
let packed_nibbles = 0xABCDEF.into();
let num_nibbles = 6.into();
let rlp_pos = 0.into();
let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles, rlp_pos];
let mut interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack);
interpreter.run()?;
assert_eq!(interpreter.stack(), vec![5.into()]);
assert_eq!(
interpreter.get_rlp_memory(),
vec![
0x80 + 4, // prefix
0, // neither flag is set
0xAB,
0xCD,
0xEF
]
);
Ok(())
}
#[test]
fn hex_prefix_odd_terminated() -> Result<()> {
let hex_prefix = KERNEL.global_labels["hex_prefix_rlp"];
let retdest = 0xDEADBEEFu32.into();
let terminated = 1.into();
let packed_nibbles = 0xABCDE.into();
let num_nibbles = 5.into();
let rlp_pos = 0.into();
let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles, rlp_pos];
let mut interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack);
interpreter.run()?;
assert_eq!(interpreter.stack(), vec![4.into()]);
assert_eq!(
interpreter.get_rlp_memory(),
vec![
0x80 + 3, // prefix
(2 + 1) * 16 + 0xA,
0xBC,
0xDE,
]
);
Ok(())
}
#[test]
fn hex_prefix_odd_terminated_tiny() -> Result<()> {
let hex_prefix = KERNEL.global_labels["hex_prefix_rlp"];
let retdest = 0xDEADBEEFu32.into();
let terminated = 1.into();
let packed_nibbles = 0xA.into();
let num_nibbles = 1.into();
let rlp_pos = 2.into();
let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles, rlp_pos];
let mut interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack);
interpreter.run()?;
assert_eq!(interpreter.stack(), vec![3.into()]);
assert_eq!(
interpreter.get_rlp_memory(),
vec![
// Since rlp_pos = 2, we skipped over the first two bytes.
0,
0,
// No length prefix; this tiny string is its own RLP encoding.
(2 + 1) * 16 + 0xA,
]
);
Ok(())
}

View File

@ -1,5 +1,7 @@
use eth_trie_utils::partial_trie::{Nibbles, PartialTrie};
mod hash;
mod hex_prefix;
mod load;
mod read;

View File

@ -42,10 +42,8 @@ fn mpt_read() -> Result<()> {
interpreter.push(interpreter.get_global_metadata_field(GlobalMetadata::StateTrieRoot));
interpreter.run()?;
dbg!(interpreter.stack());
assert_eq!(interpreter.stack().len(), 1);
let result_ptr = interpreter.stack()[0].as_usize();
dbg!(result_ptr);
let result = &interpreter.get_trie_data()[result_ptr..][..account.len()];
assert_eq!(result, account);

View File

@ -19,7 +19,7 @@ fn test_mstore_unpacking() -> Result<()> {
let mut interpreter = Interpreter::new_with_kernel(mstore_unpacking, initial_stack);
interpreter.run()?;
assert_eq!(interpreter.stack(), vec![]);
assert_eq!(interpreter.stack(), vec![4.into()]);
assert_eq!(
&interpreter.get_txn_data(),
&[0xAB.into(), 0xCD.into(), 0x12.into(), 0x34.into()]

View File

@ -3,84 +3,6 @@ use anyhow::Result;
use crate::cpu::kernel::aggregator::KERNEL;
use crate::cpu::kernel::interpreter::Interpreter;
#[test]
fn test_encode_rlp_scalar_small() -> Result<()> {
let encode_rlp_scalar = KERNEL.global_labels["encode_rlp_scalar"];
let retdest = 0xDEADBEEFu32.into();
let scalar = 42.into();
let pos = 2.into();
let initial_stack = vec![retdest, scalar, pos];
let mut interpreter = Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack);
interpreter.run()?;
let expected_stack = vec![3.into()]; // pos' = pos + rlp_len = 2 + 1
let expected_rlp = vec![0, 0, 42];
assert_eq!(interpreter.stack(), expected_stack);
assert_eq!(interpreter.get_rlp_memory(), expected_rlp);
Ok(())
}
#[test]
fn test_encode_rlp_scalar_medium() -> Result<()> {
let encode_rlp_scalar = KERNEL.global_labels["encode_rlp_scalar"];
let retdest = 0xDEADBEEFu32.into();
let scalar = 0x12345.into();
let pos = 2.into();
let initial_stack = vec![retdest, scalar, pos];
let mut interpreter = Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack);
interpreter.run()?;
let expected_stack = vec![6.into()]; // pos' = pos + rlp_len = 2 + 4
let expected_rlp = vec![0, 0, 0x80 + 3, 0x01, 0x23, 0x45];
assert_eq!(interpreter.stack(), expected_stack);
assert_eq!(interpreter.get_rlp_memory(), expected_rlp);
Ok(())
}
#[test]
fn test_encode_rlp_160() -> Result<()> {
let encode_rlp_160 = KERNEL.global_labels["encode_rlp_160"];
let retdest = 0xDEADBEEFu32.into();
let string = 0x12345.into();
let pos = 0.into();
let initial_stack = vec![retdest, string, pos];
let mut interpreter = Interpreter::new_with_kernel(encode_rlp_160, initial_stack);
interpreter.run()?;
let expected_stack = vec![(1 + 20).into()]; // pos'
#[rustfmt::skip]
let expected_rlp = vec![0x80 + 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x23, 0x45];
assert_eq!(interpreter.stack(), expected_stack);
assert_eq!(interpreter.get_rlp_memory(), expected_rlp);
Ok(())
}
#[test]
fn test_encode_rlp_256() -> Result<()> {
let encode_rlp_256 = KERNEL.global_labels["encode_rlp_256"];
let retdest = 0xDEADBEEFu32.into();
let string = 0x12345.into();
let pos = 0.into();
let initial_stack = vec![retdest, string, pos];
let mut interpreter = Interpreter::new_with_kernel(encode_rlp_256, initial_stack);
interpreter.run()?;
let expected_stack = vec![(1 + 32).into()]; // pos'
#[rustfmt::skip]
let expected_rlp = vec![0x80 + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x23, 0x45];
assert_eq!(interpreter.stack(), expected_stack);
assert_eq!(interpreter.get_rlp_memory(), expected_rlp);
Ok(())
}
#[test]
fn test_decode_rlp_string_len_short() -> Result<()> {
let decode_rlp_string_len = KERNEL.global_labels["decode_rlp_string_len"];

View File

@ -0,0 +1,155 @@
use anyhow::Result;
use crate::cpu::kernel::aggregator::KERNEL;
use crate::cpu::kernel::interpreter::Interpreter;
#[test]
fn test_encode_rlp_scalar_small() -> Result<()> {
let encode_rlp_scalar = KERNEL.global_labels["encode_rlp_scalar"];
let retdest = 0xDEADBEEFu32.into();
let scalar = 42.into();
let pos = 2.into();
let initial_stack = vec![retdest, scalar, pos];
let mut interpreter = Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack);
interpreter.run()?;
let expected_stack = vec![3.into()]; // pos' = pos + rlp_len = 2 + 1
let expected_rlp = vec![0, 0, 42];
assert_eq!(interpreter.stack(), expected_stack);
assert_eq!(interpreter.get_rlp_memory(), expected_rlp);
Ok(())
}
#[test]
fn test_encode_rlp_scalar_medium() -> Result<()> {
let encode_rlp_scalar = KERNEL.global_labels["encode_rlp_scalar"];
let retdest = 0xDEADBEEFu32.into();
let scalar = 0x12345.into();
let pos = 2.into();
let initial_stack = vec![retdest, scalar, pos];
let mut interpreter = Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack);
interpreter.run()?;
let expected_stack = vec![6.into()]; // pos' = pos + rlp_len = 2 + 4
let expected_rlp = vec![0, 0, 0x80 + 3, 0x01, 0x23, 0x45];
assert_eq!(interpreter.stack(), expected_stack);
assert_eq!(interpreter.get_rlp_memory(), expected_rlp);
Ok(())
}
#[test]
fn test_encode_rlp_160() -> Result<()> {
let encode_rlp_160 = KERNEL.global_labels["encode_rlp_160"];
let retdest = 0xDEADBEEFu32.into();
let string = 0x12345.into();
let pos = 0.into();
let initial_stack = vec![retdest, string, pos];
let mut interpreter = Interpreter::new_with_kernel(encode_rlp_160, initial_stack);
interpreter.run()?;
let expected_stack = vec![(1 + 20).into()]; // pos'
#[rustfmt::skip]
let expected_rlp = vec![0x80 + 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x23, 0x45];
assert_eq!(interpreter.stack(), expected_stack);
assert_eq!(interpreter.get_rlp_memory(), expected_rlp);
Ok(())
}
#[test]
fn test_encode_rlp_256() -> Result<()> {
let encode_rlp_256 = KERNEL.global_labels["encode_rlp_256"];
let retdest = 0xDEADBEEFu32.into();
let string = 0x12345.into();
let pos = 0.into();
let initial_stack = vec![retdest, string, pos];
let mut interpreter = Interpreter::new_with_kernel(encode_rlp_256, initial_stack);
interpreter.run()?;
let expected_stack = vec![(1 + 32).into()]; // pos'
#[rustfmt::skip]
let expected_rlp = vec![0x80 + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x23, 0x45];
assert_eq!(interpreter.stack(), expected_stack);
assert_eq!(interpreter.get_rlp_memory(), expected_rlp);
Ok(())
}
#[test]
fn test_prepend_rlp_list_prefix_small() -> Result<()> {
let prepend_rlp_list_prefix = KERNEL.global_labels["prepend_rlp_list_prefix"];
let retdest = 0xDEADBEEFu32.into();
let end_pos = (9 + 5).into();
let initial_stack = vec![retdest, end_pos];
let mut interpreter = Interpreter::new_with_kernel(prepend_rlp_list_prefix, initial_stack);
interpreter.set_rlp_memory(vec![
// Nine 0s to leave room for the longest possible RLP list prefix.
0, 0, 0, 0, 0, 0, 0, 0, 0,
// The actual RLP list payload, consisting of 5 tiny strings.
1, 2, 3, 4, 5,
]);
interpreter.run()?;
let expected_rlp_len = 6.into();
let expected_start_pos = 8.into();
let expected_stack = vec![expected_rlp_len, expected_start_pos];
let expected_rlp = vec![0, 0, 0, 0, 0, 0, 0, 0, 0xc0 + 5, 1, 2, 3, 4, 5];
assert_eq!(interpreter.stack(), expected_stack);
assert_eq!(interpreter.get_rlp_memory(), expected_rlp);
Ok(())
}
#[test]
fn test_prepend_rlp_list_prefix_large() -> Result<()> {
let prepend_rlp_list_prefix = KERNEL.global_labels["prepend_rlp_list_prefix"];
let retdest = 0xDEADBEEFu32.into();
let end_pos = (9 + 60).into();
let initial_stack = vec![retdest, end_pos];
let mut interpreter = Interpreter::new_with_kernel(prepend_rlp_list_prefix, initial_stack);
#[rustfmt::skip]
interpreter.set_rlp_memory(vec![
// Nine 0s to leave room for the longest possible RLP list prefix.
0, 0, 0, 0, 0, 0, 0, 0, 0,
// The actual RLP list payload, consisting of 60 tiny strings.
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
]);
interpreter.run()?;
let expected_rlp_len = 62.into();
let expected_start_pos = 7.into();
let expected_stack = vec![expected_rlp_len, expected_start_pos];
#[rustfmt::skip]
let expected_rlp = vec![
0, 0, 0, 0, 0, 0, 0, 0xf7 + 1, 60,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
];
assert_eq!(interpreter.stack(), expected_stack);
assert_eq!(interpreter.get_rlp_memory(), expected_rlp);
Ok(())
}

View File

@ -0,0 +1,2 @@
mod decode;
mod encode;

View File

@ -62,6 +62,7 @@ const STACK_BEHAVIORS: OpsColumnsView<Option<StackBehavior>> = OpsColumnsView {
shr: BASIC_BINARY_OP,
sar: BASIC_BINARY_OP,
keccak256: None, // TODO
keccak_general: None, // TODO
address: None, // TODO
balance: None, // TODO
origin: None, // TODO

View File

@ -89,18 +89,16 @@ pub(crate) fn generate_traces<F: RichField + Extendable<D>, const D: usize>(
let trie_roots_before = TrieRoots {
state_root: H256::from_uint(&read_metadata(GlobalMetadata::StateTrieRootDigestBefore)),
transactions_root: H256::from_uint(&read_metadata(
GlobalMetadata::TransactionsTrieRootDigestBefore,
)),
receipts_root: H256::from_uint(&read_metadata(
GlobalMetadata::ReceiptsTrieRootDigestBefore,
GlobalMetadata::TransactionTrieRootDigestBefore,
)),
receipts_root: H256::from_uint(&read_metadata(GlobalMetadata::ReceiptTrieRootDigestBefore)),
};
let trie_roots_after = TrieRoots {
state_root: H256::from_uint(&read_metadata(GlobalMetadata::StateTrieRootDigestAfter)),
transactions_root: H256::from_uint(&read_metadata(
GlobalMetadata::TransactionsTrieRootDigestAfter,
GlobalMetadata::TransactionTrieRootDigestAfter,
)),
receipts_root: H256::from_uint(&read_metadata(GlobalMetadata::ReceiptsTrieRootDigestAfter)),
receipts_root: H256::from_uint(&read_metadata(GlobalMetadata::ReceiptTrieRootDigestAfter)),
};
let GenerationState {

View File

@ -0,0 +1,81 @@
use std::collections::HashMap;
use eth_trie_utils::partial_trie::{Nibbles, PartialTrie};
use plonky2::field::goldilocks_field::GoldilocksField;
use plonky2::plonk::config::PoseidonGoldilocksConfig;
use plonky2::util::timing::TimingTree;
use plonky2_evm::all_stark::AllStark;
use plonky2_evm::config::StarkConfig;
use plonky2_evm::generation::{GenerationInputs, TrieInputs};
use plonky2_evm::proof::BlockMetadata;
use plonky2_evm::prover::prove;
use plonky2_evm::verifier::verify_proof;
type F = GoldilocksField;
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
/// Execute the empty list of transactions, i.e. a no-op.
#[test]
#[ignore] // TODO: Won't work until witness generation logic is finished.
fn test_empty_txn_list() -> anyhow::Result<()> {
let all_stark = AllStark::<F, D>::default();
let config = StarkConfig::standard_fast_config();
let block_metadata = BlockMetadata::default();
let state_trie = PartialTrie::Leaf {
nibbles: Nibbles {
count: 5,
packed: 0xABCDE.into(),
},
value: vec![1, 2, 3],
};
let transactions_trie = PartialTrie::Empty;
let receipts_trie = PartialTrie::Empty;
let storage_tries = vec![];
let state_trie_root = state_trie.calc_hash();
let txns_trie_root = transactions_trie.calc_hash();
let receipts_trie_root = receipts_trie.calc_hash();
let inputs = GenerationInputs {
signed_txns: vec![],
tries: TrieInputs {
state_trie,
transactions_trie,
receipts_trie,
storage_tries,
},
contract_code: HashMap::new(),
block_metadata,
};
let proof = prove::<F, C, D>(&all_stark, &config, inputs, &mut TimingTree::default())?;
assert_eq!(
proof.public_values.trie_roots_before.state_root,
state_trie_root
);
assert_eq!(
proof.public_values.trie_roots_after.state_root,
state_trie_root
);
assert_eq!(
proof.public_values.trie_roots_before.transactions_root,
txns_trie_root
);
assert_eq!(
proof.public_values.trie_roots_after.transactions_root,
txns_trie_root
);
assert_eq!(
proof.public_values.trie_roots_before.receipts_root,
receipts_trie_root
);
assert_eq!(
proof.public_values.trie_roots_after.receipts_root,
receipts_trie_root
);
verify_proof(all_stark, proof, &config)
}