From 209dc26dc95969e7e269999875e01c1cbd02f9e1 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 29 Jul 2022 14:29:10 +0200 Subject: [PATCH] Remove stack and code in interpreter --- evm/src/cpu/kernel/interpreter.rs | 73 +++++++++------ evm/src/cpu/kernel/tests/curve_ops.rs | 124 +++++++++++++++++++------- evm/src/cpu/kernel/tests/ecrecover.rs | 8 +- evm/src/cpu/kernel/tests/exp.rs | 24 +++-- 4 files changed, 164 insertions(+), 65 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 61272c24..5c2f6514 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -6,7 +6,7 @@ use keccak_hash::keccak; use crate::cpu::kernel::assembler::Kernel; use crate::cpu::kernel::prover_input::ProverInputFn; -use crate::generation::memory::MemoryContextState; +use crate::generation::memory::{MemoryContextState, MemorySegmentState}; use crate::memory::segments::Segment; /// Halt interpreter execution whenever a jump to this offset is done. @@ -25,6 +25,18 @@ impl Default for InterpreterMemory { } } +impl InterpreterMemory { + fn with_code_and_stack(code: &[u8], stack: Vec) -> Self { + let mut mem = Self::default(); + for (i, b) in code.iter().copied().enumerate() { + mem.context_memory[0].segments[Segment::Code as usize].set(i, b.into()); + } + mem.context_memory[0].segments[Segment::Stack as usize].content = stack; + + mem + } +} + impl InterpreterMemory { fn mload_general(&self, context: usize, segment: Segment, offset: usize) -> U256 { self.context_memory[context].segments[segment as usize].get(offset) @@ -35,12 +47,9 @@ impl InterpreterMemory { } } -// TODO: Remove `code` and `stack` fields as they are contained in `memory`. pub struct Interpreter<'a> { - code: &'a [u8], jumpdests: Vec, offset: usize, - pub(crate) stack: Vec, context: usize, memory: InterpreterMemory, prover_inputs_map: &'a HashMap, @@ -68,11 +77,9 @@ pub fn run<'a>( prover_inputs: &'a HashMap, ) -> anyhow::Result> { let mut interpreter = Interpreter { - code, jumpdests: find_jumpdests(code), offset: initial_offset, - stack: initial_stack, - memory: InterpreterMemory::default(), + memory: InterpreterMemory::with_code_and_stack(code, initial_stack), prover_inputs_map: prover_inputs, prover_inputs: Vec::new(), context: 0, @@ -87,28 +94,43 @@ pub fn run<'a>( } impl<'a> Interpreter<'a> { - fn slice(&self, n: usize) -> &[u8] { - &self.code[self.offset..self.offset + n] + fn code(&self) -> &MemorySegmentState { + &self.memory.context_memory[self.context].segments[Segment::Code as usize] + } + + fn code_slice(&self, n: usize) -> Vec { + self.code().content[self.offset..self.offset + n] + .iter() + .map(|u256| u256.byte(0)) + .collect::>() } fn incr(&mut self, n: usize) { self.offset += n; } + pub(crate) fn stack(&self) -> &[U256] { + &self.memory.context_memory[self.context].segments[Segment::Stack as usize].content + } + + fn stack_mut(&mut self) -> &mut Vec { + &mut self.memory.context_memory[self.context].segments[Segment::Stack as usize].content + } + fn push(&mut self, x: U256) { - self.stack.push(x); + self.stack_mut().push(x); } fn push_bool(&mut self, x: bool) { - self.stack.push(if x { U256::one() } else { U256::zero() }); + self.push(if x { U256::one() } else { U256::zero() }); } fn pop(&mut self) -> U256 { - self.stack.pop().expect("Pop on empty stack.") + self.stack_mut().pop().expect("Pop on empty stack.") } fn run_opcode(&mut self) -> anyhow::Result<()> { - let opcode = self.code.get(self.offset).copied().unwrap_or_default(); + let opcode = self.code().get(self.offset).byte(0); self.incr(1); match opcode { 0x00 => self.run_stop(), // "STOP", @@ -336,8 +358,8 @@ impl<'a> Interpreter<'a> { .prover_inputs_map .get(&(self.offset - 1)) .ok_or_else(|| anyhow!("Offset not in prover inputs."))?; - let output = prover_input_fn.run(&self.stack); - self.stack.push(output); + let output = prover_input_fn.run(self.stack()); + self.push(output); self.prover_inputs.push(output); Ok(()) } @@ -406,18 +428,18 @@ impl<'a> Interpreter<'a> { } fn run_push(&mut self, num_bytes: u8) { - let x = U256::from_big_endian(self.slice(num_bytes as usize)); + let x = U256::from_big_endian(&self.code_slice(num_bytes as usize)); self.incr(num_bytes as usize); self.push(x); } fn run_dup(&mut self, n: u8) { - self.push(self.stack[self.stack.len() - n as usize]); + self.push(self.stack()[self.stack().len() - n as usize]); } fn run_swap(&mut self, n: u8) { - let len = self.stack.len(); - self.stack.swap(len - 1, len - n as usize - 1); + let len = self.stack().len(); + self.stack_mut().swap(len - 1, len - n as usize - 1); } fn run_get_context(&mut self) { @@ -468,7 +490,7 @@ fn find_jumpdests(code: &[u8]) -> Vec { mod tests { use std::collections::HashMap; - use crate::cpu::kernel::interpreter::{run, Interpreter}; + use crate::cpu::kernel::interpreter::run; use crate::memory::segments::Segment; #[test] @@ -477,8 +499,8 @@ mod tests { 0x60, 0x1, 0x60, 0x2, 0x1, 0x63, 0xde, 0xad, 0xbe, 0xef, 0x56, ]; // PUSH1, 1, PUSH1, 2, ADD, PUSH4 deadbeef, JUMP assert_eq!( - run(&code, 0, vec![], &HashMap::new())?.stack, - vec![0x3.into()], + run(&code, 0, vec![], &HashMap::new())?.stack(), + &[0x3.into()], ); Ok(()) } @@ -504,14 +526,13 @@ mod tests { ]; let pis = HashMap::new(); let run = run(&code, 0, vec![], &pis)?; - let Interpreter { stack, memory, .. } = run; - assert_eq!(stack, vec![0xff.into(), 0xff00.into()]); + assert_eq!(run.stack(), &[0xff.into(), 0xff00.into()]); assert_eq!( - memory.context_memory[0].segments[Segment::MainMemory as usize].get(0x27), + run.memory.context_memory[0].segments[Segment::MainMemory as usize].get(0x27), 0x42.into() ); assert_eq!( - memory.context_memory[0].segments[Segment::MainMemory as usize].get(0x1f), + run.memory.context_memory[0].segments[Segment::MainMemory as usize].get(0x1f), 0xff.into() ); Ok(()) diff --git a/evm/src/cpu/kernel/tests/curve_ops.rs b/evm/src/cpu/kernel/tests/curve_ops.rs index 44609f21..0aaa94ea 100644 --- a/evm/src/cpu/kernel/tests/curve_ops.rs +++ b/evm/src/cpu/kernel/tests/curve_ops.rs @@ -43,76 +43,110 @@ mod bn { // Standard addition #1 let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point1.1, point1.0])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point2.1, point2.0])?); // Standard addition #2 let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, point0.1, point0.0])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point2.1, point2.0])?); // Standard doubling #1 let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point0.1, point0.0])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point3.1, point3.0])?); // Standard doubling #2 let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0])?; - let stack = run_with_kernel(&kernel, ec_double, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_double, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point3.1, point3.0])?); // Standard doubling #3 let initial_stack = u256ify(["0xdeadbeef", "0x2", point0.1, point0.0])?; - let stack = run_with_kernel(&kernel, ec_mul, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point3.1, point3.0])?); // Addition with identity #1 let initial_stack = u256ify(["0xdeadbeef", identity.1, identity.0, point1.1, point1.0])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point1.1, point1.0])?); // Addition with identity #2 let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, identity.1, identity.0])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point1.1, point1.0])?); // Addition with identity #3 let initial_stack = u256ify(["0xdeadbeef", identity.1, identity.0, identity.1, identity.0])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([identity.1, identity.0])?); // Addition with invalid point(s) #1 let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, invalid.1, invalid.0])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, vec![U256::MAX, U256::MAX]); // Addition with invalid point(s) #2 let initial_stack = u256ify(["0xdeadbeef", invalid.1, invalid.0, point0.1, point0.0])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, vec![U256::MAX, U256::MAX]); // Addition with invalid point(s) #3 let initial_stack = u256ify(["0xdeadbeef", invalid.1, invalid.0, identity.1, identity.0])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, vec![U256::MAX, U256::MAX]); // Addition with invalid point(s) #4 let initial_stack = u256ify(["0xdeadbeef", invalid.1, invalid.0, invalid.1, invalid.0])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, vec![U256::MAX, U256::MAX]); // Scalar multiplication #1 let initial_stack = u256ify(["0xdeadbeef", s, point0.1, point0.0])?; - let stack = run_with_kernel(&kernel, ec_mul, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point4.1, point4.0])?); // Scalar multiplication #2 let initial_stack = u256ify(["0xdeadbeef", "0x0", point0.1, point0.0])?; - let stack = run_with_kernel(&kernel, ec_mul, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([identity.1, identity.0])?); // Scalar multiplication #3 let initial_stack = u256ify(["0xdeadbeef", "0x1", point0.1, point0.0])?; - let stack = run_with_kernel(&kernel, ec_mul, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point0.1, point0.0])?); // Scalar multiplication #4 let initial_stack = u256ify(["0xdeadbeef", s, identity.1, identity.0])?; - let stack = run_with_kernel(&kernel, ec_mul, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([identity.1, identity.0])?); // Scalar multiplication #5 let initial_stack = u256ify(["0xdeadbeef", s, invalid.1, invalid.0])?; - let stack = run_with_kernel(&kernel, ec_mul, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, vec![U256::MAX, U256::MAX]); // Multiple calls @@ -126,7 +160,9 @@ mod bn { point0.1, point0.0, ])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point4.1, point4.0])?); Ok(()) @@ -176,55 +212,79 @@ mod secp { // Standard addition #1 let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point1.1, point1.0])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point2.1, point2.0])?); // Standard addition #2 let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, point0.1, point0.0])?; - let stack = run(&kernel.code, ec_add, initial_stack, &kernel.prover_inputs)?.stack; + let stack = run(&kernel.code, ec_add, initial_stack, &kernel.prover_inputs)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point2.1, point2.0])?); // Standard doubling #1 let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0, point0.1, point0.0])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point3.1, point3.0])?); // Standard doubling #2 let initial_stack = u256ify(["0xdeadbeef", point0.1, point0.0])?; - let stack = run_with_kernel(&kernel, ec_double, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_double, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point3.1, point3.0])?); // Standard doubling #3 let initial_stack = u256ify(["0xdeadbeef", "0x2", point0.1, point0.0])?; - let stack = run_with_kernel(&kernel, ec_mul, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point3.1, point3.0])?); // Addition with identity #1 let initial_stack = u256ify(["0xdeadbeef", identity.1, identity.0, point1.1, point1.0])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point1.1, point1.0])?); // Addition with identity #2 let initial_stack = u256ify(["0xdeadbeef", point1.1, point1.0, identity.1, identity.0])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point1.1, point1.0])?); // Addition with identity #3 let initial_stack = u256ify(["0xdeadbeef", identity.1, identity.0, identity.1, identity.0])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([identity.1, identity.0])?); // Scalar multiplication #1 let initial_stack = u256ify(["0xdeadbeef", s, point0.1, point0.0])?; - let stack = run_with_kernel(&kernel, ec_mul, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point4.1, point4.0])?); // Scalar multiplication #2 let initial_stack = u256ify(["0xdeadbeef", "0x0", point0.1, point0.0])?; - let stack = run_with_kernel(&kernel, ec_mul, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([identity.1, identity.0])?); // Scalar multiplication #3 let initial_stack = u256ify(["0xdeadbeef", "0x1", point0.1, point0.0])?; - let stack = run_with_kernel(&kernel, ec_mul, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point0.1, point0.0])?); // Scalar multiplication #4 let initial_stack = u256ify(["0xdeadbeef", s, identity.1, identity.0])?; - let stack = run_with_kernel(&kernel, ec_mul, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_mul, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([identity.1, identity.0])?); // Multiple calls @@ -238,7 +298,9 @@ mod secp { point0.1, point0.0, ])?; - let stack = run_with_kernel(&kernel, ec_add, initial_stack)?.stack; + let stack = run_with_kernel(&kernel, ec_add, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, u256ify([point4.1, point4.0])?); Ok(()) diff --git a/evm/src/cpu/kernel/tests/ecrecover.rs b/evm/src/cpu/kernel/tests/ecrecover.rs index b105cf47..c01adc53 100644 --- a/evm/src/cpu/kernel/tests/ecrecover.rs +++ b/evm/src/cpu/kernel/tests/ecrecover.rs @@ -18,7 +18,9 @@ fn test_valid_ecrecover( ) -> Result<()> { let ecrecover = kernel.global_labels["ecrecover"]; let initial_stack = u256ify(["0xdeadbeef", s, r, v, hash])?; - let stack = run_with_kernel(kernel, ecrecover, initial_stack)?.stack; + let stack = run_with_kernel(kernel, ecrecover, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack[0], U256::from_str(expected).unwrap()); Ok(()) @@ -27,7 +29,9 @@ fn test_valid_ecrecover( fn test_invalid_ecrecover(hash: &str, v: &str, r: &str, s: &str, kernel: &Kernel) -> Result<()> { let ecrecover = kernel.global_labels["ecrecover"]; let initial_stack = u256ify(["0xdeadbeef", s, r, v, hash])?; - let stack = run_with_kernel(kernel, ecrecover, initial_stack)?.stack; + let stack = run_with_kernel(kernel, ecrecover, initial_stack)? + .stack() + .to_vec(); assert_eq!(stack, vec![U256::MAX]); Ok(()) diff --git a/evm/src/cpu/kernel/tests/exp.rs b/evm/src/cpu/kernel/tests/exp.rs index 049fd23a..388a1ac3 100644 --- a/evm/src/cpu/kernel/tests/exp.rs +++ b/evm/src/cpu/kernel/tests/exp.rs @@ -18,26 +18,38 @@ fn test_exp() -> Result<()> { // Random input let initial_stack = vec![U256::from_str("0xdeadbeef")?, b, a]; - let stack_with_kernel = run_with_kernel(&kernel, exp, initial_stack)?.stack; + let stack_with_kernel = run_with_kernel(&kernel, 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; + let stack_with_opcode = run(&code, 0, initial_stack, &kernel.prover_inputs)? + .stack() + .to_vec(); assert_eq!(stack_with_kernel, stack_with_opcode); // 0 base let initial_stack = vec![U256::from_str("0xdeadbeef")?, b, U256::zero()]; - let stack_with_kernel = run_with_kernel(&kernel, exp, initial_stack)?.stack; + let stack_with_kernel = run_with_kernel(&kernel, 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; + let stack_with_opcode = run(&code, 0, initial_stack, &kernel.prover_inputs)? + .stack() + .to_vec(); assert_eq!(stack_with_kernel, stack_with_opcode); // 0 exponent let initial_stack = vec![U256::from_str("0xdeadbeef")?, U256::zero(), a]; - let stack_with_kernel = run_with_kernel(&kernel, exp, initial_stack)?.stack; + let stack_with_kernel = run_with_kernel(&kernel, 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; + let stack_with_opcode = run(&code, 0, initial_stack, &kernel.prover_inputs)? + .stack() + .to_vec(); assert_eq!(stack_with_kernel, stack_with_opcode); Ok(())