Changes in interpreter and implement interpreter version for add11 (#1359)

* Fix interpreter, turn syscall opcodes into actual syscalls, create interpreter test for add11

* Rename test_add11 to test_add11_yml

* Apply comments

* Cleanup add11_yml interpreter test

* Make stack_top() return a Result, and remove Result from add11_yml test

* Apply comment
This commit is contained in:
Linda Guiga 2023-11-28 13:35:40 -05:00 committed by GitHub
parent 6dd2e313c4
commit 96f3faf26e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 504 additions and 528 deletions

View File

@ -21,6 +21,7 @@ use crate::generation::state::GenerationState;
use crate::generation::GenerationInputs;
use crate::memory::segments::Segment;
use crate::util::u256_to_usize;
use crate::witness::errors::ProgramError;
use crate::witness::gas::gas_to_charge;
use crate::witness::memory::{MemoryAddress, MemoryContextState, MemorySegmentState, MemoryState};
use crate::witness::operation::Operation;
@ -43,9 +44,7 @@ impl MemoryState {
}
pub(crate) struct Interpreter<'a> {
pub(crate) kernel_mode: bool,
jumpdests: Vec<usize>,
pub(crate) context: usize,
pub(crate) generation_state: GenerationState<F>,
prover_inputs_map: &'a HashMap<usize, ProverInputFn>,
pub(crate) halt_offsets: Vec<usize>,
@ -121,16 +120,16 @@ impl<'a> Interpreter<'a> {
prover_inputs: &'a HashMap<usize, ProverInputFn>,
) -> Self {
let mut result = Self {
kernel_mode: true,
jumpdests: find_jumpdests(code),
generation_state: GenerationState::new(GenerationInputs::default(), code)
.expect("Default inputs are known-good"),
prover_inputs_map: prover_inputs,
context: 0,
halt_offsets: vec![DEFAULT_HALT_OFFSET],
// `DEFAULT_HALT_OFFSET` is used as a halting point for the interpreter,
// while the label `halt` is the halting label in the kernel.
halt_offsets: vec![DEFAULT_HALT_OFFSET, KERNEL.global_labels["halt"]],
debug_offsets: vec![],
running: false,
opcode_count: [0; 0x100],
opcode_count: [0; 256],
};
result.generation_state.registers.program_counter = initial_offset;
let initial_stack_len = initial_stack.len();
@ -147,6 +146,10 @@ impl<'a> Interpreter<'a> {
pub(crate) fn run(&mut self) -> anyhow::Result<()> {
self.running = true;
while self.running {
let pc = self.generation_state.registers.program_counter;
if self.is_kernel() && self.halt_offsets.contains(&pc) {
return Ok(());
};
self.run_opcode()?;
}
println!("Opcode count:");
@ -161,8 +164,7 @@ impl<'a> Interpreter<'a> {
fn code(&self) -> &MemorySegmentState {
// The context is 0 if we are in kernel mode.
&self.generation_state.memory.contexts
[(1 - self.generation_state.registers.is_kernel as usize) * self.context]
&self.generation_state.memory.contexts[(1 - self.is_kernel() as usize) * self.context()]
.segments[Segment::Code as usize]
}
@ -198,6 +200,13 @@ impl<'a> Interpreter<'a> {
.set(field as usize, value)
}
pub(crate) fn set_global_metadata_multi_fields(&mut self, metadata: &[(GlobalMetadata, U256)]) {
for &(field, value) in metadata {
self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata as usize]
.set(field as usize, value);
}
}
pub(crate) fn get_trie_data(&self) -> &[U256] {
&self.generation_state.memory.contexts[0].segments[Segment::TrieData as usize].content
}
@ -221,7 +230,7 @@ impl<'a> Interpreter<'a> {
}
pub(crate) fn get_current_general_memory(&self) -> Vec<U256> {
self.generation_state.memory.contexts[self.context].segments
self.generation_state.memory.contexts[self.context()].segments
[Segment::KernelGeneral as usize]
.content
.clone()
@ -236,8 +245,8 @@ impl<'a> Interpreter<'a> {
}
pub(crate) fn set_current_general_memory(&mut self, memory: Vec<U256>) {
self.generation_state.memory.contexts[self.context].segments
[Segment::KernelGeneral as usize]
let context = self.context();
self.generation_state.memory.contexts[context].segments[Segment::KernelGeneral as usize]
.content = memory;
}
@ -279,18 +288,32 @@ impl<'a> Interpreter<'a> {
}
pub(crate) fn stack(&self) -> Vec<U256> {
let mut stack = self.generation_state.memory.contexts[self.context].segments
match self.stack_len().cmp(&1) {
Ordering::Greater => {
let mut stack = self.generation_state.memory.contexts[self.context()].segments
[Segment::Stack as usize]
.content
.clone();
if self.stack_len() > 0 {
stack.push(self.stack_top());
}
stack.truncate(self.stack_len() - 1);
stack.push(
self.stack_top()
.expect("The stack is checked to be nonempty"),
);
stack
}
Ordering::Equal => {
vec![self
.stack_top()
.expect("The stack is checked to be nonempty")]
}
Ordering::Less => {
vec![]
}
}
}
fn stack_segment_mut(&mut self) -> &mut Vec<U256> {
&mut self.generation_state.memory.contexts[self.context].segments[Segment::Stack as usize]
let context = self.context();
&mut self.generation_state.memory.contexts[context].segments[Segment::Stack as usize]
.content
}
@ -308,8 +331,12 @@ impl<'a> Interpreter<'a> {
pub(crate) fn push(&mut self, x: U256) {
if self.stack_len() > 0 {
let top = self.stack_top();
self.stack_segment_mut().push(top);
let top = self
.stack_top()
.expect("The stack is checked to be nonempty");
let cur_len = self.stack_len();
let stack_addr = MemoryAddress::new(self.context(), Segment::Stack, cur_len - 1);
self.generation_state.memory.set(stack_addr, top);
}
self.generation_state.registers.stack_top = x;
self.generation_state.registers.stack_len += 1;
@ -326,12 +353,7 @@ impl<'a> Interpreter<'a> {
self.generation_state.registers.stack_top = top;
}
self.generation_state.registers.stack_len -= 1;
let new_len = self.stack_len();
if new_len > 0 {
self.stack_segment_mut().truncate(new_len - 1);
} else {
self.stack_segment_mut().truncate(0);
}
result.expect("Empty stack")
}
@ -342,28 +364,27 @@ impl<'a> Interpreter<'a> {
.byte(0);
self.opcode_count[opcode as usize] += 1;
self.incr(1);
match opcode {
0x00 => self.run_stop(), // "STOP",
0x00 => self.run_syscall(opcode, 0, false)?, // "STOP",
0x01 => self.run_add(), // "ADD",
0x02 => self.run_mul(), // "MUL",
0x03 => self.run_sub(), // "SUB",
0x04 => self.run_div(), // "DIV",
0x05 => self.run_sdiv(), // "SDIV",
0x05 => self.run_syscall(opcode, 2, false)?, // "SDIV",
0x06 => self.run_mod(), // "MOD",
0x07 => self.run_smod(), // "SMOD",
0x07 => self.run_syscall(opcode, 2, false)?, // "SMOD",
0x08 => self.run_addmod(), // "ADDMOD",
0x09 => self.run_mulmod(), // "MULMOD",
0x0a => self.run_exp(), // "EXP",
0x0b => self.run_signextend(), // "SIGNEXTEND",
0x0a => self.run_syscall(opcode, 2, false)?, // "EXP",
0x0b => self.run_syscall(opcode, 2, false)?, // "SIGNEXTEND",
0x0c => self.run_addfp254(), // "ADDFP254",
0x0d => self.run_mulfp254(), // "MULFP254",
0x0e => self.run_subfp254(), // "SUBFP254",
0x0f => self.run_submod(), // "SUBMOD",
0x10 => self.run_lt(), // "LT",
0x11 => self.run_gt(), // "GT",
0x12 => self.run_slt(), // "SLT",
0x13 => self.run_sgt(), // "SGT",
0x12 => self.run_syscall(opcode, 2, false)?, // "SLT",
0x13 => self.run_syscall(opcode, 2, false)?, // "SGT",
0x14 => self.run_eq(), // "EQ",
0x15 => self.run_iszero(), // "ISZERO",
0x16 => self.run_and(), // "AND",
@ -373,44 +394,45 @@ impl<'a> Interpreter<'a> {
0x1a => self.run_byte(), // "BYTE",
0x1b => self.run_shl(), // "SHL",
0x1c => self.run_shr(), // "SHR",
0x1d => self.run_sar(), // "SAR",
0x20 => self.run_keccak256(), // "KECCAK256",
0x1d => self.run_syscall(opcode, 2, false)?, // "SAR",
0x20 => self.run_syscall(opcode, 2, false)?, // "KECCAK256",
0x21 => self.run_keccak_general(), // "KECCAK_GENERAL",
0x30 => self.run_address(), // "ADDRESS",
0x30 => self.run_syscall(opcode, 0, true)?, // "ADDRESS",
0x31 => self.run_syscall(opcode, 1, false)?, // "BALANCE",
0x32 => self.run_origin(), // "ORIGIN",
0x33 => self.run_caller(), // "CALLER",
0x34 => self.run_callvalue(), // "CALLVALUE",
0x35 => self.run_calldataload(), // "CALLDATALOAD",
0x36 => self.run_calldatasize(), // "CALLDATASIZE",
0x37 => self.run_calldatacopy(), // "CALLDATACOPY",
0x38 => self.run_codesize(), // "CODESIZE",
0x39 => self.run_codecopy(), // "CODECOPY",
0x3a => self.run_gasprice(), // "GASPRICE",
0x32 => self.run_syscall(opcode, 0, true)?, // "ORIGIN",
0x33 => self.run_syscall(opcode, 0, true)?, // "CALLER",
0x34 => self.run_syscall(opcode, 0, true)?, // "CALLVALUE",
0x35 => self.run_syscall(opcode, 1, false)?, // "CALLDATALOAD",
0x36 => self.run_syscall(opcode, 0, true)?, // "CALLDATASIZE",
0x37 => self.run_syscall(opcode, 3, false)?, // "CALLDATACOPY",
0x38 => self.run_syscall(opcode, 0, true)?, // "CODESIZE",
0x39 => self.run_syscall(opcode, 3, false)?, // "CODECOPY",
0x3a => self.run_syscall(opcode, 0, true)?, // "GASPRICE",
0x3b => self.run_syscall(opcode, 1, false)?, // "EXTCODESIZE",
0x3c => self.run_syscall(opcode, 4, false)?, // "EXTCODECOPY",
0x3d => self.run_returndatasize(), // "RETURNDATASIZE",
0x3e => self.run_returndatacopy(), // "RETURNDATACOPY",
0x3d => self.run_syscall(opcode, 0, true)?, // "RETURNDATASIZE",
0x3e => self.run_syscall(opcode, 3, false)?, // "RETURNDATACOPY",
0x3f => self.run_syscall(opcode, 1, false)?, // "EXTCODEHASH",
0x40 => self.run_syscall(opcode, 1, false)?, // "BLOCKHASH",
0x41 => self.run_coinbase(), // "COINBASE",
0x42 => self.run_timestamp(), // "TIMESTAMP",
0x43 => self.run_number(), // "NUMBER",
0x44 => self.run_difficulty(), // "DIFFICULTY",
0x45 => self.run_gaslimit(), // "GASLIMIT",
0x46 => self.run_chainid(), // "CHAINID",
0x48 => self.run_basefee(), // "BASEFEE",
0x41 => self.run_syscall(opcode, 0, true)?, // "COINBASE",
0x42 => self.run_syscall(opcode, 0, true)?, // "TIMESTAMP",
0x43 => self.run_syscall(opcode, 0, true)?, // "NUMBER",
0x44 => self.run_syscall(opcode, 0, true)?, // "DIFFICULTY",
0x45 => self.run_syscall(opcode, 0, true)?, // "GASLIMIT",
0x46 => self.run_syscall(opcode, 0, true)?, // "CHAINID",
0x47 => self.run_syscall(opcode, 0, true)?, // SELFABALANCE,
0x48 => self.run_syscall(opcode, 0, true)?, // "BASEFEE",
0x49 => self.run_prover_input()?, // "PROVER_INPUT",
0x50 => self.run_pop(), // "POP",
0x51 => self.run_mload(), // "MLOAD",
0x52 => self.run_mstore(), // "MSTORE",
0x53 => self.run_mstore8(), // "MSTORE8",
0x51 => self.run_syscall(opcode, 1, false)?, // "MLOAD",
0x52 => self.run_syscall(opcode, 2, false)?, // "MSTORE",
0x53 => self.run_syscall(opcode, 2, false)?, // "MSTORE8",
0x54 => self.run_syscall(opcode, 1, false)?, // "SLOAD",
0x55 => self.run_syscall(opcode, 2, false)?, // "SSTORE",
0x56 => self.run_jump(), // "JUMP",
0x57 => self.run_jumpi(), // "JUMPI",
0x58 => self.run_pc(), // "PC",
0x59 => self.run_msize(), // "MSIZE",
0x59 => self.run_syscall(opcode, 0, true)?, // "MSIZE",
0x5a => self.run_syscall(opcode, 0, true)?, // "GAS",
0x5b => self.run_jumpdest(), // "JUMPDEST",
x if (0x5f..0x80).contains(&x) => self.run_push(x - 0x5f), // "PUSH"
@ -471,10 +493,6 @@ impl<'a> Interpreter<'a> {
KERNEL.offset_label(self.generation_state.registers.program_counter)
}
fn run_stop(&mut self) {
self.running = false;
}
fn run_add(&mut self) {
let x = self.pop();
let y = self.pop();
@ -522,75 +540,12 @@ impl<'a> Interpreter<'a> {
self.push(if y.is_zero() { U256::zero() } else { x / y });
}
fn run_sdiv(&mut self) {
let mut x = self.pop();
let mut y = self.pop();
let y_is_zero = y.is_zero();
if y_is_zero {
self.push(U256::zero());
} else if y.eq(&MINUS_ONE) && x.eq(&MIN_VALUE) {
self.push(MIN_VALUE);
} else {
let x_is_pos = x.eq(&(x & SIGN_MASK));
let y_is_pos = y.eq(&(y & SIGN_MASK));
// We compute the absolute quotient first,
// then adapt its sign based on the operands.
if !x_is_pos {
x = two_complement(x);
}
if !y_is_pos {
y = two_complement(y);
}
let div = x / y;
if div.eq(&U256::zero()) {
self.push(U256::zero());
}
self.push(if x_is_pos == y_is_pos {
div
} else {
two_complement(div)
});
}
}
fn run_mod(&mut self) {
let x = self.pop();
let y = self.pop();
self.push(if y.is_zero() { U256::zero() } else { x % y });
}
fn run_smod(&mut self) {
let mut x = self.pop();
let mut y = self.pop();
if y.is_zero() {
self.push(U256::zero());
} else {
let x_is_pos = x.eq(&(x & SIGN_MASK));
let y_is_pos = y.eq(&(y & SIGN_MASK));
// We compute the absolute remainder first,
// then adapt its sign based on the operands.
if !x_is_pos {
x = two_complement(x);
}
if !y_is_pos {
y = two_complement(y);
}
let rem = x % y;
if rem.eq(&U256::zero()) {
self.push(U256::zero());
}
// Remainder always has the same sign as the dividend.
self.push(if x_is_pos { rem } else { two_complement(rem) });
}
}
fn run_addmod(&mut self) {
let x = self.pop();
let y = self.pop();
@ -629,12 +584,6 @@ impl<'a> Interpreter<'a> {
});
}
fn run_exp(&mut self) {
let x = self.pop();
let y = self.pop();
self.push(x.overflowing_pow(y).0);
}
fn run_lt(&mut self) {
let x = self.pop();
let y = self.pop();
@ -647,43 +596,6 @@ impl<'a> Interpreter<'a> {
self.push_bool(x > y);
}
fn run_slt(&mut self) {
let x = self.pop();
let y = self.pop();
self.push_bool(signed_cmp(x, y) == Ordering::Less);
}
fn run_sgt(&mut self) {
let x = self.pop();
let y = self.pop();
self.push_bool(signed_cmp(x, y) == Ordering::Greater);
}
fn run_signextend(&mut self) {
let n = self.pop();
let x = self.pop();
if n > U256::from(31) {
self.push(x);
} else {
let n = n.low_u64() as usize;
let num_bytes_prepend = 31 - n;
let mut x_bytes = [0u8; 32];
x.to_big_endian(&mut x_bytes);
let x_bytes = x_bytes[num_bytes_prepend..].to_vec();
let sign_bit = x_bytes[0] >> 7;
let mut bytes = if sign_bit == 0 {
vec![0; num_bytes_prepend]
} else {
vec![0xff; num_bytes_prepend]
};
bytes.extend_from_slice(&x_bytes);
self.push(U256::from_big_endian(&bytes));
}
}
fn run_eq(&mut self) {
let x = self.pop();
let y = self.pop();
@ -745,45 +657,6 @@ impl<'a> Interpreter<'a> {
self.push(value >> shift);
}
fn run_sar(&mut self) {
let shift = self.pop();
let value = self.pop();
let value_is_neg = !value.eq(&(value & SIGN_MASK));
if shift < U256::from(256usize) {
let shift = shift.low_u64() as usize;
let mask = !(MINUS_ONE >> shift);
let value_shifted = value >> shift;
if value_is_neg {
self.push(value_shifted | mask);
} else {
self.push(value_shifted);
};
} else {
self.push(if value_is_neg {
MINUS_ONE
} else {
U256::zero()
});
}
}
fn run_keccak256(&mut self) {
let offset = self.pop().as_usize();
let size = self.pop().as_usize();
let bytes = (offset..offset + size)
.map(|i| {
self.generation_state
.memory
.mload_general(self.context, Segment::MainMemory, i)
.byte(0)
})
.collect::<Vec<_>>();
let hash = keccak(bytes);
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()];
@ -804,161 +677,6 @@ impl<'a> Interpreter<'a> {
self.push(U256::from_big_endian(hash.as_bytes()));
}
fn run_address(&mut self) {
self.push(
self.generation_state.memory.contexts[self.context].segments
[Segment::ContextMetadata as usize]
.get(ContextMetadata::Address as usize),
)
}
fn run_origin(&mut self) {
self.push(self.get_txn_field(NormalizedTxnField::Origin))
}
fn run_caller(&mut self) {
self.push(
self.generation_state.memory.contexts[self.context].segments
[Segment::ContextMetadata as usize]
.get(ContextMetadata::Caller as usize),
)
}
fn run_callvalue(&mut self) {
self.push(
self.generation_state.memory.contexts[self.context].segments
[Segment::ContextMetadata as usize]
.get(ContextMetadata::CallValue as usize),
)
}
fn run_calldataload(&mut self) {
let offset = self.pop().as_usize();
let value = U256::from_big_endian(
&(0..32)
.map(|i| {
self.generation_state
.memory
.mload_general(self.context, Segment::Calldata, offset + i)
.byte(0)
})
.collect::<Vec<_>>(),
);
self.push(value);
}
fn run_calldatasize(&mut self) {
self.push(
self.generation_state.memory.contexts[self.context].segments
[Segment::ContextMetadata as usize]
.get(ContextMetadata::CalldataSize as usize),
)
}
fn run_calldatacopy(&mut self) {
let dest_offset = self.pop().as_usize();
let offset = self.pop().as_usize();
let size = self.pop().as_usize();
for i in 0..size {
let calldata_byte = self.generation_state.memory.mload_general(
self.context,
Segment::Calldata,
offset + i,
);
self.generation_state.memory.mstore_general(
self.context,
Segment::MainMemory,
dest_offset + i,
calldata_byte,
);
}
}
fn run_codesize(&mut self) {
self.push(
self.generation_state.memory.contexts[self.context].segments
[Segment::ContextMetadata as usize]
.get(ContextMetadata::CodeSize as usize),
)
}
fn run_codecopy(&mut self) {
let dest_offset = self.pop().as_usize();
let offset = self.pop().as_usize();
let size = self.pop().as_usize();
for i in 0..size {
let code_byte =
self.generation_state
.memory
.mload_general(self.context, Segment::Code, offset + i);
self.generation_state.memory.mstore_general(
self.context,
Segment::MainMemory,
dest_offset + i,
code_byte,
);
}
}
fn run_gasprice(&mut self) {
self.push(self.get_txn_field(NormalizedTxnField::ComputedFeePerGas))
}
fn run_returndatasize(&mut self) {
self.push(
self.generation_state.memory.contexts[self.context].segments
[Segment::ContextMetadata as usize]
.get(ContextMetadata::ReturndataSize as usize),
)
}
fn run_returndatacopy(&mut self) {
let dest_offset = self.pop().as_usize();
let offset = self.pop().as_usize();
let size = self.pop().as_usize();
for i in 0..size {
let returndata_byte = self.generation_state.memory.mload_general(
self.context,
Segment::Returndata,
offset + i,
);
self.generation_state.memory.mstore_general(
self.context,
Segment::MainMemory,
dest_offset + i,
returndata_byte,
);
}
}
fn run_coinbase(&mut self) {
self.push(self.get_global_metadata_field(GlobalMetadata::BlockBeneficiary))
}
fn run_timestamp(&mut self) {
self.push(self.get_global_metadata_field(GlobalMetadata::BlockTimestamp))
}
fn run_number(&mut self) {
self.push(self.get_global_metadata_field(GlobalMetadata::BlockNumber))
}
fn run_difficulty(&mut self) {
self.push(self.get_global_metadata_field(GlobalMetadata::BlockDifficulty))
}
fn run_gaslimit(&mut self) {
self.push(self.get_global_metadata_field(GlobalMetadata::BlockGasLimit))
}
fn run_basefee(&mut self) {
self.push(self.get_global_metadata_field(GlobalMetadata::BlockBaseFee))
}
fn run_chainid(&mut self) {
self.push(self.get_global_metadata_field(GlobalMetadata::BlockChainId))
}
fn run_prover_input(&mut self) -> anyhow::Result<()> {
let prover_input_fn = self
.prover_inputs_map
@ -976,47 +694,6 @@ impl<'a> Interpreter<'a> {
self.pop();
}
fn run_mload(&mut self) {
let offset = self.pop().as_usize();
let value = U256::from_big_endian(
&(0..32)
.map(|i| {
self.generation_state
.memory
.mload_general(self.context, Segment::MainMemory, offset + i)
.byte(0)
})
.collect::<Vec<_>>(),
);
self.push(value);
}
fn run_mstore(&mut self) {
let offset = self.pop().as_usize();
let value = self.pop();
let mut bytes = [0; 32];
value.to_big_endian(&mut bytes);
for (i, byte) in (0..32).zip(bytes) {
self.generation_state.memory.mstore_general(
self.context,
Segment::MainMemory,
offset + i,
byte.into(),
);
}
}
fn run_mstore8(&mut self) {
let offset = self.pop().as_usize();
let value = self.pop();
self.generation_state.memory.mstore_general(
self.context,
Segment::MainMemory,
offset,
value.byte(0).into(),
);
}
fn run_syscall(
&mut self,
opcode: u8,
@ -1030,7 +707,7 @@ impl<'a> Interpreter<'a> {
}
if stack_len_increased
&& !self.generation_state.registers.is_kernel
&& !self.is_kernel()
&& self.generation_state.registers.stack_len >= MAX_USER_STACK_SIZE
{
return Err(anyhow!("Stack overflow"));
@ -1048,12 +725,11 @@ impl<'a> Interpreter<'a> {
u256_to_usize(handler_addr).map_err(|_| anyhow!("The program counter is too large"))?;
let syscall_info = U256::from(self.generation_state.registers.program_counter)
+ U256::from((self.generation_state.registers.is_kernel as usize) << 32)
+ U256::from((self.is_kernel() as usize) << 32)
+ (U256::from(self.generation_state.registers.gas_used) << 192);
self.generation_state.registers.program_counter = new_program_counter;
self.generation_state.registers.is_kernel = true;
self.kernel_mode = true;
self.set_is_kernel(true);
self.generation_state.registers.gas_used = 0;
self.push(syscall_info);
@ -1084,21 +760,13 @@ impl<'a> Interpreter<'a> {
);
}
fn run_msize(&mut self) {
self.push(
self.generation_state.memory.contexts[self.context].segments
[Segment::ContextMetadata as usize]
.get(ContextMetadata::MemWords as usize),
)
}
fn run_jumpdest(&mut self) {
assert!(!self.kernel_mode, "JUMPDEST is not needed in kernel code");
assert!(!self.is_kernel(), "JUMPDEST is not needed in kernel code");
}
fn jump_to(&mut self, offset: usize) {
// The JUMPDEST rule is not enforced in kernel mode.
if !self.kernel_mode && self.jumpdests.binary_search(&offset).is_err() {
if !self.is_kernel() && self.jumpdests.binary_search(&offset).is_err() {
panic!("Destination is not a JUMPDEST.");
}
@ -1116,14 +784,13 @@ impl<'a> Interpreter<'a> {
}
fn run_dup(&mut self, n: u8) -> anyhow::Result<()> {
if n == 0 {
self.push(self.stack_top());
} else {
assert!(n > 0);
self.push(
stack_peek(&self.generation_state, n as usize - 1)
.map_err(|_| anyhow!("Stack underflow."))?,
);
}
Ok(())
}
@ -1132,18 +799,40 @@ impl<'a> Interpreter<'a> {
ensure!(len > n as usize);
let to_swap = stack_peek(&self.generation_state, n as usize)
.map_err(|_| anyhow!("Stack underflow"))?;
self.stack_segment_mut()[len - n as usize - 1] = self.stack_top();
let old_value = self.stack_segment_mut()[len - n as usize - 1];
self.stack_segment_mut()[len - n as usize - 1] = self
.stack_top()
.expect("The stack is checked to be nonempty.");
self.generation_state.registers.stack_top = to_swap;
Ok(())
}
fn run_get_context(&mut self) {
self.push(self.context.into());
self.push(self.context().into());
}
fn run_set_context(&mut self) {
let x = self.pop();
self.context = x.as_usize();
let new_ctx = self.pop().as_usize();
let sp_to_save = self.stack_len().into();
let old_ctx = self.context();
let sp_field = ContextMetadata::StackSize as usize;
let old_sp_addr = MemoryAddress::new(old_ctx, Segment::ContextMetadata, sp_field);
let new_sp_addr = MemoryAddress::new(new_ctx, Segment::ContextMetadata, sp_field);
self.generation_state.memory.set(old_sp_addr, sp_to_save);
let new_sp = self.generation_state.memory.get(new_sp_addr).as_usize();
if new_sp > 0 {
let new_stack_top = self.generation_state.memory.contexts[new_ctx].segments
[Segment::Stack as usize]
.content[new_sp - 1];
self.generation_state.registers.stack_top = new_stack_top;
}
self.set_context(new_ctx);
self.generation_state.registers.stack_len = new_sp;
}
fn run_mload_general(&mut self) {
@ -1219,8 +908,7 @@ impl<'a> Interpreter<'a> {
}
self.generation_state.registers.program_counter = program_counter;
self.generation_state.registers.is_kernel = is_kernel_mode;
self.kernel_mode = is_kernel_mode;
self.set_is_kernel(is_kernel_mode);
self.generation_state.registers.gas_used = gas_used_val;
}
@ -1228,8 +916,31 @@ impl<'a> Interpreter<'a> {
self.generation_state.registers.stack_len
}
pub(crate) fn stack_top(&self) -> U256 {
self.generation_state.registers.stack_top
pub(crate) fn stack_top(&self) -> anyhow::Result<U256> {
if self.stack_len() > 0 {
Ok(self.generation_state.registers.stack_top)
} else {
Err(anyhow!("Stack underflow"))
}
}
pub(crate) fn is_kernel(&self) -> bool {
self.generation_state.registers.is_kernel
}
pub(crate) fn set_is_kernel(&mut self, is_kernel: bool) {
self.generation_state.registers.is_kernel = is_kernel
}
pub(crate) fn context(&self) -> usize {
self.generation_state.registers.context
}
pub(crate) fn set_context(&mut self, context: usize) {
if context == 0 {
assert!(self.is_kernel());
}
self.generation_state.registers.context = context;
}
}
@ -1511,8 +1222,10 @@ fn get_mnemonic(opcode: u8) -> &'static str {
mod tests {
use std::collections::HashMap;
use crate::cpu::kernel::interpreter::run;
use crate::cpu::kernel::constants::context_metadata::ContextMetadata;
use crate::cpu::kernel::interpreter::{run, Interpreter};
use crate::memory::segments::Segment;
use crate::witness::memory::MemoryAddress;
#[test]
fn test_run() -> anyhow::Result<()> {
@ -1541,20 +1254,51 @@ mod tests {
// PUSH1 0x42
// PUSH1 0x27
// MSTORE8
let code = vec![
let code = [
0x60, 0xff, 0x60, 0x0, 0x52, 0x60, 0, 0x51, 0x60, 0x1, 0x51, 0x60, 0x42, 0x60, 0x27,
0x53,
];
let pis = HashMap::new();
let run = run(&code, 0, vec![], &pis)?;
assert_eq!(run.stack(), &[0xff.into(), 0xff00.into()]);
let mut interpreter = Interpreter::new_with_kernel(0, vec![]);
interpreter.set_code(1, code.to_vec());
interpreter.generation_state.memory.contexts[1].segments[Segment::ContextMetadata as usize]
.set(ContextMetadata::GasLimit as usize, 100_000.into());
// Set context and kernel mode.
interpreter.set_context(1);
interpreter.set_is_kernel(false);
// Set memory necessary to sys_stop.
interpreter.generation_state.memory.set(
MemoryAddress::new(
1,
Segment::ContextMetadata,
ContextMetadata::ParentProgramCounter as usize,
),
0xdeadbeefu32.into(),
);
interpreter.generation_state.memory.set(
MemoryAddress::new(
1,
Segment::ContextMetadata,
ContextMetadata::ParentContext as usize,
),
1.into(),
);
interpreter.run()?;
// sys_stop returns `success` and `cum_gas_used`, that we need to pop.
interpreter.pop();
interpreter.pop();
assert_eq!(interpreter.stack(), &[0xff.into(), 0xff00.into()]);
assert_eq!(
run.generation_state.memory.contexts[0].segments[Segment::MainMemory as usize]
interpreter.generation_state.memory.contexts[1].segments[Segment::MainMemory as usize]
.get(0x27),
0x42.into()
);
assert_eq!(
run.generation_state.memory.contexts[0].segments[Segment::MainMemory as usize]
interpreter.generation_state.memory.contexts[1].segments[Segment::MainMemory as usize]
.get(0x1f),
0xff.into()
);

View File

@ -16,6 +16,7 @@ use crate::cpu::kernel::tests::mpt::nibbles_64;
use crate::generation::mpt::{all_mpt_prover_inputs_reversed, AccountRlp};
use crate::generation::TrieInputs;
use crate::memory::segments::Segment;
use crate::witness::memory::MemoryAddress;
use crate::Node;
// Test account with a given code hash.
@ -146,19 +147,20 @@ fn test_extcodecopy() -> Result<()> {
// Prepare the interpreter by inserting the account in the state trie.
prepare_interpreter(&mut interpreter, address, &account)?;
interpreter.generation_state.memory.contexts[interpreter.context].segments
let context = interpreter.context();
interpreter.generation_state.memory.contexts[context].segments
[Segment::ContextMetadata as usize]
.set(GasLimit as usize, U256::from(1000000000000u64) << 192);
.set(GasLimit as usize, U256::from(1000000000000u64));
let extcodecopy = KERNEL.global_labels["sys_extcodecopy"];
// Put random data in main memory and the `KernelAccountCode` segment for realism.
let mut rng = thread_rng();
for i in 0..2000 {
interpreter.generation_state.memory.contexts[interpreter.context].segments
interpreter.generation_state.memory.contexts[context].segments
[Segment::MainMemory as usize]
.set(i, U256::from(rng.gen::<u8>()));
interpreter.generation_state.memory.contexts[interpreter.context].segments
interpreter.generation_state.memory.contexts[context].segments
[Segment::KernelAccountCode as usize]
.set(i, U256::from(rng.gen::<u8>()));
}
@ -176,7 +178,7 @@ fn test_extcodecopy() -> Result<()> {
interpreter.push(offset.into());
interpreter.push(dest_offset.into());
interpreter.push(U256::from_big_endian(address.as_bytes()));
interpreter.push(0xDEADBEEFu32.into()); // kexit_info
interpreter.push((0xDEADBEEFu64 + (1 << 32)).into()); // kexit_info
interpreter.generation_state.inputs.contract_code =
HashMap::from([(keccak(&code), code.clone())]);
interpreter.run()?;
@ -184,7 +186,7 @@ fn test_extcodecopy() -> Result<()> {
assert!(interpreter.stack().is_empty());
// Check that the code was correctly copied to memory.
for i in 0..size {
let memory = interpreter.generation_state.memory.contexts[interpreter.context].segments
let memory = interpreter.generation_state.memory.contexts[context].segments
[Segment::MainMemory as usize]
.get(dest_offset + i);
assert_eq!(
@ -226,10 +228,24 @@ fn prepare_interpreter_all_accounts(
);
interpreter.generation_state.memory.contexts[1].segments[Segment::ContextMetadata as usize]
.set(ContextMetadata::GasLimit as usize, 100_000.into());
interpreter.context = 1;
interpreter.generation_state.registers.context = 1;
interpreter.generation_state.registers.is_kernel = false;
interpreter.kernel_mode = false;
interpreter.set_context(1);
interpreter.set_is_kernel(false);
interpreter.generation_state.memory.set(
MemoryAddress::new(
1,
Segment::ContextMetadata,
ContextMetadata::ParentProgramCounter as usize,
),
0xdeadbeefu32.into(),
);
interpreter.generation_state.memory.set(
MemoryAddress::new(
1,
Segment::ContextMetadata,
ContextMetadata::ParentContext as usize,
),
1.into(),
);
Ok(())
}
@ -272,6 +288,11 @@ fn sstore() -> Result<()> {
interpreter.run()?;
// The first two elements in the stack are `success` and `leftover_gas`,
// returned by the `sys_stop` opcode.
interpreter.pop();
interpreter.pop();
// The code should have added an element to the storage of `to_account`. We run
// `mpt_hash_state_trie` to check that.
let account_after = AccountRlp {
@ -287,10 +308,8 @@ fn sstore() -> Result<()> {
// Now, execute mpt_hash_state_trie.
let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"];
interpreter.generation_state.registers.program_counter = mpt_hash_state_trie;
interpreter.context = 0;
interpreter.generation_state.registers.context = 0;
interpreter.generation_state.registers.is_kernel = true;
interpreter.kernel_mode = true;
interpreter.set_is_kernel(true);
interpreter.set_context(0);
interpreter.push(0xDEADBEEFu32.into());
interpreter.run()?;
@ -353,6 +372,11 @@ fn sload() -> Result<()> {
interpreter.run()?;
// The first two elements in the stack are `success` and `leftover_gas`,
// returned by the `sys_stop` opcode.
interpreter.pop();
interpreter.pop();
// The SLOAD in the provided code should return 0, since
// the storage trie is empty. The last step in the code
// pushes the value 3.
@ -362,10 +386,8 @@ fn sload() -> Result<()> {
// Now, execute mpt_hash_state_trie. We check that the state trie has not changed.
let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"];
interpreter.generation_state.registers.program_counter = mpt_hash_state_trie;
interpreter.context = 0;
interpreter.generation_state.registers.context = 0;
interpreter.generation_state.registers.is_kernel = true;
interpreter.kernel_mode = true;
interpreter.set_is_kernel(true);
interpreter.set_context(0);
interpreter.push(0xDEADBEEFu32.into());
interpreter.run()?;

View File

@ -0,0 +1,212 @@
use std::collections::HashMap;
use std::str::FromStr;
use anyhow::{anyhow, Result};
use eth_trie_utils::nibbles::Nibbles;
use eth_trie_utils::partial_trie::{HashedPartialTrie, Node, PartialTrie};
use ethereum_types::{Address, H256, U256};
use hex_literal::hex;
use keccak_hash::keccak;
use crate::cpu::kernel::aggregator::KERNEL;
use crate::cpu::kernel::constants::context_metadata::ContextMetadata;
use crate::cpu::kernel::constants::global_metadata::GlobalMetadata;
use crate::cpu::kernel::interpreter::Interpreter;
use crate::generation::mpt::{all_mpt_prover_inputs_reversed, AccountRlp, LegacyReceiptRlp};
use crate::generation::rlp::all_rlp_prover_inputs_reversed;
use crate::generation::TrieInputs;
use crate::memory::segments::Segment;
use crate::proof::TrieRoots;
use crate::util::h2u;
// Stolen from `tests/mpt/insert.rs`
// Prepare the interpreter by loading the initial MPTs and
// by setting all `GlobalMetadata` and necessary code into memory.
fn prepare_interpreter(
interpreter: &mut Interpreter,
trie_inputs: TrieInputs,
transaction: &[u8],
contract_code: HashMap<H256, Vec<u8>>,
) {
let load_all_mpts = KERNEL.global_labels["load_all_mpts"];
interpreter.generation_state.registers.program_counter = load_all_mpts;
interpreter.push(0xDEADBEEFu32.into());
interpreter.generation_state.mpt_prover_inputs =
all_mpt_prover_inputs_reversed(&trie_inputs).expect("Invalid MPT data.");
interpreter.run().expect("MPT loading failed.");
assert_eq!(interpreter.stack(), vec![]);
// Set necessary `GlobalMetadata`.
let global_metadata_to_set = [
(
GlobalMetadata::StateTrieRootDigestBefore,
h2u(trie_inputs.state_trie.hash()),
),
(
GlobalMetadata::TransactionTrieRootDigestBefore,
h2u(trie_inputs.transactions_trie.hash()),
),
(
GlobalMetadata::ReceiptTrieRootDigestBefore,
h2u(trie_inputs.receipts_trie.hash()),
),
(GlobalMetadata::TxnNumberAfter, 1.into()),
(GlobalMetadata::BlockGasUsedAfter, 0xa868u64.into()),
(GlobalMetadata::BlockGasLimit, 1_000_000.into()),
(GlobalMetadata::BlockBaseFee, 10.into()),
(
GlobalMetadata::BlockBeneficiary,
U256::from_big_endian(
&Address::from(hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba")).0,
),
),
];
interpreter.set_global_metadata_multi_fields(&global_metadata_to_set);
// Set contract code and transaction.
interpreter.generation_state.inputs.contract_code = contract_code;
interpreter.generation_state.inputs.signed_txn = Some(transaction.to_vec());
let rlp_prover_inputs = all_rlp_prover_inputs_reversed(transaction);
interpreter.generation_state.rlp_prover_inputs = rlp_prover_inputs;
}
#[test]
fn test_add11_yml() {
let beneficiary = hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba");
let sender = hex!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b");
let to = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87");
let beneficiary_state_key = keccak(beneficiary);
let sender_state_key = keccak(sender);
let to_hashed = keccak(to);
let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap();
let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap();
let to_nibbles = Nibbles::from_bytes_be(to_hashed.as_bytes()).unwrap();
let code = [0x60, 0x01, 0x60, 0x01, 0x01, 0x60, 0x00, 0x55, 0x00];
let code_hash = keccak(code);
let mut contract_code = HashMap::new();
contract_code.insert(keccak(vec![]), vec![]);
contract_code.insert(code_hash, code.to_vec());
let beneficiary_account_before = AccountRlp {
nonce: 1.into(),
..AccountRlp::default()
};
let sender_account_before = AccountRlp {
balance: 0x0de0b6b3a7640000u64.into(),
..AccountRlp::default()
};
let to_account_before = AccountRlp {
balance: 0x0de0b6b3a7640000u64.into(),
code_hash,
..AccountRlp::default()
};
let mut state_trie_before = HashedPartialTrie::from(Node::Empty);
state_trie_before.insert(
beneficiary_nibbles,
rlp::encode(&beneficiary_account_before).to_vec(),
);
state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec());
state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec());
let tries_before = TrieInputs {
state_trie: state_trie_before,
transactions_trie: Node::Empty.into(),
receipts_trie: Node::Empty.into(),
storage_tries: vec![(to_hashed, Node::Empty.into())],
};
let txn = hex!("f863800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d87830186a0801ba0ffb600e63115a7362e7811894a91d8ba4330e526f22121c994c4692035dfdfd5a06198379fcac8de3dbfac48b165df4bf88e2088f294b61efb9a65fe2281c76e16");
let initial_stack = vec![];
let mut interpreter = Interpreter::new_with_kernel(0, initial_stack);
prepare_interpreter(&mut interpreter, tries_before.clone(), &txn, contract_code);
let expected_state_trie_after = {
let beneficiary_account_after = AccountRlp {
nonce: 1.into(),
..AccountRlp::default()
};
let sender_account_after = AccountRlp {
balance: 0xde0b6b3a75be550u64.into(),
nonce: 1.into(),
..AccountRlp::default()
};
let to_account_after = AccountRlp {
balance: 0xde0b6b3a76586a0u64.into(),
code_hash,
// Storage map: { 0 => 2 }
storage_root: HashedPartialTrie::from(Node::Leaf {
nibbles: Nibbles::from_h256_be(keccak([0u8; 32])),
value: vec![2],
})
.hash(),
..AccountRlp::default()
};
let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty);
expected_state_trie_after.insert(
beneficiary_nibbles,
rlp::encode(&beneficiary_account_after).to_vec(),
);
expected_state_trie_after
.insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec());
expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec());
expected_state_trie_after
};
let receipt_0 = LegacyReceiptRlp {
status: true,
cum_gas_used: 0xa868u64.into(),
bloom: vec![0; 256].into(),
logs: vec![],
};
let mut receipts_trie = HashedPartialTrie::from(Node::Empty);
receipts_trie.insert(
Nibbles::from_str("0x80").unwrap(),
rlp::encode(&receipt_0).to_vec(),
);
let transactions_trie: HashedPartialTrie = Node::Leaf {
nibbles: Nibbles::from_str("0x80").unwrap(),
value: txn.to_vec(),
}
.into();
let trie_roots_after = TrieRoots {
state_root: expected_state_trie_after.hash(),
transactions_root: transactions_trie.hash(),
receipts_root: receipts_trie.hash(),
};
// Set trie roots after the transaction was executed.
let metadata_to_set = [
(
GlobalMetadata::StateTrieRootDigestAfter,
h2u(trie_roots_after.state_root),
),
(
GlobalMetadata::TransactionTrieRootDigestAfter,
h2u(trie_roots_after.transactions_root),
),
(
GlobalMetadata::ReceiptTrieRootDigestAfter,
h2u(trie_roots_after.receipts_root),
),
];
interpreter.set_global_metadata_multi_fields(&metadata_to_set);
let route_txn_label = KERNEL.global_labels["hash_initial_tries"];
// Switch context and initialize memory with the data we need for the tests.
interpreter.generation_state.registers.program_counter = route_txn_label;
interpreter.generation_state.memory.contexts[0].segments[Segment::ContextMetadata as usize]
.set(ContextMetadata::GasLimit as usize, 1_000_000.into());
interpreter.set_is_kernel(true);
interpreter.run().expect("Proving add11 failed.");
}

View File

@ -3,7 +3,7 @@ use ethereum_types::U256;
use rand::{thread_rng, Rng};
use crate::cpu::kernel::aggregator::KERNEL;
use crate::cpu::kernel::interpreter::{run, run_interpreter};
use crate::cpu::kernel::interpreter::{run, run_interpreter, Interpreter};
#[test]
fn test_exp() -> Result<()> {
@ -15,33 +15,28 @@ fn test_exp() -> Result<()> {
// Random input
let initial_stack = vec![0xDEADBEEFu32.into(), b, a];
let stack_with_kernel = run_interpreter(exp, initial_stack)?.stack().to_vec();
let initial_stack = vec![b, a];
let code = [0xa, 0x63, 0xde, 0xad, 0xbe, 0xef, 0x56]; // EXP, PUSH4 deadbeef, JUMP
let stack_with_opcode = run(&code, 0, initial_stack, &KERNEL.prover_inputs)?
.stack()
.to_vec();
assert_eq!(stack_with_kernel, stack_with_opcode);
let mut interpreter = Interpreter::new_with_kernel(0, initial_stack.clone());
let stack_with_kernel = run_interpreter(exp, initial_stack)?.stack();
let expected_exp = a.overflowing_pow(b).0;
assert_eq!(stack_with_kernel, vec![expected_exp]);
// 0 base
let initial_stack = vec![0xDEADBEEFu32.into(), b, U256::zero()];
let stack_with_kernel = run_interpreter(exp, initial_stack)?.stack().to_vec();
let initial_stack = vec![b, U256::zero()];
let code = [0xa, 0x63, 0xde, 0xad, 0xbe, 0xef, 0x56]; // EXP, PUSH4 deadbeef, JUMP
let stack_with_opcode = run(&code, 0, initial_stack, &KERNEL.prover_inputs)?
.stack()
.to_vec();
assert_eq!(stack_with_kernel, stack_with_opcode);
let stack_with_kernel = run_interpreter(exp, initial_stack)?.stack();
let expected_exp = U256::zero().overflowing_pow(b).0;
assert_eq!(stack_with_kernel, vec![expected_exp]);
// 0 exponent
let initial_stack = vec![0xDEADBEEFu32.into(), U256::zero(), a];
let stack_with_kernel = run_interpreter(exp, initial_stack)?.stack().to_vec();
let initial_stack = vec![U256::zero(), a];
let code = [0xa, 0x63, 0xde, 0xad, 0xbe, 0xef, 0x56]; // EXP, PUSH4 deadbeef, JUMP
let stack_with_opcode = run(&code, 0, initial_stack, &KERNEL.prover_inputs)?
.stack()
.to_vec();
assert_eq!(stack_with_kernel, stack_with_opcode);
interpreter.set_is_kernel(true);
interpreter.set_context(0);
let stack_with_kernel = run_interpreter(exp, initial_stack)?.stack();
let expected_exp = 1.into();
assert_eq!(stack_with_kernel, vec![expected_exp]);
Ok(())
}

View File

@ -1,4 +1,5 @@
mod account_code;
mod add11;
mod balance;
mod bignum;
mod blake2_f;

View File

@ -120,7 +120,9 @@ fn run_test(fn_label: &str, expected_fn: fn(U256, U256) -> U256, opname: &str) {
let mut interpreter = Interpreter::new_with_kernel(fn_label, stack);
interpreter.run().unwrap();
assert_eq!(interpreter.stack_len(), 1usize, "unexpected stack size");
let output = interpreter.stack_top();
let output = interpreter
.stack_top()
.expect("The stack should not be empty.");
let expected_output = expected_fn(x, y);
assert_eq!(
output, expected_output,