From fb732b017c887e10cd47e25ce0b94d541108a570 Mon Sep 17 00:00:00 2001 From: clabby Date: Sat, 23 Sep 2023 17:19:04 -0400 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20EVM=20Diff=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 1 + crates/mipsevm/Cargo.toml | 5 +- crates/mipsevm/src/lib.rs | 2 - crates/mipsevm/src/mips/instrumented.rs | 7 +- crates/mipsevm/src/mips/mips_vm.rs | 2 +- crates/mipsevm/src/{ => test_utils}/evm.rs | 118 +++++++++++++++++- .../src/{test_utils.rs => test_utils/mod.rs} | 9 +- 7 files changed, 128 insertions(+), 16 deletions(-) rename crates/mipsevm/src/{ => test_utils}/evm.rs (74%) rename crates/mipsevm/src/{test_utils.rs => test_utils/mod.rs} (76%) diff --git a/Cargo.lock b/Cargo.lock index c375225..d536c3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -402,6 +402,7 @@ dependencies = [ "alloy-primitives", "alloy-sol-types", "anyhow", + "cfg-if", "criterion", "fnv", "once_cell", diff --git a/crates/mipsevm/Cargo.toml b/crates/mipsevm/Cargo.toml index 683d349..a133f43 100644 --- a/crates/mipsevm/Cargo.toml +++ b/crates/mipsevm/Cargo.toml @@ -7,20 +7,21 @@ version.workspace = true authors.workspace = true [dependencies] +preimage-oracle = { path = "../preimage" } alloy-primitives = "0.3.3" alloy-sol-types = "0.3.2" once_cell = "1.18.0" serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.106" fnv = "1.0.7" -revm = { version = "3.3.0", features = ["no_gas_measuring"] } -preimage-oracle = { path = "../preimage" } +cfg-if = "1.0.0" # misc anyhow = "1.0.75" tracing = { version = "0.1.37", optional = true } [dev-dependencies] +revm = { version = "3.3.0", features = ["no_gas_measuring"] } rand = "0.8.5" criterion = { version = "0.5.1", features = ["html_reports"] } diff --git a/crates/mipsevm/src/lib.rs b/crates/mipsevm/src/lib.rs index 0a46a4d..43cf7a1 100644 --- a/crates/mipsevm/src/lib.rs +++ b/crates/mipsevm/src/lib.rs @@ -27,7 +27,5 @@ pub use types::{Address, Fd, Gindex, Page, PageIndex, StateWitness, VMStatus}; mod mips; pub use mips::InstrumentedState; -mod evm; - #[cfg(test)] mod test_utils; diff --git a/crates/mipsevm/src/mips/instrumented.rs b/crates/mipsevm/src/mips/instrumented.rs index 234fcc0..82f81a9 100644 --- a/crates/mipsevm/src/mips/instrumented.rs +++ b/crates/mipsevm/src/mips/instrumented.rs @@ -107,14 +107,9 @@ where mod test { use crate::test_utils::StaticOracle; - /// Used in tests to write the results to - const BASE_ADDR_END: u32 = 0xBF_FF_FF_F0; - - /// Used as the return-address for tests - const END_ADDR: u32 = 0xA7_EF_00_D0; - mod open_mips { use super::*; + use crate::test_utils::{BASE_ADDR_END, END_ADDR}; use crate::{Address, InstrumentedState, Memory, State}; use std::{ cell::RefCell, diff --git a/crates/mipsevm/src/mips/mips_vm.rs b/crates/mipsevm/src/mips/mips_vm.rs index 5059b5d..ec70bea 100644 --- a/crates/mipsevm/src/mips/mips_vm.rs +++ b/crates/mipsevm/src/mips/mips_vm.rs @@ -60,7 +60,7 @@ where /// - A [Result] indicating if the operation was successful. pub(crate) fn track_mem_access(&mut self, effective_address: Address) -> Result<()> { if self.mem_proof_enabled && self.last_mem_access != effective_address { - if self.last_mem_access != Address::MAX { + if self.last_mem_access as u32 != u32::MAX { anyhow::bail!("Unexpected diffrent memory access at {:x}, already have access at {:x} buffered", effective_address, self.last_mem_access); } diff --git a/crates/mipsevm/src/evm.rs b/crates/mipsevm/src/test_utils/evm.rs similarity index 74% rename from crates/mipsevm/src/evm.rs rename to crates/mipsevm/src/test_utils/evm.rs index 394632a..905c0b9 100644 --- a/crates/mipsevm/src/evm.rs +++ b/crates/mipsevm/src/test_utils/evm.rs @@ -19,10 +19,10 @@ pub const MIPS_ADDR: [u8; 20] = hex!("000000000000000000000000000000000000C0DE") pub const PREIMAGE_ORACLE_ADDR: [u8; 20] = hex!("00000000000000000000000000000000424f4f4b"); /// The creation EVM bytecode of the MIPS contract. Does not include constructor arguments. -pub const MIPS_CREATION_CODE: &str = include_str!("../bindings/mips_creation.bin"); +pub const MIPS_CREATION_CODE: &str = include_str!("../../bindings/mips_creation.bin"); /// The deployed EVM bytecode of the PreimageOracle contract. pub const PREIMAGE_ORACLE_DEPLOYED_CODE: &str = - include_str!("../bindings/preimage_oracle_deployed.bin"); + include_str!("../../bindings/preimage_oracle_deployed.bin"); /// A wrapper around a [revm] inspector with an in-memory backend that has the MIPS & PreimageOracle /// smart contracts deployed at deterministic addresses. This is used for differential testing the @@ -104,7 +104,7 @@ impl MipsEVM> { /// ### Returns /// - A [Result] containing the post-state hash of the MIPS VM or an error returned during /// execution. - pub fn step(&mut self, witness: StepWitness) -> Result { + pub fn step(&mut self, witness: StepWitness) -> Result { if witness.has_preimage() { crate::debug!( target: "mipsevm::evm", @@ -162,7 +162,7 @@ impl MipsEVM> { ); } - Ok(output) + Ok(post_state) } else { anyhow::bail!("Failed to step MIPS contract"); } @@ -212,7 +212,18 @@ impl MipsEVM> { #[cfg(test)] mod test { use super::*; + use crate::{ + test_utils::{StaticOracle, BASE_ADDR_END, END_ADDR}, + Address, InstrumentedState, Memory, State, + }; use revm::primitives::ExecutionResult; + use std::{ + cell::RefCell, + fs, + io::{self, BufReader}, + path::PathBuf, + rc::Rc, + }; #[test] fn sanity_evm_execution() { @@ -247,4 +258,103 @@ mod test { )) ); } + + #[test] + fn evm() { + // todo + let mut mips_evm = MipsEVM::new(); + mips_evm.try_init().unwrap(); + + let tests_path = PathBuf::from(std::env::current_dir().unwrap()) + .join("open_mips_tests") + .join("test") + .join("bin"); + let test_files = fs::read_dir(tests_path).unwrap(); + + for f in test_files.into_iter() { + if let Ok(f) = f { + let file_name = String::from(f.file_name().to_str().unwrap()); + println!(" -> Running test: {file_name}"); + + if file_name.starts_with("oracle") { + println!("skipping oracle test"); + continue; + } + + // Short circuit early for `exit_group.bin` + let exit_group = file_name == "exit_group.bin"; + + let program_mem = fs::read(f.path()).unwrap(); + + let mut state = { + let mut state = State::default(); + state.pc = 0; + state.next_pc = 4; + state.memory = Rc::new(RefCell::new(Memory::default())); + state + }; + state + .memory + .borrow_mut() + .set_memory_range(0, BufReader::new(program_mem.as_slice())) + .unwrap(); + + // Set the return address ($ra) to jump into when the test completes. + state.registers[31] = END_ADDR; + + let mut instrumented = InstrumentedState::new( + state, + StaticOracle::new(b"hello world".to_vec()), + io::stdout(), + io::stderr(), + ); + + for _ in 0..1000 { + if instrumented.state.pc == END_ADDR { + break; + } + if exit_group && instrumented.state.exited { + break; + } + + let instruction = instrumented + .state + .memory + .borrow_mut() + .get_memory(instrumented.state.pc as Address) + .unwrap(); + println!( + "{}", + format!( + "step: {} pc: 0x{:08x} insn: 0x{:08x}", + instrumented.state.step, instrumented.state.pc, instruction + ) + ); + + let step_witness = instrumented.step(true).unwrap().unwrap(); + + // Verify that the post state matches + let evm_post = mips_evm.step(step_witness).unwrap(); + let rust_post = instrumented.state.encode_witness().unwrap(); + + assert_eq!(evm_post, rust_post); + } + + if exit_group { + assert_ne!(END_ADDR, instrumented.state.pc, "must not reach end"); + assert!(instrumented.state.exited, "must exit"); + assert_eq!(1, instrumented.state.exit_code, "must exit with 1"); + } else { + assert_eq!(END_ADDR, instrumented.state.pc, "must reach end"); + let mut state = instrumented.state.memory.borrow_mut(); + let (done, result) = ( + state.get_memory((BASE_ADDR_END + 4) as Address).unwrap(), + state.get_memory((BASE_ADDR_END + 8) as Address).unwrap(), + ); + assert_eq!(done, 1, "must set done to 1"); + assert_eq!(result, 1, "must have success result {:?}", f.file_name()); + } + } + } + } } diff --git a/crates/mipsevm/src/test_utils.rs b/crates/mipsevm/src/test_utils/mod.rs similarity index 76% rename from crates/mipsevm/src/test_utils.rs rename to crates/mipsevm/src/test_utils/mod.rs index a07ed5f..d83e5b3 100644 --- a/crates/mipsevm/src/test_utils.rs +++ b/crates/mipsevm/src/test_utils/mod.rs @@ -1,9 +1,16 @@ //! Testing utilities. +use crate::PreimageOracle; use alloy_primitives::B256; use preimage_oracle::{Keccak256Key, Key}; -use crate::PreimageOracle; +pub mod evm; + +/// Used in tests to write the results to +pub const BASE_ADDR_END: u32 = 0xBF_FF_FF_F0; + +/// Used as the return-address for tests +pub const END_ADDR: u32 = 0xA7_EF_00_D0; pub struct StaticOracle { preimage_data: Vec, From 84ba1ee10d1e4250d25da3ec151d79633e2b3d71 Mon Sep 17 00:00:00 2001 From: clabby Date: Sat, 23 Sep 2023 18:05:40 -0400 Subject: [PATCH 2/4] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Make=20`Address`=2032?= =?UTF-8?q?=20bits?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/mipsevm/src/memory.rs | 30 ++++++++++++------------- crates/mipsevm/src/mips/instrumented.rs | 3 +-- crates/mipsevm/src/mips/mips_vm.rs | 24 ++++++++------------ crates/mipsevm/src/page.rs | 2 +- crates/mipsevm/src/types.rs | 4 ++-- 5 files changed, 28 insertions(+), 35 deletions(-) diff --git a/crates/mipsevm/src/memory.rs b/crates/mipsevm/src/memory.rs index a2679ad..fb981ff 100644 --- a/crates/mipsevm/src/memory.rs +++ b/crates/mipsevm/src/memory.rs @@ -61,13 +61,13 @@ impl Memory { } // Find the page and invalidate the address within it. - match self.page_lookup(address >> page::PAGE_ADDRESS_SIZE) { + match self.page_lookup(address as u64 >> page::PAGE_ADDRESS_SIZE) { Some(page) => { let mut page = page.borrow_mut(); let prev_valid = !page.is_valid(1); // Invalidate the address within the page. - page.invalidate(address & page::PAGE_ADDRESS_MASK as u64)?; + page.invalidate(address & page::PAGE_ADDRESS_MASK as u32)?; // If the page was already invalid before, then nodes to the memory // root will also still be invalid. @@ -82,7 +82,7 @@ impl Memory { } // Find the generalized index of the first page covering the address - let mut g_index = ((1u64 << 32) | address) >> page::PAGE_ADDRESS_SIZE as u64; + let mut g_index = ((1u64 << 32) | address as u64) >> page::PAGE_ADDRESS_SIZE; // Invalidate all nodes in the branch while g_index > 0 { self.nodes.insert(g_index, None); @@ -236,7 +236,7 @@ impl Memory { anyhow::bail!("Unaligned memory access: {:x}", address); } - let page_index = address >> page::PAGE_ADDRESS_SIZE as u64; + let page_index = address as PageIndex >> page::PAGE_ADDRESS_SIZE as u64; let page_address = address as usize & page::PAGE_ADDRESS_MASK; // Attempt to look up the page. @@ -271,7 +271,7 @@ impl Memory { anyhow::bail!("Unaligned memory access: {:x}", address); } - match self.page_lookup(address >> page::PAGE_ADDRESS_SIZE as u64) { + match self.page_lookup(address as u64 >> page::PAGE_ADDRESS_SIZE as u64) { Some(page) => { let page_address = address as usize & page::PAGE_ADDRESS_MASK; Ok(u32::from_be_bytes( @@ -313,7 +313,7 @@ impl Memory { let mut address = address; let mut data = data; loop { - let page_index = address >> page::PAGE_ADDRESS_SIZE as u64; + let page_index = address as PageIndex >> page::PAGE_ADDRESS_SIZE as u64; let page_address = address as usize & page::PAGE_ADDRESS_MASK; let page = self @@ -327,7 +327,7 @@ impl Memory { if n == 0 { return Ok(()); } - address += n as u64; + address += n as u32; } Err(e) => return Err(e.into()), }; @@ -338,11 +338,11 @@ impl Memory { pub struct MemoryReader { memory: Rc>, address: Address, - count: u64, + count: u32, } impl MemoryReader { - pub fn new(memory: Rc>, address: Address, count: u64) -> Self { + pub fn new(memory: Rc>, address: Address, count: u32) -> Self { Self { memory, address, @@ -357,13 +357,13 @@ impl Read for MemoryReader { return Ok(0); } - let end_address = self.address + self.count; + let end_address = self.address + self.count as Address; - let page_index = self.address >> page::PAGE_ADDRESS_SIZE as u64; + let page_index = self.address as PageIndex >> page::PAGE_ADDRESS_SIZE as u64; let start = self.address as usize & page::PAGE_ADDRESS_MASK; let mut end = page::PAGE_SIZE; - if page_index == (end_address >> page::PAGE_ADDRESS_SIZE as u64) { + if page_index == (end_address as u64 >> page::PAGE_ADDRESS_SIZE as u64) { end = end_address as usize & page::PAGE_ADDRESS_MASK; } let n = end - start; @@ -377,8 +377,8 @@ impl Read for MemoryReader { std::io::copy(&mut vec![0; n].as_slice(), &mut buf)?; } }; - self.address += n as u64; - self.count -= n as u64; + self.address += n as u32; + self.count -= n as u32; Ok(n) } } @@ -578,7 +578,7 @@ mod test { .expect("Should not error"); let mut reader = - MemoryReader::new(Rc::clone(&memory), 0x1337 - 10, data.len() as u64 + 20); + MemoryReader::new(Rc::clone(&memory), 0x1337 - 10, data.len() as u32 + 20); let mut buf = Vec::with_capacity(1260); reader.read_to_end(&mut buf).unwrap(); diff --git a/crates/mipsevm/src/mips/instrumented.rs b/crates/mipsevm/src/mips/instrumented.rs index 82f81a9..ea40895 100644 --- a/crates/mipsevm/src/mips/instrumented.rs +++ b/crates/mipsevm/src/mips/instrumented.rs @@ -66,7 +66,7 @@ where /// - Err(_): An error occurred while processing the instruction step in the MIPS emulator. pub fn step(&mut self, proof: bool) -> Result> { self.mem_proof_enabled = proof; - self.last_mem_access = !0u32 as u64; + self.last_mem_access = !0u32 as Address; self.last_preimage_offset = !0u32; let mut witness = None; @@ -120,7 +120,6 @@ mod test { }; #[test] - // #[ignore] fn open_mips_tests() { let tests_path = PathBuf::from(std::env::current_dir().unwrap()) .join("open_mips_tests") diff --git a/crates/mipsevm/src/mips/mips_vm.rs b/crates/mipsevm/src/mips/mips_vm.rs index ec70bea..4330326 100644 --- a/crates/mipsevm/src/mips/mips_vm.rs +++ b/crates/mipsevm/src/mips/mips_vm.rs @@ -31,8 +31,8 @@ where /// - `Err(_)`: An error occurred while fetching the preimage. pub(crate) fn read_preimage(&mut self, key: B256, offset: u32) -> Result<(B256, usize)> { if key != self.last_preimage_key { - self.last_preimage_key = key; let data = self.preimage_oracle.get(key)?; + self.last_preimage_key = key; // Add the length prefix to the preimage // Resizes the `last_preimage` vec in-place to reduce reallocations. @@ -60,7 +60,7 @@ where /// - A [Result] indicating if the operation was successful. pub(crate) fn track_mem_access(&mut self, effective_address: Address) -> Result<()> { if self.mem_proof_enabled && self.last_mem_access != effective_address { - if self.last_mem_access as u32 != u32::MAX { + if self.last_mem_access != Address::MAX { anyhow::bail!("Unexpected diffrent memory access at {:x}, already have access at {:x} buffered", effective_address, self.last_mem_access); } @@ -214,7 +214,7 @@ where let mut v0 = 0; let mut v1 = 0; - let (a0, a1, a2) = ( + let (a0, a1, mut a2) = ( self.state.registers[4], self.state.registers[5], self.state.registers[6], @@ -256,6 +256,7 @@ where // Nothing to do; Leave v0 and v1 zero, read nothing, and give no error. } Ok(Fd::PreimageRead) => { + dbg!("moose"); let effective_address = (a1 & 0xFFFFFFFC) as Address; self.track_mem_access(effective_address)?; @@ -298,11 +299,8 @@ where }, Syscall::Write => match (a0 as u8).try_into() { Ok(fd @ (Fd::Stdout | Fd::StdErr)) => { - let mut reader = MemoryReader::new( - Rc::clone(&self.state.memory), - a1 as Address, - a2 as u64, - ); + let mut reader = + MemoryReader::new(Rc::clone(&self.state.memory), a1 as Address, a2); let writer: &mut dyn Write = if matches!(fd, Fd::Stdout) { &mut self.std_out } else { @@ -312,14 +310,11 @@ where v0 = a2; } Ok(Fd::HintWrite) => { - let mut reader = MemoryReader::new( - Rc::clone(&self.state.memory), - a1 as Address, - a2 as u64, - ); + let mut reader = + MemoryReader::new(Rc::clone(&self.state.memory), a1 as Address, a2); let mut hint_data = Vec::with_capacity(a2 as usize); reader.read_to_end(&mut hint_data)?; - self.state.last_hint.extend_from_slice(hint_data.as_slice()); + self.state.last_hint.extend(hint_data); // Continue processing while there is enough data to check if there are any // hints. @@ -351,7 +346,6 @@ where let alignment = a1 & 0x3; let space = 4 - alignment; - let mut a2 = a2; if space < a2 { a2 = space; } diff --git a/crates/mipsevm/src/page.rs b/crates/mipsevm/src/page.rs index 1b7f51f..7749281 100644 --- a/crates/mipsevm/src/page.rs +++ b/crates/mipsevm/src/page.rs @@ -51,7 +51,7 @@ impl CachedPage { /// ### Returns /// - A [Result] indicating if the operation was successful. pub fn invalidate(&mut self, page_addr: Address) -> Result<()> { - if page_addr >= PAGE_SIZE as u64 { + if page_addr >= PAGE_SIZE as Address { anyhow::bail!("Invalid page address: {}", page_addr); } diff --git a/crates/mipsevm/src/types.rs b/crates/mipsevm/src/types.rs index fb4aed9..d173d81 100644 --- a/crates/mipsevm/src/types.rs +++ b/crates/mipsevm/src/types.rs @@ -12,8 +12,8 @@ pub type PageIndex = u64; /// A [Gindex] is a generalized index, defined as $2^{\text{depth}} + \text{index}$. pub type Gindex = u64; -/// An [Address] is a 64 bit address in the MIPS emulator's memory. -pub type Address = u64; +/// An [Address] is a 32 bit address in the MIPS emulator's memory. +pub type Address = u32; /// The [VMStatus] is an indicator within the [StateWitness] hash that indicates /// the current status of the MIPS emulator. From 40ddd86f66b13454b05099e876709a57e4703f81 Mon Sep 17 00:00:00 2001 From: clabby Date: Sat, 23 Sep 2023 18:56:34 -0400 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20Add=20oracle=20test?= =?UTF-8?q?s=20to=20EVM=20diff=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 1 - crates/mipsevm/Cargo.toml | 1 - crates/mipsevm/src/mips/mips_vm.rs | 1 - crates/mipsevm/src/test_utils/evm.rs | 8 ++------ crates/mipsevm/src/witness.rs | 11 +---------- 5 files changed, 3 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d536c3f..c375225 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -402,7 +402,6 @@ dependencies = [ "alloy-primitives", "alloy-sol-types", "anyhow", - "cfg-if", "criterion", "fnv", "once_cell", diff --git a/crates/mipsevm/Cargo.toml b/crates/mipsevm/Cargo.toml index a133f43..eca9d6f 100644 --- a/crates/mipsevm/Cargo.toml +++ b/crates/mipsevm/Cargo.toml @@ -14,7 +14,6 @@ once_cell = "1.18.0" serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.106" fnv = "1.0.7" -cfg-if = "1.0.0" # misc anyhow = "1.0.75" diff --git a/crates/mipsevm/src/mips/mips_vm.rs b/crates/mipsevm/src/mips/mips_vm.rs index 4330326..58e0650 100644 --- a/crates/mipsevm/src/mips/mips_vm.rs +++ b/crates/mipsevm/src/mips/mips_vm.rs @@ -256,7 +256,6 @@ where // Nothing to do; Leave v0 and v1 zero, read nothing, and give no error. } Ok(Fd::PreimageRead) => { - dbg!("moose"); let effective_address = (a1 & 0xFFFFFFFC) as Address; self.track_mem_access(effective_address)?; diff --git a/crates/mipsevm/src/test_utils/evm.rs b/crates/mipsevm/src/test_utils/evm.rs index 905c0b9..bc93b86 100644 --- a/crates/mipsevm/src/test_utils/evm.rs +++ b/crates/mipsevm/src/test_utils/evm.rs @@ -67,7 +67,8 @@ impl MipsEVM> { // Deploy the MIPS contract prior to deploying it manually. This contract has an immutable // variable, so we let the creation code fill this in for us, and then deploy it to the // test address. - let encoded_preimage_addr = Address::from_slice(MIPS_ADDR.as_slice()).into_word(); + let encoded_preimage_addr = + Address::from_slice(PREIMAGE_ORACLE_ADDR.as_slice()).into_word(); let mips_creation_heap = hex::decode(MIPS_CREATION_CODE)? .into_iter() .chain(encoded_preimage_addr) @@ -276,11 +277,6 @@ mod test { let file_name = String::from(f.file_name().to_str().unwrap()); println!(" -> Running test: {file_name}"); - if file_name.starts_with("oracle") { - println!("skipping oracle test"); - continue; - } - // Short circuit early for `exit_group.bin` let exit_group = file_name == "exit_group.bin"; diff --git a/crates/mipsevm/src/witness.rs b/crates/mipsevm/src/witness.rs index b3a80fa..3bd698c 100644 --- a/crates/mipsevm/src/witness.rs +++ b/crates/mipsevm/src/witness.rs @@ -115,17 +115,8 @@ impl StepWitness { /// ### Returns /// - The ABI encoded input to the MIPS step function. pub fn encode_step_input(&self) -> Bytes { - let mut abi_state_len = self.state.len(); - if abi_state_len % 32 != 0 { - abi_state_len += 32 - (abi_state_len % 32); - } - - // Pad state to 32 byte multiple per ABI - let mut abi_state = vec![0u8; abi_state_len]; - abi_state[..self.state.len()].copy_from_slice(&self.state); - let call = stepCall { - _0: abi_state, + _0: self.state.to_vec(), _1: self.mem_proof.clone(), }; From 7a4b93372c88f96f3fe6ae0e312c94f326b3d165 Mon Sep 17 00:00:00 2001 From: clabby Date: Sat, 23 Sep 2023 19:13:22 -0400 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20Add=20rest=20of=20E?= =?UTF-8?q?VM=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/mipsevm/src/state.rs | 2 +- crates/mipsevm/src/test_utils/evm.rs | 96 +++++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/crates/mipsevm/src/state.rs b/crates/mipsevm/src/state.rs index 3e3c4c2..79e2be6 100644 --- a/crates/mipsevm/src/state.rs +++ b/crates/mipsevm/src/state.rs @@ -10,7 +10,7 @@ use anyhow::Result; /// /// The [State] by itself does not contain functionality for performing instruction steps /// or executing the MIPS emulator. For this, use the [crate::InstrumentedState] struct. -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct State { /// The [Memory] of the emulated MIPS thread context. pub memory: Rc>, diff --git a/crates/mipsevm/src/test_utils/evm.rs b/crates/mipsevm/src/test_utils/evm.rs index bc93b86..2e497a9 100644 --- a/crates/mipsevm/src/test_utils/evm.rs +++ b/crates/mipsevm/src/test_utils/evm.rs @@ -262,7 +262,6 @@ mod test { #[test] fn evm() { - // todo let mut mips_evm = MipsEVM::new(); mips_evm.try_init().unwrap(); @@ -353,4 +352,99 @@ mod test { } } } + + #[test] + fn evm_single_step() { + let mut mips_evm = MipsEVM::new(); + mips_evm.try_init().unwrap(); + + let cases = [ + ("j MSB set target", 0, 4, 0x0A_00_00_02), + ( + "j non-zero PC region", + 0x10_00_00_00, + 0x10_00_00_04, + 0x08_00_00_02, + ), + ("jal MSB set target", 0, 4, 0x0E_00_00_02), + ( + "jal non-zero PC region", + 0x10_00_00_00, + 0x10_00_00_04, + 0x0C_00_00_02, + ), + ]; + + for (name, pc, next_pc, instruction) in cases { + println!(" -> Running test: {name}"); + + let mut state = State::default(); + state.pc = pc; + state.next_pc = next_pc; + state + .memory + .borrow_mut() + .set_memory(pc, instruction) + .unwrap(); + + let mut instrumented = InstrumentedState::new( + state, + StaticOracle::new(b"hello world".to_vec()), + io::stdout(), + io::stderr(), + ); + let step_witness = instrumented.step(true).unwrap().unwrap(); + + let evm_post = mips_evm.step(step_witness).unwrap(); + let rust_post = instrumented.state.encode_witness().unwrap(); + + assert_eq!(evm_post, rust_post); + } + } + + #[test] + fn evm_fault() { + let mut mips_evm = MipsEVM::new(); + mips_evm.try_init().unwrap(); + + let cases = [ + ("illegal instruction", 0, 0xFF_FF_FF_FFu32), + ("branch in delay slot", 8, 0x11_02_00_03), + ("jump in delay slot", 8, 0x0c_00_00_0c), + ]; + + for (name, next_pc, instruction) in cases { + println!(" -> Running test: {name}"); + + let mut state = State::default(); + state.next_pc = next_pc; + let mut initial_state = state.clone(); + state + .memory + .borrow_mut() + .set_memory(0, instruction) + .unwrap(); + + // Set the return address ($ra) to jump to when the test completes. + state.registers[31] = END_ADDR; + + let mut instrumented = InstrumentedState::new( + state, + StaticOracle::new(b"hello world".to_vec()), + io::stdout(), + io::stderr(), + ); + assert!(instrumented.step(true).is_err()); + + let instruction_proof = initial_state.memory.borrow_mut().merkle_proof(0).unwrap(); + let step_witness = StepWitness { + state: initial_state.encode_witness().unwrap(), + mem_proof: instruction_proof.to_vec(), + preimage_key: alloy_primitives::B256::ZERO, + preimage_value: Vec::default(), + preimage_offset: 0, + }; + assert!(mips_evm.step(step_witness).is_err()); + } + } }