From ee979428f4c359927a95007141dfbb4365a6dfba Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Sat, 23 Jul 2022 15:35:48 +0200 Subject: [PATCH 1/6] Start implementing context and segments in interpreter --- evm/src/cpu/kernel/interpreter.rs | 83 +++++++++++++++++++++++++++---- evm/src/generation/memory.rs | 4 +- evm/src/generation/mod.rs | 2 +- 3 files changed, 76 insertions(+), 13 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index f2fb276a..9e39fb4f 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -2,6 +2,9 @@ use anyhow::{anyhow, bail}; use ethereum_types::{BigEndianHash, U256, U512}; use keccak_hash::keccak; +use crate::generation::memory::MemoryContextState; +use crate::memory::segments::Segment; + /// Halt interpreter execution whenever a jump to this offset is done. const HALT_OFFSET: usize = 0xdeadbeef; @@ -49,12 +52,36 @@ impl EvmMemory { } } +#[derive(Debug)] +pub(crate) struct ContextMemory { + memory: Vec, +} + +impl Default for ContextMemory { + fn default() -> Self { + Self { + memory: vec![MemoryContextState::default()], + } + } +} + +impl ContextMemory { + fn mload_general(&self, context: usize, segment: Segment, offset: usize) -> U256 { + self.memory[context].segments[segment as usize].get(offset) + } + + fn mstore_general(&mut self, context: usize, segment: Segment, offset: usize, value: U256) { + self.memory[context].segments[segment as usize].set(offset, value) + } +} + pub(crate) struct Interpreter<'a> { code: &'a [u8], jumpdests: Vec, offset: usize, pub(crate) stack: Vec, - pub(crate) memory: EvmMemory, + context: usize, + memory: ContextMemory, /// Non-deterministic prover inputs, stored backwards so that popping the last item gives the /// next prover input. prover_inputs: Vec, @@ -83,7 +110,8 @@ pub(crate) fn run_with_input( jumpdests: find_jumpdests(code), offset: initial_offset, stack: initial_stack, - memory: EvmMemory::default(), + context: 0, + memory: ContextMemory::default(), prover_inputs, running: true, }; @@ -203,8 +231,8 @@ impl<'a> Interpreter<'a> { 0xf3 => todo!(), // "RETURN", 0xf4 => todo!(), // "DELEGATECALL", 0xf5 => todo!(), // "CREATE2", - 0xf6 => todo!(), // "GET_CONTEXT", - 0xf7 => todo!(), // "SET_CONTEXT", + 0xf6 => self.run_get_context(), // "GET_CONTEXT", + 0xf7 => self.run_set_context(), // "SET_CONTEXT", 0xf8 => todo!(), // "CONSUME_GAS", 0xf9 => todo!(), // "EXIT_KERNEL", 0xfa => todo!(), // "STATICCALL", @@ -330,7 +358,11 @@ impl<'a> Interpreter<'a> { let offset = self.pop().as_usize(); let size = self.pop().as_usize(); let bytes = (offset..offset + size) - .map(|i| self.memory.mload8(i)) + .map(|i| { + self.memory + .mload_general(self.context, Segment::MainMemory, i) + .byte(0) + }) .collect::>(); let hash = keccak(bytes); self.push(hash.into_uint()); @@ -351,20 +383,42 @@ impl<'a> Interpreter<'a> { fn run_mload(&mut self) { let offset = self.pop(); - let value = self.memory.mload(offset.as_usize()); + let value = U256::from_big_endian( + &(0..32) + .map(|i| { + self.memory + .mload_general(self.context, Segment::MainMemory, offset.as_usize() + i) + .byte(0) + }) + .collect::>(), + ); self.push(value); } fn run_mstore(&mut self) { let offset = self.pop(); let value = self.pop(); - self.memory.mstore(offset.as_usize(), value); + let mut bytes = [0; 32]; + value.to_big_endian(&mut bytes); + for i in 0..32 { + self.memory.mstore_general( + self.context, + Segment::MainMemory, + offset.as_usize() + i, + bytes[i].into(), + ); + } } fn run_mstore8(&mut self) { let offset = self.pop(); let value = self.pop(); - self.memory.mstore8(offset.as_usize(), value); + self.memory.mstore_general( + self.context, + Segment::MainMemory, + offset.as_usize(), + value.byte(0).into(), + ); } fn run_jump(&mut self) { @@ -404,6 +458,15 @@ impl<'a> Interpreter<'a> { let len = self.stack.len(); self.stack.swap(len - 1, len - n as usize - 1); } + + fn run_get_context(&mut self) { + self.push(self.context.into()); + } + + fn run_set_context(&mut self) { + let x = self.pop(); + self.context = x.as_usize(); + } } /// Return the (ordered) JUMPDEST offsets in the code. @@ -424,7 +487,7 @@ fn find_jumpdests(code: &[u8]) -> Vec { #[cfg(test)] mod tests { - use hex_literal::hex; + // use hex_literal::hex; use crate::cpu::kernel::interpreter::{run, Interpreter}; @@ -459,7 +522,7 @@ mod tests { let run = run(&code, 0, vec![])?; let Interpreter { stack, memory, .. } = run; assert_eq!(stack, vec![0xff.into(), 0xff00.into()]); - assert_eq!(&memory.memory, &hex!("00000000000000000000000000000000000000000000000000000000000000ff0000000000000042000000000000000000000000000000000000000000000000")); + // assert_eq!(&memory.memory, &hex!("00000000000000000000000000000000000000000000000000000000000000ff0000000000000042000000000000000000000000000000000000000000000000")); Ok(()) } } diff --git a/evm/src/generation/memory.rs b/evm/src/generation/memory.rs index 60bfe794..5e2919a4 100644 --- a/evm/src/generation/memory.rs +++ b/evm/src/generation/memory.rs @@ -34,14 +34,14 @@ pub(crate) struct MemorySegmentState { } impl MemorySegmentState { - pub(super) fn get(&self, virtual_addr: usize) -> U256 { + pub(crate) fn get(&self, virtual_addr: usize) -> U256 { self.content .get(virtual_addr) .copied() .unwrap_or(U256::zero()) } - pub(super) fn set(&mut self, virtual_addr: usize, value: U256) { + pub(crate) fn set(&mut self, virtual_addr: usize, value: U256) { if virtual_addr >= self.content.len() { self.content.resize(virtual_addr + 1, U256::zero()); } diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index b8ab0376..02c91d16 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -10,7 +10,7 @@ use crate::cpu::columns::NUM_CPU_COLUMNS; use crate::generation::state::GenerationState; use crate::util::trace_rows_to_poly_values; -mod memory; +pub(crate) mod memory; pub(crate) mod state; /// A piece of data which has been encoded using Recursive Length Prefix (RLP) serialization. From 715c350ee86fef62534b035d37c9f0b17b26f777 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 25 Jul 2022 10:34:18 +0200 Subject: [PATCH 2/6] Implement mload/store_general --- evm/src/cpu/kernel/interpreter.rs | 78 ++++++++++++------------------- evm/src/memory/segments.rs | 2 + 2 files changed, 33 insertions(+), 47 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 9e39fb4f..fec42351 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -8,50 +8,6 @@ use crate::memory::segments::Segment; /// Halt interpreter execution whenever a jump to this offset is done. const HALT_OFFSET: usize = 0xdeadbeef; -#[derive(Debug, Default)] -pub(crate) struct EvmMemory { - memory: Vec, -} - -impl EvmMemory { - fn len(&self) -> usize { - self.memory.len() - } - - /// Expand memory until `self.len() >= offset`. - fn expand(&mut self, offset: usize) { - while self.len() < offset { - self.memory.extend([0; 32]); - } - } - - fn mload(&mut self, offset: usize) -> U256 { - self.expand(offset + 32); - U256::from_big_endian(&self.memory[offset..offset + 32]) - } - - fn mload8(&mut self, offset: usize) -> u8 { - self.expand(offset + 1); - self.memory[offset] - } - - fn mstore(&mut self, offset: usize, value: U256) { - self.expand(offset + 32); - let value_be = { - let mut tmp = [0; 32]; - value.to_big_endian(&mut tmp); - tmp - }; - self.memory[offset..offset + 32].copy_from_slice(&value_be); - } - - fn mstore8(&mut self, offset: usize, value: U256) { - self.expand(offset + 1); - let value_byte = value.0[0] as u8; - self.memory[offset] = value_byte; - } -} - #[derive(Debug)] pub(crate) struct ContextMemory { memory: Vec, @@ -236,8 +192,8 @@ impl<'a> Interpreter<'a> { 0xf8 => todo!(), // "CONSUME_GAS", 0xf9 => todo!(), // "EXIT_KERNEL", 0xfa => todo!(), // "STATICCALL", - 0xfb => todo!(), // "MLOAD_GENERAL", - 0xfc => todo!(), // "MSTORE_GENERAL", + 0xfb => self.run_mload_general(), // "MLOAD_GENERAL", + 0xfc => self.run_mstore_general(), // "MSTORE_GENERAL", 0xfd => todo!(), // "REVERT", 0xfe => bail!("Executed INVALID"), // "INVALID", 0xff => todo!(), // "SELFDESTRUCT", @@ -467,6 +423,26 @@ impl<'a> Interpreter<'a> { let x = self.pop(); self.context = x.as_usize(); } + + fn run_mload_general(&mut self) { + let context = self.pop().as_usize(); + let segment = self.pop().as_usize(); + let offset = self.pop().as_usize(); + let value = self + .memory + .mload_general(context, Segment::all()[segment], offset); + self.push(value); + } + + fn run_mstore_general(&mut self) { + // stack: context, segment, offset, value + let context = self.pop().as_usize(); + let segment = self.pop().as_usize(); + let offset = self.pop().as_usize(); + let value = self.pop(); + self.memory + .mstore_general(context, Segment::all()[segment], offset, value); + } } /// Return the (ordered) JUMPDEST offsets in the code. @@ -490,6 +466,7 @@ mod tests { // use hex_literal::hex; use crate::cpu::kernel::interpreter::{run, Interpreter}; + use crate::memory::segments::Segment; #[test] fn test_run() -> anyhow::Result<()> { @@ -522,7 +499,14 @@ mod tests { let run = run(&code, 0, vec![])?; let Interpreter { stack, memory, .. } = run; assert_eq!(stack, vec![0xff.into(), 0xff00.into()]); - // assert_eq!(&memory.memory, &hex!("00000000000000000000000000000000000000000000000000000000000000ff0000000000000042000000000000000000000000000000000000000000000000")); + assert_eq!( + memory.memory[0].segments[Segment::MainMemory as usize].get(0x27), + 0x42.into() + ); + assert_eq!( + memory.memory[0].segments[Segment::MainMemory as usize].get(0x1f), + 0xff.into() + ); Ok(()) } } diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index f6a67dc8..ad092d41 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -54,4 +54,6 @@ impl Segment { Segment::RlpRaw => "SEGMENT_RLP_RAW", } } + + pub(crate) } From 304299a007929c60b283033b0db4be2427d07dc7 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 25 Jul 2022 10:39:51 +0200 Subject: [PATCH 3/6] Add assert to range check memory values --- evm/src/cpu/kernel/interpreter.rs | 14 ++++++-------- evm/src/memory/segments.rs | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index fec42351..d332a830 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -426,22 +426,20 @@ impl<'a> Interpreter<'a> { fn run_mload_general(&mut self) { let context = self.pop().as_usize(); - let segment = self.pop().as_usize(); + let segment = Segment::all()[self.pop().as_usize()]; let offset = self.pop().as_usize(); - let value = self - .memory - .mload_general(context, Segment::all()[segment], offset); + let value = self.memory.mload_general(context, segment, offset); + assert!(value < U256::one() << segment.bit_range()); self.push(value); } fn run_mstore_general(&mut self) { - // stack: context, segment, offset, value let context = self.pop().as_usize(); - let segment = self.pop().as_usize(); + let segment = Segment::all()[self.pop().as_usize()]; let offset = self.pop().as_usize(); let value = self.pop(); - self.memory - .mstore_general(context, Segment::all()[segment], offset, value); + assert!(value < U256::one() << segment.bit_range()); + self.memory.mstore_general(context, segment, offset, value); } } diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index ad092d41..106f2963 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -55,5 +55,18 @@ impl Segment { } } - pub(crate) + #[allow(dead_code)] + pub(crate) fn bit_range(&self) -> usize { + match self { + Segment::Code => 8, + Segment::Stack => 256, + Segment::MainMemory => 8, + Segment::Calldata => 8, + Segment::Returndata => 8, + Segment::Metadata => 8, + Segment::KernelGeneral => 8, + Segment::TxnData => 8, + Segment::RlpRaw => 8, + } + } } From a0295f0079650b9a1e463b4437f0ce0ce1e0698f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 25 Jul 2022 11:09:41 +0200 Subject: [PATCH 4/6] Minor --- evm/src/cpu/kernel/interpreter.rs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index d332a830..d7c61f02 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -338,12 +338,12 @@ impl<'a> Interpreter<'a> { } fn run_mload(&mut self) { - let offset = self.pop(); + let offset = self.pop().as_usize(); let value = U256::from_big_endian( &(0..32) .map(|i| { self.memory - .mload_general(self.context, Segment::MainMemory, offset.as_usize() + i) + .mload_general(self.context, Segment::MainMemory, offset + i) .byte(0) }) .collect::>(), @@ -352,27 +352,23 @@ impl<'a> Interpreter<'a> { } fn run_mstore(&mut self) { - let offset = self.pop(); + let offset = self.pop().as_usize(); let value = self.pop(); let mut bytes = [0; 32]; value.to_big_endian(&mut bytes); - for i in 0..32 { - self.memory.mstore_general( - self.context, - Segment::MainMemory, - offset.as_usize() + i, - bytes[i].into(), - ); + for (i, byte) in (0..32).zip(bytes) { + self.memory + .mstore_general(self.context, Segment::MainMemory, offset + i, byte.into()); } } fn run_mstore8(&mut self) { - let offset = self.pop(); + let offset = self.pop().as_usize(); let value = self.pop(); self.memory.mstore_general( self.context, Segment::MainMemory, - offset.as_usize(), + offset, value.byte(0).into(), ); } @@ -461,8 +457,6 @@ fn find_jumpdests(code: &[u8]) -> Vec { #[cfg(test)] mod tests { - // use hex_literal::hex; - use crate::cpu::kernel::interpreter::{run, Interpreter}; use crate::memory::segments::Segment; From e8ab92b1157fb2071518e7957c504c70ac32e79d Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 27 Jul 2022 10:05:31 +0200 Subject: [PATCH 5/6] PR feedback --- evm/src/cpu/kernel/interpreter.rs | 27 ++++++++++++++------------- evm/src/memory/segments.rs | 6 +++--- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index d7c61f02..016e3c44 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -9,35 +9,36 @@ use crate::memory::segments::Segment; const HALT_OFFSET: usize = 0xdeadbeef; #[derive(Debug)] -pub(crate) struct ContextMemory { - memory: Vec, +pub(crate) struct InterpreterMemory { + context_memory: Vec, } -impl Default for ContextMemory { +impl Default for InterpreterMemory { fn default() -> Self { Self { - memory: vec![MemoryContextState::default()], + context_memory: vec![MemoryContextState::default()], } } } -impl ContextMemory { +impl InterpreterMemory { fn mload_general(&self, context: usize, segment: Segment, offset: usize) -> U256 { - self.memory[context].segments[segment as usize].get(offset) + self.context_memory[context].segments[segment as usize].get(offset) } fn mstore_general(&mut self, context: usize, segment: Segment, offset: usize, value: U256) { - self.memory[context].segments[segment as usize].set(offset, value) + self.context_memory[context].segments[segment as usize].set(offset, value) } } +// TODO: Remove `code` and `stack` fields as they are contained in `memory`. pub(crate) struct Interpreter<'a> { code: &'a [u8], jumpdests: Vec, offset: usize, pub(crate) stack: Vec, context: usize, - memory: ContextMemory, + memory: InterpreterMemory, /// Non-deterministic prover inputs, stored backwards so that popping the last item gives the /// next prover input. prover_inputs: Vec, @@ -67,7 +68,7 @@ pub(crate) fn run_with_input( offset: initial_offset, stack: initial_stack, context: 0, - memory: ContextMemory::default(), + memory: InterpreterMemory::default(), prover_inputs, running: true, }; @@ -425,7 +426,7 @@ impl<'a> Interpreter<'a> { let segment = Segment::all()[self.pop().as_usize()]; let offset = self.pop().as_usize(); let value = self.memory.mload_general(context, segment, offset); - assert!(value < U256::one() << segment.bit_range()); + assert!(value.bits() <= segment.bit_range()); self.push(value); } @@ -434,7 +435,7 @@ impl<'a> Interpreter<'a> { let segment = Segment::all()[self.pop().as_usize()]; let offset = self.pop().as_usize(); let value = self.pop(); - assert!(value < U256::one() << segment.bit_range()); + assert!(value.bits() <= segment.bit_range()); self.memory.mstore_general(context, segment, offset, value); } } @@ -492,11 +493,11 @@ mod tests { let Interpreter { stack, memory, .. } = run; assert_eq!(stack, vec![0xff.into(), 0xff00.into()]); assert_eq!( - memory.memory[0].segments[Segment::MainMemory as usize].get(0x27), + memory.context_memory[0].segments[Segment::MainMemory as usize].get(0x27), 0x42.into() ); assert_eq!( - memory.memory[0].segments[Segment::MainMemory as usize].get(0x1f), + memory.context_memory[0].segments[Segment::MainMemory as usize].get(0x1f), 0xff.into() ); Ok(()) diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index 106f2963..ba90f183 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -63,9 +63,9 @@ impl Segment { Segment::MainMemory => 8, Segment::Calldata => 8, Segment::Returndata => 8, - Segment::Metadata => 8, - Segment::KernelGeneral => 8, - Segment::TxnData => 8, + Segment::Metadata => 256, + Segment::KernelGeneral => 256, + Segment::TxnData => 256, Segment::RlpRaw => 8, } } From ac68ce62c2d27aa0edc8fabc07bd1045a7c1a3cc Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 27 Jul 2022 10:16:04 +0200 Subject: [PATCH 6/6] Merge conflicts --- evm/src/memory/segments.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index 00b4c8de..15545ea0 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -69,6 +69,7 @@ impl Segment { Segment::Returndata => 8, Segment::Metadata => 256, Segment::KernelGeneral => 256, + Segment::TxnFields => 256, Segment::TxnData => 256, Segment::RlpRaw => 8, }