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 3416d12..f88bf37 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 = "0.1.37" [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 afb30d9..130598f 100644 --- a/crates/mipsevm/src/lib.rs +++ b/crates/mipsevm/src/lib.rs @@ -25,7 +25,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 75% rename from crates/mipsevm/src/evm.rs rename to crates/mipsevm/src/test_utils/evm.rs index 1779b84..6c81241 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() { #[cfg(feature = "tracing")] tracing::info!( @@ -172,7 +172,7 @@ impl MipsEVM> { ); } - Ok(output) + Ok(post_state) } else { anyhow::bail!("Failed to step MIPS contract"); } @@ -222,7 +222,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() { @@ -257,4 +268,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,