mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-04 06:43:07 +00:00
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:
parent
6dd2e313c4
commit
96f3faf26e
@ -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()
|
||||
);
|
||||
|
||||
@ -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()?;
|
||||
|
||||
|
||||
212
evm/src/cpu/kernel/tests/add11.rs
Normal file
212
evm/src/cpu/kernel/tests/add11.rs
Normal 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.");
|
||||
}
|
||||
@ -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(())
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
mod account_code;
|
||||
mod add11;
|
||||
mod balance;
|
||||
mod bignum;
|
||||
mod blake2_f;
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user