diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 37de2314..b40337d9 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -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, - pub(crate) context: usize, pub(crate) generation_state: GenerationState, prover_inputs_map: &'a HashMap, pub(crate) halt_offsets: Vec, @@ -121,16 +120,16 @@ impl<'a> Interpreter<'a> { prover_inputs: &'a HashMap, ) -> 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 { - 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) { - 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 { - 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()); + 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(); + 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![] + } } - stack } - fn stack_segment_mut(&mut self) -> &mut Vec { - &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,85 +364,85 @@ impl<'a> Interpreter<'a> { .byte(0); self.opcode_count[opcode as usize] += 1; self.incr(1); - match opcode { - 0x00 => self.run_stop(), // "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", - 0x06 => self.run_mod(), // "MOD", - 0x07 => self.run_smod(), // "SMOD", - 0x08 => self.run_addmod(), // "ADDMOD", - 0x09 => self.run_mulmod(), // "MULMOD", - 0x0a => self.run_exp(), // "EXP", - 0x0b => self.run_signextend(), // "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", - 0x14 => self.run_eq(), // "EQ", - 0x15 => self.run_iszero(), // "ISZERO", - 0x16 => self.run_and(), // "AND", - 0x17 => self.run_or(), // "OR", - 0x18 => self.run_xor(), // "XOR", - 0x19 => self.run_not(), // "NOT", - 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", - 0x21 => self.run_keccak_general(), // "KECCAK_GENERAL", - 0x30 => self.run_address(), // "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", - 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", - 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", - 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", - 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", - 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" - x if (0x80..0x90).contains(&x) => self.run_dup(x - 0x7f)?, // "DUP" + 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_syscall(opcode, 2, false)?, // "SDIV", + 0x06 => self.run_mod(), // "MOD", + 0x07 => self.run_syscall(opcode, 2, false)?, // "SMOD", + 0x08 => self.run_addmod(), // "ADDMOD", + 0x09 => self.run_mulmod(), // "MULMOD", + 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_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", + 0x17 => self.run_or(), // "OR", + 0x18 => self.run_xor(), // "XOR", + 0x19 => self.run_not(), // "NOT", + 0x1a => self.run_byte(), // "BYTE", + 0x1b => self.run_shl(), // "SHL", + 0x1c => self.run_shr(), // "SHR", + 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_syscall(opcode, 0, true)?, // "ADDRESS", + 0x31 => self.run_syscall(opcode, 1, false)?, // "BALANCE", + 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_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_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_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_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" + x if (0x80..0x90).contains(&x) => self.run_dup(x - 0x7f)?, // "DUP" x if (0x90..0xa0).contains(&x) => self.run_swap(x - 0x8f)?, // "SWAP" - 0xa0 => self.run_syscall(opcode, 2, false)?, // "LOG0", - 0xa1 => self.run_syscall(opcode, 3, false)?, // "LOG1", - 0xa2 => self.run_syscall(opcode, 4, false)?, // "LOG2", - 0xa3 => self.run_syscall(opcode, 5, false)?, // "LOG3", - 0xa4 => self.run_syscall(opcode, 6, false)?, // "LOG4", + 0xa0 => self.run_syscall(opcode, 2, false)?, // "LOG0", + 0xa1 => self.run_syscall(opcode, 3, false)?, // "LOG1", + 0xa2 => self.run_syscall(opcode, 4, false)?, // "LOG2", + 0xa3 => self.run_syscall(opcode, 5, false)?, // "LOG3", + 0xa4 => self.run_syscall(opcode, 6, false)?, // "LOG4", 0xa5 => bail!( "Executed PANIC, stack={:?}, memory={:?}", self.stack(), @@ -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::>(); - 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::>(), - ); - 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::>(), - ); - 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 { - self.push( - stack_peek(&self.generation_state, n as usize - 1) - .map_err(|_| anyhow!("Stack underflow."))?, - ); - } + 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 { + 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() ); diff --git a/evm/src/cpu/kernel/tests/account_code.rs b/evm/src/cpu/kernel/tests/account_code.rs index 919b9f2f..2782e71f 100644 --- a/evm/src/cpu/kernel/tests/account_code.rs +++ b/evm/src/cpu/kernel/tests/account_code.rs @@ -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::())); - 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::())); } @@ -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()?; diff --git a/evm/src/cpu/kernel/tests/add11.rs b/evm/src/cpu/kernel/tests/add11.rs new file mode 100644 index 00000000..048dad86 --- /dev/null +++ b/evm/src/cpu/kernel/tests/add11.rs @@ -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>, +) { + 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."); +} diff --git a/evm/src/cpu/kernel/tests/exp.rs b/evm/src/cpu/kernel/tests/exp.rs index 1655064e..8fee16d9 100644 --- a/evm/src/cpu/kernel/tests/exp.rs +++ b/evm/src/cpu/kernel/tests/exp.rs @@ -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(()) } diff --git a/evm/src/cpu/kernel/tests/mod.rs b/evm/src/cpu/kernel/tests/mod.rs index b66c0162..2fe2d096 100644 --- a/evm/src/cpu/kernel/tests/mod.rs +++ b/evm/src/cpu/kernel/tests/mod.rs @@ -1,4 +1,5 @@ mod account_code; +mod add11; mod balance; mod bignum; mod blake2_f; diff --git a/evm/src/cpu/kernel/tests/signed_syscalls.rs b/evm/src/cpu/kernel/tests/signed_syscalls.rs index 93391cf6..74b3524b 100644 --- a/evm/src/cpu/kernel/tests/signed_syscalls.rs +++ b/evm/src/cpu/kernel/tests/signed_syscalls.rs @@ -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,