mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-08 00:33:06 +00:00
Merge branch 'main' of github.com:mir-protocol/plonky2 into ripeMD
This commit is contained in:
commit
0dc1a40306
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
80
evm/src/cpu/kernel/asm/mpt/hash_trie_specific.asm
Normal file
80
evm/src/cpu/kernel/asm/mpt/hash_trie_specific.asm
Normal 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
|
||||
104
evm/src/cpu/kernel/asm/mpt/hex_prefix.asm
Normal file
104
evm/src/cpu/kernel/asm/mpt/hex_prefix.asm
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
global mpt_write:
|
||||
// stack: node_ptr, num_nibbles, key, retdest
|
||||
// TODO
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
62
evm/src/cpu/kernel/tests/mpt/hash.rs
Normal file
62
evm/src/cpu/kernel/tests/mpt/hash.rs
Normal 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(())
|
||||
}
|
||||
87
evm/src/cpu/kernel/tests/mpt/hex_prefix.rs
Normal file
87
evm/src/cpu/kernel/tests/mpt/hex_prefix.rs
Normal 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(())
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
use eth_trie_utils::partial_trie::{Nibbles, PartialTrie};
|
||||
|
||||
mod hash;
|
||||
mod hex_prefix;
|
||||
mod load;
|
||||
mod read;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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()]
|
||||
|
||||
@ -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"];
|
||||
155
evm/src/cpu/kernel/tests/rlp/encode.rs
Normal file
155
evm/src/cpu/kernel/tests/rlp/encode.rs
Normal 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(())
|
||||
}
|
||||
2
evm/src/cpu/kernel/tests/rlp/mod.rs
Normal file
2
evm/src/cpu/kernel/tests/rlp/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
mod decode;
|
||||
mod encode;
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
81
evm/tests/empty_txn_list.rs
Normal file
81
evm/tests/empty_txn_list.rs
Normal 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)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user