mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-03 06:13:07 +00:00
MPT hashing logic, part 3
This commit is contained in:
parent
239ecedb0c
commit
9e483528d3
@ -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),
|
||||
|
||||
@ -1,3 +1,46 @@
|
||||
global mpt_hash_state_trie:
|
||||
// stack: retdest
|
||||
%mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT)
|
||||
// stack: node_ptr, retdest
|
||||
%mpt_hash(encode_account)
|
||||
|
||||
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
|
||||
|
||||
// Computes the Merkle root of the given trie node.
|
||||
//
|
||||
// The encode_value function should take as input
|
||||
@ -47,7 +90,7 @@
|
||||
// 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)
|
||||
%jump(hex_prefix_rlp)
|
||||
%%mpt_hash_leaf_after_hex_prefix:
|
||||
// stack: rlp_pos, node_payload_ptr, retdest
|
||||
SWAP1
|
||||
@ -59,12 +102,12 @@
|
||||
// stack: rlp_end_pos, retdest
|
||||
%prepend_rlp_list_prefix
|
||||
// stack: rlp_start_pos, rlp_len, retdest
|
||||
PUSH $SEGMENT_RLP
|
||||
PUSH @SEGMENT_RLP_RAW
|
||||
PUSH 0 // kernel context
|
||||
// stack: rlp_start_addr: 3, rlp_len, retdest
|
||||
KECCAK_GENERAL
|
||||
// stack: hash, retdest
|
||||
SWAP
|
||||
SWAP1
|
||||
JUMP
|
||||
%endmacro
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ global hex_prefix_rlp:
|
||||
|
||||
rlp_header_medium:
|
||||
// stack: hp_len, i, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest
|
||||
DUP1 // value = hp_len
|
||||
DUP1 %add_const(0x80) // value = 0x80 + hp_len
|
||||
DUP4 // offset = rlp_pos
|
||||
%mstore_rlp
|
||||
|
||||
@ -44,7 +44,7 @@ rlp_header_large:
|
||||
// In practice hex-prefix length will never exceed 256, so the length of the
|
||||
// length will always be 1 byte in this case.
|
||||
|
||||
PUSH 1 // value = len_of_len = 1
|
||||
PUSH 0xb8 // value = 0xb7 + len_of_len = 0xb8
|
||||
DUP4 // offset = rlp_pos
|
||||
%mstore_rlp
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -79,7 +79,7 @@ encode_rlp_fixed:
|
||||
%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
|
||||
PUSH 0 // context
|
||||
// stack: context, segment, pos, string, len, encode_rlp_fixed, pos, retdest
|
||||
%jump(mstore_unpacking)
|
||||
|
||||
@ -90,6 +90,54 @@ encode_rlp_fixed_finish:
|
||||
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)
|
||||
-> (0, @SEGMENT_RLP_RAW, pos, payload_len, len_of_len,
|
||||
encode_rlp_list_prefix_large_done_writing_len,
|
||||
pos, len_of_len, retdest)
|
||||
%jump(mstore_unpacking)
|
||||
encode_rlp_list_prefix_large_done_writing_len:
|
||||
// stack: pos', len_of_len, retdest
|
||||
ADD
|
||||
// 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
|
||||
@ -131,16 +179,15 @@ prepend_rlp_list_prefix_big:
|
||||
PUSH 8
|
||||
SUB
|
||||
// stack: start_pos, len_of_len, payload_len, end_pos, retdest
|
||||
DUP2 DUP2 %mstore_rlp // rlp[start_pos] = len_of_len
|
||||
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)
|
||||
-> (len_of_len, start_len_pos, payload_len,
|
||||
-> (0, @SEGMENT_RLP_RAW, start_len_pos, // context, segment, offset
|
||||
payload_len, len_of_len,
|
||||
prepend_rlp_list_prefix_big_done_writing_len,
|
||||
start_pos, end_pos, retdest)
|
||||
%jump(encode_rlp_fixed)
|
||||
%jump(mstore_unpacking)
|
||||
prepend_rlp_list_prefix_big_done_writing_len:
|
||||
// stack: start_payload_pos, start_pos, end_pos, retdest
|
||||
POP
|
||||
// stack: start_pos, end_pos, retdest
|
||||
DUP1
|
||||
SWAP2
|
||||
@ -160,7 +207,7 @@ prepend_rlp_list_prefix_big_done_writing_len:
|
||||
// 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:
|
||||
global num_bytes:
|
||||
// stack: x, retdest
|
||||
PUSH 0 // i
|
||||
// stack: i, x, retdest
|
||||
@ -192,3 +239,22 @@ 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
|
||||
|
||||
@ -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,
|
||||
|
||||
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(())
|
||||
}
|
||||
@ -20,9 +20,11 @@ fn hex_prefix_even_nonterminated() -> Result<()> {
|
||||
assert_eq!(
|
||||
interpreter.get_rlp_memory(),
|
||||
vec![
|
||||
4, // length
|
||||
0, // neither flag is set
|
||||
0xAB, 0xCD, 0xEF
|
||||
0x80 + 4, // prefix
|
||||
0, // neither flag is set
|
||||
0xAB,
|
||||
0xCD,
|
||||
0xEF
|
||||
]
|
||||
);
|
||||
|
||||
@ -46,7 +48,7 @@ fn hex_prefix_odd_terminated() -> Result<()> {
|
||||
assert_eq!(
|
||||
interpreter.get_rlp_memory(),
|
||||
vec![
|
||||
3, // length
|
||||
0x80 + 3, // prefix
|
||||
(2 + 1) * 16 + 0xA,
|
||||
0xBC,
|
||||
0xDE,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
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);
|
||||
|
||||
|
||||
@ -108,3 +108,48 @@ fn test_prepend_rlp_list_prefix_small() -> Result<()> {
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user