diff --git a/Cargo.lock b/Cargo.lock index 1a0eb17..d03309d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3684,8 +3684,6 @@ dependencies = [ "common", "compile-utils", "jolt", - "jolt-core", - "jolt-sdk", "serde", "tempfile", "test-utils", @@ -3717,11 +3715,11 @@ version = "0.0.12" dependencies = [ "bincode 1.3.3", "build-utils", + "compile-utils", "nexus-sdk", "serde", "test-utils", "thiserror 2.0.12", - "toml 0.8.23", "tracing", "zkvm-interface", ] @@ -3742,7 +3740,6 @@ dependencies = [ "test-utils", "thiserror 2.0.12", "toml 0.8.23", - "tracing", "zkvm-interface", ] @@ -3833,7 +3830,7 @@ version = "0.0.12" dependencies = [ "bincode 1.3.3", "build-utils", - "cargo_metadata 0.19.2", + "compile-utils", "serde", "test-utils", "thiserror 2.0.12", @@ -3850,12 +3847,12 @@ dependencies = [ "blake3", "build-utils", "bytemuck", + "compile-utils", "serde", "strum 0.27.2", "tempfile", "test-utils", "thiserror 2.0.12", - "toml 0.8.23", "tracing", "zkvm-interface", ] @@ -10154,10 +10151,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" dependencies = [ "bytes", - "heck 0.5.0", - "itertools 0.12.1", + "heck 0.4.1", + "itertools 0.10.5", "log", - "multimap 0.10.1", + "multimap 0.8.3", "once_cell", "petgraph 0.6.5", "prettyplease 0.2.32", @@ -10208,7 +10205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.101", @@ -10511,9 +10508,9 @@ dependencies = [ [[package]] name = "rangemap" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" +checksum = "f93e7e49bb0bf967717f7bd674458b3d6b0c5f48ec7e3038166026a69fc22223" dependencies = [ "serde", ] @@ -12818,9 +12815,9 @@ dependencies = [ [[package]] name = "symbolic-common" -version = "12.16.0" +version = "12.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c5199e46f23c77c611aa2a383b2f72721dfee4fb2bf85979eea1e0f26ba6e35" +checksum = "d03f433c9befeea460a01d750e698aa86caf86dcfbd77d552885cd6c89d52f50" dependencies = [ "debugid", "memmap2", @@ -12830,9 +12827,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.16.0" +version = "12.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa3c03956e32254f74e461a330b9522a2689686d80481708fb2014780d8d3959" +checksum = "13d359ef6192db1760a34321ec4f089245ede4342c27e59be99642f12a859de8" dependencies = [ "cpp_demangle", "rustc-demangle", diff --git a/Cargo.toml b/Cargo.toml index bf46ebb..29f67a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,8 +64,6 @@ twirp-build = "0.9.0" ark-serialize = "0.5.0" common = { git = "https://github.com/a16z/jolt.git", rev = "55b9830a3944dde55d33a55c42522b81dd49f87a" } jolt = { git = "https://github.com/a16z/jolt.git", rev = "55b9830a3944dde55d33a55c42522b81dd49f87a" } -jolt-core = { git = "https://github.com/a16z/jolt.git", rev = "55b9830a3944dde55d33a55c42522b81dd49f87a" } -jolt-sdk = { git = "https://github.com/a16z/jolt.git", rev = "55b9830a3944dde55d33a55c42522b81dd49f87a" } # Miden dependencies miden-assembly = { git = "https://github.com/0xPolygonMiden/miden-vm.git", tag = "v0.17.1" } diff --git a/crates/ere-cli/src/main.rs b/crates/ere-cli/src/main.rs index 6bd3019..f9c40f8 100644 --- a/crates/ere-cli/src/main.rs +++ b/crates/ere-cli/src/main.rs @@ -135,31 +135,31 @@ fn main() -> Result<(), Error> { fn compile(guest_path: PathBuf, program_path: PathBuf) -> Result<(), Error> { #[cfg(feature = "jolt")] - let program = ere_jolt::JOLT_TARGET.compile(&guest_path); + let program = ere_jolt::compiler::RustRv32imaCustomized.compile(&guest_path); #[cfg(feature = "miden")] - let program = ere_miden::MIDEN_TARGET.compile(&guest_path); + let program = ere_miden::compiler::MidenAsm.compile(&guest_path); #[cfg(feature = "nexus")] - let program = ere_nexus::NEXUS_TARGET.compile(&guest_path); + let program = ere_nexus::compiler::RustRv32i.compile(&guest_path); #[cfg(feature = "openvm")] - let program = ere_openvm::OPENVM_TARGET.compile(&guest_path); + let program = ere_openvm::compiler::RustRv32imaCustomized.compile(&guest_path); #[cfg(feature = "pico")] - let program = ere_pico::PICO_TARGET.compile(&guest_path); + let program = ere_pico::compiler::RustRv32imaCustomized.compile(&guest_path); #[cfg(feature = "risc0")] - let program = ere_risc0::RV32_IM_RISC0_ZKVM_ELF.compile(&guest_path); + let program = ere_risc0::compiler::RustRv32imaCustomized.compile(&guest_path); #[cfg(feature = "sp1")] - let program = ere_sp1::RV32_IM_SUCCINCT_ZKVM_ELF.compile(&guest_path); + let program = ere_sp1::compiler::RustRv32imaCustomized.compile(&guest_path); #[cfg(feature = "ziren")] - let program = ere_ziren::MIPS32R2_ZKM_ZKVM_ELF.compile(&guest_path); + let program = ere_ziren::compiler::RustMips32r2Customized.compile(&guest_path); #[cfg(feature = "zisk")] - let program = ere_zisk::RV64_IMA_ZISK_ZKVM_ELF.compile(&guest_path); + let program = ere_zisk::compiler::RustRv64imaCustomized.compile(&guest_path); serde::write( &program_path, diff --git a/crates/ere-jolt/Cargo.toml b/crates/ere-jolt/Cargo.toml index 101a207..611b0ae 100644 --- a/crates/ere-jolt/Cargo.toml +++ b/crates/ere-jolt/Cargo.toml @@ -14,8 +14,6 @@ thiserror.workspace = true ark-serialize = { workspace = true, features = ["derive"] } common.workspace = true jolt = { workspace = true, features = ["host"] } -jolt-core = { workspace = true, features = ["host"] } -jolt-sdk = { workspace = true, features = ["host"] } # Local dependencies compile-utils.workspace = true diff --git a/crates/ere-jolt/src/compile_stock_rust.rs b/crates/ere-jolt/src/compile_stock_rust.rs deleted file mode 100644 index 0927d84..0000000 --- a/crates/ere-jolt/src/compile_stock_rust.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::error::CompileError; -use compile_utils::CargoBuildCmd; -use std::path::Path; - -const TARGET_TRIPLE: &str = "riscv32im-unknown-none-elf"; -// According to https://github.com/a16z/jolt/blob/55b9830a3944dde55d33a55c42522b81dd49f87a/jolt-core/src/host/mod.rs#L95 -const RUSTFLAGS: &[&str] = &[ - "-C", - "passes=lower-atomic", - "-C", - "panic=abort", - "-C", - "strip=symbols", - "-C", - "opt-level=z", -]; -const CARGO_BUILD_OPTIONS: &[&str] = &[ - "--features", - "guest", - // For bare metal we have to build core and alloc - "-Zbuild-std=core,alloc", -]; - -pub fn compile_jolt_program_stock_rust( - guest_directory: &Path, - toolchain: &String, -) -> Result, CompileError> { - compile_program_stock_rust(guest_directory, toolchain) -} - -fn compile_program_stock_rust( - guest_directory: &Path, - toolchain: &String, -) -> Result, CompileError> { - let elf = CargoBuildCmd::new() - .linker_script(Some(make_linker_script())) - .toolchain(toolchain) - .build_options(CARGO_BUILD_OPTIONS) - .rustflags(RUSTFLAGS) - .exec(guest_directory, TARGET_TRIPLE)?; - - Ok(elf) -} - -const DEFAULT_MEMORY_SIZE: u64 = 10 * 1024 * 1024; -const DEFAULT_STACK_SIZE: u64 = 4096; -const LINKER_SCRIPT_TEMPLATE: &str = include_str!("template.ld"); - -fn make_linker_script() -> String { - LINKER_SCRIPT_TEMPLATE - .replace("{MEMORY_SIZE}", &DEFAULT_MEMORY_SIZE.to_string()) - .replace("{STACK_SIZE}", &DEFAULT_STACK_SIZE.to_string()) -} - -#[cfg(test)] -mod tests { - use crate::compile_stock_rust::compile_jolt_program_stock_rust; - use test_utils::host::testing_guest_directory; - - #[test] - fn test_stock_compiler_impl() { - let guest_directory = testing_guest_directory("jolt", "stock_nightly_no_std"); - let result = compile_jolt_program_stock_rust(&guest_directory, &"nightly".to_string()); - assert!(result.is_ok(), "Jolt guest program compilation failure."); - assert!( - !result.unwrap().is_empty(), - "ELF bytes should not be empty." - ); - } -} diff --git a/crates/ere-jolt/src/compiler.rs b/crates/ere-jolt/src/compiler.rs new file mode 100644 index 0000000..dbcf7a1 --- /dev/null +++ b/crates/ere-jolt/src/compiler.rs @@ -0,0 +1,7 @@ +mod rust_rv32ima; +mod rust_rv32ima_customized; + +pub use rust_rv32ima::RustRv32ima; +pub use rust_rv32ima_customized::RustRv32imaCustomized; + +pub type JoltProgram = Vec; diff --git a/crates/ere-jolt/src/compiler/rust_rv32ima.rs b/crates/ere-jolt/src/compiler/rust_rv32ima.rs new file mode 100644 index 0000000..e60c4f9 --- /dev/null +++ b/crates/ere-jolt/src/compiler/rust_rv32ima.rs @@ -0,0 +1,80 @@ +use crate::{ + compiler::JoltProgram, + error::{CompileError, JoltError}, +}; +use compile_utils::CargoBuildCmd; +use std::{env, path::Path}; +use zkvm_interface::Compiler; + +const TARGET_TRIPLE: &str = "riscv32ima-unknown-none-elf"; +// According to https://github.com/a16z/jolt/blob/55b9830a3944dde55d33a55c42522b81dd49f87a/jolt-core/src/host/mod.rs#L95 +const RUSTFLAGS: &[&str] = &[ + "-C", + "passes=lower-atomic", + "-C", + "panic=abort", + "-C", + "strip=symbols", + "-C", + "opt-level=z", +]; +const CARGO_BUILD_OPTIONS: &[&str] = &[ + "--features", + "guest", + // For bare metal we have to build core and alloc + "-Zbuild-std=core,alloc", +]; + +const DEFAULT_MEMORY_SIZE: u64 = 10 * 1024 * 1024; +const DEFAULT_STACK_SIZE: u64 = 4096; +const LINKER_SCRIPT_TEMPLATE: &str = include_str!("rust_rv32ima/template.ld"); + +fn make_linker_script() -> String { + LINKER_SCRIPT_TEMPLATE + .replace("{MEMORY_SIZE}", &DEFAULT_MEMORY_SIZE.to_string()) + .replace("{STACK_SIZE}", &DEFAULT_STACK_SIZE.to_string()) +} + +/// Compiler for Rust guest program to RV32IMA architecture. +pub struct RustRv32ima; + +impl Compiler for RustRv32ima { + type Error = JoltError; + + type Program = JoltProgram; + + fn compile(&self, guest_directory: &Path) -> Result { + let toolchain = env::var("ERE_RUST_TOOLCHAIN").unwrap_or_else(|_| "nightly".into()); + let elf = CargoBuildCmd::new() + .linker_script(Some(make_linker_script())) + .toolchain(toolchain) + .build_options(CARGO_BUILD_OPTIONS) + .rustflags(RUSTFLAGS) + .exec(guest_directory, TARGET_TRIPLE) + .map_err(CompileError::CompileUtilError)?; + Ok(elf) + } +} + +#[cfg(test)] +mod tests { + use crate::{EreJolt, compiler::RustRv32ima}; + use test_utils::host::testing_guest_directory; + use zkvm_interface::{Compiler, Input, ProverResourceType, zkVM}; + + #[test] + fn test_compile() { + let guest_directory = testing_guest_directory("jolt", "stock_nightly_no_std"); + let elf = RustRv32ima.compile(&guest_directory).unwrap(); + assert!(!elf.is_empty(), "ELF bytes should not be empty."); + } + + #[test] + fn test_execute() { + let guest_directory = testing_guest_directory("jolt", "stock_nightly_no_std"); + let program = RustRv32ima.compile(&guest_directory).unwrap(); + let zkvm = EreJolt::new(program, ProverResourceType::Cpu).unwrap(); + + zkvm.execute(&Input::new()).unwrap(); + } +} diff --git a/crates/ere-jolt/src/template.ld b/crates/ere-jolt/src/compiler/rust_rv32ima/template.ld similarity index 100% rename from crates/ere-jolt/src/template.ld rename to crates/ere-jolt/src/compiler/rust_rv32ima/template.ld diff --git a/crates/ere-jolt/src/compiler/rust_rv32ima_customized.rs b/crates/ere-jolt/src/compiler/rust_rv32ima_customized.rs new file mode 100644 index 0000000..9a97d34 --- /dev/null +++ b/crates/ere-jolt/src/compiler/rust_rv32ima_customized.rs @@ -0,0 +1,68 @@ +use crate::{ + compiler::JoltProgram, + error::{CompileError, JoltError}, +}; +use compile_utils::cargo_metadata; +use jolt::host::DEFAULT_TARGET_DIR; +use std::{env::set_current_dir, fs, path::Path}; +use zkvm_interface::Compiler; + +/// Compiler for Rust guest program to RV32IMA architecture, using customized +/// Rust toolchain of Jolt. +pub struct RustRv32imaCustomized; + +impl Compiler for RustRv32imaCustomized { + type Error = JoltError; + + type Program = JoltProgram; + + fn compile(&self, guest_directory: &Path) -> Result { + // Change current directory for `Program::build` to build guest program. + set_current_dir(guest_directory).map_err(|source| CompileError::SetCurrentDirFailed { + source, + path: guest_directory.to_path_buf(), + })?; + + let metadata = cargo_metadata(guest_directory).map_err(CompileError::CompileUtilError)?; + let package_name = &metadata.root_package().unwrap().name; + + // Note that if this fails, it will panic, hence we need to catch it. + let elf_path = std::panic::catch_unwind(|| { + let mut program = jolt::host::Program::new(package_name); + program.set_std(true); + program.build(DEFAULT_TARGET_DIR); + program.elf.unwrap() + }) + .map_err(|_| CompileError::BuildFailed)?; + + let elf = fs::read(&elf_path).map_err(|source| CompileError::ReadElfFailed { + source, + path: elf_path.to_path_buf(), + })?; + + Ok(elf) + } +} + +#[cfg(test)] +mod tests { + use crate::{EreJolt, compiler::RustRv32imaCustomized}; + use test_utils::host::testing_guest_directory; + use zkvm_interface::{Compiler, Input, ProverResourceType, zkVM}; + + #[test] + fn test_compile() { + let guest_directory = testing_guest_directory("jolt", "basic"); + let elf = RustRv32imaCustomized.compile(&guest_directory).unwrap(); + assert!(!elf.is_empty(), "ELF bytes should not be empty."); + } + + #[test] + fn test_execute() { + let guest_directory = testing_guest_directory("jolt", "basic"); + let program = RustRv32imaCustomized.compile(&guest_directory).unwrap(); + let zkvm = EreJolt::new(program, ProverResourceType::Cpu).unwrap(); + + zkvm.execute(&Input::new()).unwrap(); + } +} diff --git a/crates/ere-jolt/src/error.rs b/crates/ere-jolt/src/error.rs index 3ad167c..b59d324 100644 --- a/crates/ere-jolt/src/error.rs +++ b/crates/ere-jolt/src/error.rs @@ -1,5 +1,5 @@ use ark_serialize::SerializationError; -use jolt_core::utils::errors::ProofVerifyError; +use jolt::jolt_core::utils::errors::ProofVerifyError; use std::{io, path::PathBuf}; use thiserror::Error; use zkvm_interface::zkVMError; diff --git a/crates/ere-jolt/src/lib.rs b/crates/ere-jolt/src/lib.rs index ee85492..36695d3 100644 --- a/crates/ere-jolt/src/lib.rs +++ b/crates/ere-jolt/src/lib.rs @@ -1,83 +1,29 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] -use crate::error::{CompileError, JoltError, ProveError, VerifyError}; +use crate::{ + compiler::JoltProgram, + error::{JoltError, ProveError, VerifyError}, + jolt_methods::{preprocess_prover, preprocess_verifier, prove_generic, verify_generic}, +}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use compile_stock_rust::compile_jolt_program_stock_rust; -use compile_utils::cargo_metadata; use jolt::{JoltHyperKZGProof, JoltProverPreprocessing, JoltVerifierPreprocessing}; -use jolt_core::host::Program; -use jolt_methods::{preprocess_prover, preprocess_verifier, prove_generic, verify_generic}; -use jolt_sdk::host::DEFAULT_TARGET_DIR; use serde::de::DeserializeOwned; use std::{ - env, - env::set_current_dir, - fs, + env, fs, io::{Cursor, Read}, - path::Path, }; use tempfile::TempDir; use zkvm_interface::{ - Compiler, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType, - PublicValues, zkVM, zkVMError, + Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType, PublicValues, + zkVM, zkVMError, }; include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); -mod compile_stock_rust; -mod error; + +pub mod compiler; +pub mod error; mod jolt_methods; -#[allow(non_camel_case_types)] -pub struct JOLT_TARGET; - -impl Compiler for JOLT_TARGET { - type Error = JoltError; - - type Program = Vec; - - fn compile(&self, guest_directory: &Path) -> Result { - let toolchain = env::var("ERE_GUEST_TOOLCHAIN").unwrap_or_else(|_error| "jolt".into()); - match toolchain.as_str() { - "jolt" => Ok(compile_jolt_program(guest_directory)?), - _ => Ok(compile_jolt_program_stock_rust( - guest_directory, - &toolchain, - )?), - } - } -} - -fn compile_jolt_program(guest_directory: &Path) -> Result, JoltError> { - // Change current directory for `Program::build` to build guest program. - set_current_dir(guest_directory).map_err(|source| CompileError::SetCurrentDirFailed { - source, - path: guest_directory.to_path_buf(), - })?; - - let package_name = cargo_metadata(guest_directory) - .map_err(CompileError::CompileUtilError)? - .root_package() - .unwrap() - .name - .clone(); - - // Note that if this fails, it will panic, hence we need to catch it. - let elf_path = std::panic::catch_unwind(|| { - let mut program = Program::new(&package_name); - program.set_std(true); - program.build(DEFAULT_TARGET_DIR); - program.elf.unwrap() - }) - .map_err(|_| CompileError::BuildFailed)?; - - let elf = fs::read(&elf_path).map_err(|source| CompileError::ReadElfFailed { - source, - path: elf_path.to_path_buf(), - })?; - - Ok(elf) -} - #[derive(CanonicalSerialize, CanonicalDeserialize)] pub struct EreJoltProof { proof: JoltHyperKZGProof, @@ -85,14 +31,14 @@ pub struct EreJoltProof { } pub struct EreJolt { - elf: Vec, + elf: JoltProgram, prover_preprocessing: JoltProverPreprocessing<4, jolt::F, jolt::PCS, jolt::ProofTranscript>, verifier_preprocessing: JoltVerifierPreprocessing<4, jolt::F, jolt::PCS, jolt::ProofTranscript>, _resource: ProverResourceType, } impl EreJolt { - pub fn new(elf: Vec, _resource: ProverResourceType) -> Result { + pub fn new(elf: JoltProgram, _resource: ProverResourceType) -> Result { let (_tempdir, program) = program(&elf)?; let prover_preprocessing = preprocess_prover(&program); let verifier_preprocessing = preprocess_verifier(&program); @@ -178,47 +124,11 @@ impl zkVM for EreJolt { /// file, and set the elf path for `program`, so methods like `decode`, `trace` /// and `trace_analyze` that depend on elf path will work. pub fn program(elf: &[u8]) -> Result<(TempDir, jolt::host::Program), zkVMError> { - let tempdir = TempDir::new().map_err(|err| zkVMError::Other(err.into()))?; + let tempdir = TempDir::new().map_err(zkVMError::other)?; let elf_path = tempdir.path().join("guest.elf"); - fs::write(&elf_path, elf).map_err(|err| zkVMError::Other(err.into()))?; + fs::write(&elf_path, elf).map_err(zkVMError::other)?; // Set a dummy package name because we don't need to compile anymore. - let mut program = Program::new(""); + let mut program = jolt::host::Program::new(""); program.elf = Some(elf_path); Ok((tempdir, program)) } - -#[cfg(test)] -mod tests { - use super::*; - use std::sync::OnceLock; - use test_utils::host::{BasicProgramIo, testing_guest_directory}; - - static BASIC_PROGRAM: OnceLock> = OnceLock::new(); - - fn basic_program() -> Vec { - BASIC_PROGRAM - .get_or_init(|| { - JOLT_TARGET - .compile(&testing_guest_directory("jolt", "basic")) - .unwrap() - }) - .clone() - } - - #[test] - fn test_compiler_impl() { - let elf_bytes = basic_program(); - assert!(!elf_bytes.is_empty(), "ELF bytes should not be empty."); - } - - #[test] - fn test_execute_nightly() { - let guest_directory = testing_guest_directory("jolt", "stock_nightly_no_std"); - let program = - compile_jolt_program_stock_rust(&guest_directory, &"nightly".to_string()).unwrap(); - let zkvm = EreJolt::new(program, ProverResourceType::Cpu).unwrap(); - - let result = zkvm.execute(&BasicProgramIo::empty()); - assert!(result.is_ok(), "Jolt execution failure"); - } -} diff --git a/crates/ere-miden/src/compiler.rs b/crates/ere-miden/src/compiler.rs new file mode 100644 index 0000000..eb308f8 --- /dev/null +++ b/crates/ere-miden/src/compiler.rs @@ -0,0 +1,31 @@ +use miden_core::utils::{Deserializable, Serializable}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; + +mod miden_asm; + +pub use miden_asm::MidenAsm; + +/// Wrapper for [`miden_core::Program`] that implements `serde`. +#[derive(Clone)] +pub struct MidenProgram(pub miden_core::Program); + +impl Serialize for MidenProgram { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_bytes(&self.0.to_bytes()) + } +} + +impl<'de> Deserialize<'de> for MidenProgram { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let bytes = Vec::::deserialize(deserializer)?; + miden_core::Program::read_from_bytes(&bytes) + .map(Self) + .map_err(D::Error::custom) + } +} diff --git a/crates/ere-miden/src/compile.rs b/crates/ere-miden/src/compiler/miden_asm.rs similarity index 83% rename from crates/ere-miden/src/compile.rs rename to crates/ere-miden/src/compiler/miden_asm.rs index cef1731..ee921d4 100644 --- a/crates/ere-miden/src/compile.rs +++ b/crates/ere-miden/src/compiler/miden_asm.rs @@ -1,14 +1,16 @@ use crate::{ - MIDEN_TARGET, MidenProgram, + compiler::MidenProgram, error::{CompileError, MidenError}, }; use miden_assembly::Assembler; -use miden_core::utils::Serializable; use miden_stdlib::StdLibrary; use std::{fs, path::Path}; use zkvm_interface::Compiler; -impl Compiler for MIDEN_TARGET { +/// Compiler for Miden assembly guest program. +pub struct MidenAsm; + +impl Compiler for MidenAsm { type Error = MidenError; type Program = MidenProgram; @@ -43,22 +45,20 @@ impl Compiler for MIDEN_TARGET { .assemble_program(&source) .map_err(|e| CompileError::AssemblyCompilation(e.to_string()))?; - Ok(MidenProgram { - program_bytes: program.to_bytes(), - }) + Ok(MidenProgram(program)) } } #[cfg(test)] mod tests { - use super::*; + use crate::compiler::MidenAsm; use test_utils::host::testing_guest_directory; use zkvm_interface::Compiler; #[test] fn test_compile() { let guest_directory = testing_guest_directory("miden", "fib"); - let program = MIDEN_TARGET.compile(&guest_directory).unwrap(); - assert!(!program.program_bytes.is_empty()); + let program = MidenAsm.compile(&guest_directory).unwrap(); + assert!(program.0.num_procedures() > 0); } } diff --git a/crates/ere-miden/src/lib.rs b/crates/ere-miden/src/lib.rs index 064d85f..5284943 100644 --- a/crates/ere-miden/src/lib.rs +++ b/crates/ere-miden/src/lib.rs @@ -1,9 +1,8 @@ -pub mod compile; -pub mod error; -pub mod io; - -use self::error::{ExecuteError, MidenError, VerifyError}; -use self::io::{generate_miden_inputs, outputs_to_public_values}; +use crate::{ + compiler::MidenProgram, + error::{ExecuteError, MidenError, VerifyError}, + io::{generate_miden_inputs, outputs_to_public_values}, +}; use miden_core::{ Program, utils::{Deserializable, Serializable}, @@ -23,13 +22,9 @@ use zkvm_interface::{ include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); -#[allow(non_camel_case_types)] -pub struct MIDEN_TARGET; - -#[derive(Clone, Serialize, Deserialize)] -pub struct MidenProgram { - pub program_bytes: Vec, -} +pub mod compiler; +pub mod error; +mod io; #[derive(Serialize, Deserialize)] struct MidenProofBundle { @@ -44,11 +39,7 @@ pub struct EreMiden { impl EreMiden { pub fn new(program: MidenProgram, _resource: ProverResourceType) -> Result { - let program = Program::read_from_bytes(&program.program_bytes) - .map_err(ExecuteError::ProgramDeserialization) - .map_err(MidenError::Execute)?; - - Ok(Self { program }) + Ok(Self { program: program.0 }) } fn setup_host() -> Result { @@ -166,12 +157,15 @@ impl zkVM for EreMiden { #[cfg(test)] mod tests { - use super::*; + use crate::{ + EreMiden, + compiler::{MidenAsm, MidenProgram}, + }; use test_utils::host::testing_guest_directory; - use zkvm_interface::Compiler; + use zkvm_interface::{Compiler, Input, ProverResourceType, zkVM}; fn load_miden_program(guest_name: &str) -> MidenProgram { - MIDEN_TARGET + MidenAsm .compile(&testing_guest_directory("miden", guest_name)) .unwrap() } diff --git a/crates/ere-nexus/Cargo.toml b/crates/ere-nexus/Cargo.toml index d47f532..a51b860 100644 --- a/crates/ere-nexus/Cargo.toml +++ b/crates/ere-nexus/Cargo.toml @@ -9,13 +9,13 @@ license.workspace = true bincode.workspace = true serde.workspace = true thiserror.workspace = true -toml.workspace = true tracing.workspace = true # Nexus dependencies nexus-sdk.workspace = true # Local dependencies +compile-utils.workspace = true zkvm-interface.workspace = true [dev-dependencies] diff --git a/crates/ere-nexus/src/compiler.rs b/crates/ere-nexus/src/compiler.rs new file mode 100644 index 0000000..925698e --- /dev/null +++ b/crates/ere-nexus/src/compiler.rs @@ -0,0 +1,5 @@ +mod rust_rv32i; + +pub use rust_rv32i::RustRv32i; + +pub type NexusProgram = Vec; diff --git a/crates/ere-nexus/src/compiler/rust_rv32i.rs b/crates/ere-nexus/src/compiler/rust_rv32i.rs new file mode 100644 index 0000000..48fe629 --- /dev/null +++ b/crates/ere-nexus/src/compiler/rust_rv32i.rs @@ -0,0 +1,51 @@ +use crate::{ + compiler::NexusProgram, + error::{CompileError, NexusError}, +}; +use compile_utils::cargo_metadata; +use nexus_sdk::compile::{Compile, Compiler as NexusCompiler, cargo::CargoPackager}; +use std::{fs, path::Path}; +use zkvm_interface::Compiler; + +/// Compiler for Rust guest program to RV32I architecture. +pub struct RustRv32i; + +impl Compiler for RustRv32i { + type Error = NexusError; + + type Program = NexusProgram; + + fn compile(&self, guest_path: &Path) -> Result { + // 1. Check guest path + if !guest_path.exists() { + return Err(CompileError::PathNotFound(guest_path.to_path_buf()))?; + } + std::env::set_current_dir(guest_path).map_err(|e| CompileError::Client(e.into()))?; + + let metadata = cargo_metadata(guest_path).map_err(CompileError::CompileUtilError)?; + let package_name = &metadata.root_package().unwrap().name; + + let mut prover_compiler = NexusCompiler::::new(package_name); + let elf_path = prover_compiler + .build() + .map_err(|e| CompileError::Client(e.into()))?; + + let elf = fs::read(&elf_path).map_err(|_| CompileError::ElfNotFound(elf_path))?; + + Ok(elf) + } +} + +#[cfg(test)] +mod tests { + use crate::compiler::RustRv32i; + use test_utils::host::testing_guest_directory; + use zkvm_interface::Compiler; + + #[test] + fn test_compile() { + let guest_directory = testing_guest_directory("nexus", "basic"); + let elf = RustRv32i.compile(&guest_directory).unwrap(); + assert!(!elf.is_empty(), "ELF bytes should not be empty."); + } +} diff --git a/crates/ere-nexus/src/error.rs b/crates/ere-nexus/src/error.rs index a287f7a..81a1d69 100644 --- a/crates/ere-nexus/src/error.rs +++ b/crates/ere-nexus/src/error.rs @@ -18,20 +18,20 @@ pub enum NexusError { #[error(transparent)] Verify(#[from] VerifyError), - - /// Guest program directory does not exist. - #[error("guest program directory not found: {0}")] - PathNotFound(PathBuf), - - /// Expected ELF file was not produced. - #[error("ELF file not found at {0}")] - ElfNotFound(PathBuf), } #[derive(Debug, Error)] pub enum CompileError { #[error("nexus execution failed: {0}")] Client(#[source] Box), + /// Guest program directory does not exist. + #[error("guest program directory not found: {0}")] + PathNotFound(PathBuf), + /// Expected ELF file was not produced. + #[error("ELF file not found at {0}")] + ElfNotFound(PathBuf), + #[error(transparent)] + CompileUtilError(#[from] compile_utils::CompileError), } #[derive(Debug, Error)] diff --git a/crates/ere-nexus/src/lib.rs b/crates/ere-nexus/src/lib.rs index 0e913d1..efed6ed 100644 --- a/crates/ere-nexus/src/lib.rs +++ b/crates/ere-nexus/src/lib.rs @@ -1,72 +1,33 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] -#![allow(clippy::uninlined_format_args)] -use std::io::Read; -use std::path::{Path, PathBuf}; -use std::time::Instant; - -use nexus_sdk::compile::cargo::CargoPackager; -use nexus_sdk::compile::{Compile, Compiler as NexusCompiler}; -use nexus_sdk::stwo::seq::Stwo; -use nexus_sdk::{Local, Prover, Verifiable}; +use crate::{ + compiler::NexusProgram, + error::{NexusError, ProveError, VerifyError}, +}; +use nexus_sdk::{Local, Prover, Verifiable, stwo::seq::Stwo}; use serde::de::DeserializeOwned; +use std::{io::Read, time::Instant}; use tracing::info; use zkvm_interface::{ - Compiler, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType, - PublicValues, zkVM, zkVMError, + Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType, PublicValues, + zkVM, zkVMError, }; include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); -mod error; -pub(crate) mod utils; - -use crate::error::ProveError; -use crate::utils::get_cargo_package_name; -use error::{CompileError, NexusError, VerifyError}; - -#[allow(non_camel_case_types)] -pub struct NEXUS_TARGET; - -impl Compiler for NEXUS_TARGET { - type Error = NexusError; - - type Program = PathBuf; - - fn compile(&self, guest_path: &Path) -> Result { - // 1. Check guest path - if !guest_path.exists() { - return Err(NexusError::PathNotFound(guest_path.to_path_buf())); - } - std::env::set_current_dir(guest_path).map_err(|e| CompileError::Client(e.into()))?; - - let package_name = get_cargo_package_name(guest_path) - .ok_or(CompileError::Client(Box::from(format!( - "Failed to get guest package name, where guest path: {:?}", - guest_path - )))) - .map_err(|e| CompileError::Client(e.into()))?; - let mut prover_compiler = NexusCompiler::::new(&package_name); - let elf_path = prover_compiler - .build() - .map_err(|e| CompileError::Client(e.into()))?; - - Ok(elf_path) - } -} +pub mod compiler; +pub mod error; pub struct EreNexus { - program: ::Program, + program: NexusProgram, } impl EreNexus { - pub fn new( - program: ::Program, - _resource_type: ProverResourceType, - ) -> Self { + pub fn new(program: NexusProgram, _resource_type: ProverResourceType) -> Self { Self { program } } } + impl zkVM for EreNexus { fn execute( &self, @@ -87,7 +48,7 @@ impl zkVM for EreNexus { &self, _inputs: &Input, ) -> Result<(PublicValues, Proof, zkvm_interface::ProgramProvingReport), zkVMError> { - let prover: Stwo = Stwo::new_from_file(&self.program.to_string_lossy().to_string()) + let prover: Stwo = Stwo::new_from_bytes(&self.program) .map_err(|e| NexusError::Prove(ProveError::Client(e.into()))) .map_err(zkVMError::from)?; @@ -116,21 +77,20 @@ impl zkVM for EreNexus { let proof: nexus_sdk::stwo::seq::Proof = bincode::deserialize(proof) .map_err(|err| NexusError::Verify(VerifyError::Bincode(err)))?; - let prover: Stwo = Stwo::new_from_file(&self.program.to_string_lossy().to_string()) + let prover: Stwo = Stwo::new_from_bytes(&self.program) .map_err(|e| NexusError::Prove(ProveError::Client(e.into()))) .map_err(zkVMError::from)?; let elf = prover.elf.clone(); // save elf for use with verification - #[rustfmt::skip] proof - .verify_expected::<(), ()>( - &(), // no public input - nexus_sdk::KnownExitCodes::ExitSuccess as u32, - &(), // no public output - &elf, // expected elf (program binary) - &[], // no associated data, - ) - .map_err(|e| NexusError::Verify(VerifyError::Client(e.into()))) - .map_err(zkVMError::from)?; + .verify_expected::<(), ()>( + &(), // no public input + nexus_sdk::KnownExitCodes::ExitSuccess as u32, + &(), // no public output + &elf, // expected elf (program binary) + &[], // no associated data, + ) + .map_err(|e| NexusError::Verify(VerifyError::Client(e.into()))) + .map_err(zkVMError::from)?; info!("Verify Succeeded!"); @@ -153,31 +113,3 @@ impl zkVM for EreNexus { todo!() } } - -#[cfg(test)] -mod tests { - use super::*; - use std::{fs, sync::OnceLock}; - use test_utils::host::testing_guest_directory; - - static BASIC_PROGRAM: OnceLock = OnceLock::new(); - - fn basic_program() -> PathBuf { - BASIC_PROGRAM - .get_or_init(|| { - NEXUS_TARGET - .compile(&testing_guest_directory("nexus", "basic")) - .unwrap() - }) - .to_path_buf() - } - - #[test] - fn test_compiler_impl() { - let elf_path = basic_program(); - assert!( - fs::metadata(&elf_path).unwrap().len() != 0, - "ELF bytes should not be empty." - ); - } -} diff --git a/crates/ere-nexus/src/utils.rs b/crates/ere-nexus/src/utils.rs deleted file mode 100644 index 16f26c4..0000000 --- a/crates/ere-nexus/src/utils.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::fs; -use toml::Table; - -pub fn get_cargo_package_name(crate_path: &std::path::Path) -> Option { - let cargo_contents = fs::read_to_string(crate_path.join("Cargo.toml")).ok()?; - let cargo_toml: Table = toml::from_str(&cargo_contents).ok()?; - - cargo_toml - .get("package")? - .get("name")? - .as_str() - .map(|s| s.to_string()) -} diff --git a/crates/ere-openvm/Cargo.toml b/crates/ere-openvm/Cargo.toml index d0e6988..2586964 100644 --- a/crates/ere-openvm/Cargo.toml +++ b/crates/ere-openvm/Cargo.toml @@ -8,7 +8,6 @@ license.workspace = true [dependencies] serde = { workspace = true, features = ["derive"] } thiserror.workspace = true -tracing.workspace = true toml.workspace = true # OpenVM dependencies diff --git a/crates/ere-openvm/src/compile_stock_rust.rs b/crates/ere-openvm/src/compile_stock_rust.rs deleted file mode 100644 index d9646ac..0000000 --- a/crates/ere-openvm/src/compile_stock_rust.rs +++ /dev/null @@ -1,116 +0,0 @@ -use crate::OpenVMProgram; -use crate::error::CompileError; -use compile_utils::CargoBuildCmd; -use openvm_sdk::config::{AppConfig, DEFAULT_APP_LOG_BLOWUP, DEFAULT_LEAF_LOG_BLOWUP, SdkVmConfig}; -use openvm_stark_sdk::config::FriParameters; -use std::fs; -use std::path::Path; -use tracing::info; - -const TARGET_TRIPLE: &str = "riscv32ima-unknown-none-elf"; -// Rust flags according to https://github.com/openvm-org/openvm/blob/v1.4.0/crates/toolchain/build/src/lib.rs#L291 -const RUSTFLAGS: &[&str] = &[ - // Replace atomic ops with nonatomic versions since the guest is single threaded. - "-C", - "passes=lower-atomic", - // Specify where to start loading the program in - // memory. The clang linker understands the same - // command line arguments as the GNU linker does; see - // https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html#SEC3 - // for details. - "-C", - "link-arg=-Ttext=0x00200800", - // Apparently not having an entry point is only a linker warning(!), so - // error out in this case. - "-C", - "link-arg=--fatal-warnings", - "-C", - "panic=abort", - // https://docs.rs/getrandom/0.3.2/getrandom/index.html#opt-in-backends - "--cfg", - "getrandom_backend=\"custom\"", -]; -const CARGO_BUILD_OPTIONS: &[&str] = &[ - // For bare metal we have to build core and alloc - "-Zbuild-std=core,alloc", -]; - -pub fn compile_openvm_program_stock_rust( - guest_directory: &Path, - toolchain: &String, -) -> Result { - wrap_into_openvm_program( - compile_program_stock_rust(guest_directory, toolchain)?, - guest_directory, - ) -} - -fn compile_program_stock_rust( - guest_directory: &Path, - toolchain: &String, -) -> Result, CompileError> { - let elf = CargoBuildCmd::new() - .toolchain(toolchain) - .build_options(CARGO_BUILD_OPTIONS) - .rustflags(RUSTFLAGS) - .exec(guest_directory, TARGET_TRIPLE)?; - - Ok(elf) -} - -fn wrap_into_openvm_program( - elf: Vec, - guest_directory: &Path, -) -> Result { - let app_config_path = guest_directory.join("openvm.toml"); - let app_config = if app_config_path.exists() { - let toml = fs::read_to_string(&app_config_path).map_err(|source| { - CompileError::ReadConfigFailed { - source, - path: app_config_path.to_path_buf(), - } - })?; - toml::from_str(&toml).map_err(CompileError::DeserializeConfigFailed)? - } else { - // The default `AppConfig` copied from https://github.com/openvm-org/openvm/blob/ca36de3/crates/cli/src/default.rs#L31. - AppConfig { - app_fri_params: FriParameters::standard_with_100_bits_conjectured_security( - DEFAULT_APP_LOG_BLOWUP, - ) - .into(), - // By default it supports RISCV32IM with IO but no precompiles. - app_vm_config: SdkVmConfig::builder() - .system(Default::default()) - .rv32i(Default::default()) - .rv32m(Default::default()) - .io(Default::default()) - .build(), - leaf_fri_params: FriParameters::standard_with_100_bits_conjectured_security( - DEFAULT_LEAF_LOG_BLOWUP, - ) - .into(), - compiler_options: Default::default(), - } - }; - - info!("Openvm program compiled OK - {} bytes", elf.len()); - - Ok(OpenVMProgram { elf, app_config }) -} - -#[cfg(test)] -mod tests { - use crate::compile_stock_rust::compile_openvm_program_stock_rust; - use test_utils::host::testing_guest_directory; - - #[test] - fn test_stock_compiler_impl() { - let guest_directory = testing_guest_directory("openvm", "stock_nightly_no_std"); - let result = compile_openvm_program_stock_rust(&guest_directory, &"nightly".to_string()); - assert!(result.is_ok(), "Openvm guest program compilation failure."); - assert!( - !result.unwrap().elf.is_empty(), - "ELF bytes should not be empty." - ); - } -} diff --git a/crates/ere-openvm/src/compiler.rs b/crates/ere-openvm/src/compiler.rs new file mode 100644 index 0000000..eb07eb5 --- /dev/null +++ b/crates/ere-openvm/src/compiler.rs @@ -0,0 +1,56 @@ +use crate::error::CompileError; +use openvm_sdk::config::{AppConfig, DEFAULT_APP_LOG_BLOWUP, DEFAULT_LEAF_LOG_BLOWUP, SdkVmConfig}; +use openvm_stark_sdk::config::FriParameters; +use serde::{Deserialize, Serialize}; +use std::{fs, path::Path}; + +mod rust_rv32ima; +mod rust_rv32ima_customized; + +pub use rust_rv32ima::RustRv32ima; +pub use rust_rv32ima_customized::RustRv32imaCustomized; + +#[derive(Clone, Serialize, Deserialize)] +pub struct OpenVMProgram { + pub elf: Vec, + pub app_config: AppConfig, +} + +impl OpenVMProgram { + fn from_elf_and_app_config_path( + elf: Vec, + app_config_path: impl AsRef, + ) -> Result { + let app_config = if app_config_path.as_ref().exists() { + let toml = fs::read_to_string(app_config_path.as_ref()).map_err(|source| { + CompileError::ReadConfigFailed { + source, + path: app_config_path.as_ref().to_path_buf(), + } + })?; + toml::from_str(&toml).map_err(CompileError::DeserializeConfigFailed)? + } else { + // The default `AppConfig` copied from https://github.com/openvm-org/openvm/blob/v1.4.0/crates/cli/src/default.rs#L35. + AppConfig { + app_fri_params: FriParameters::standard_with_100_bits_conjectured_security( + DEFAULT_APP_LOG_BLOWUP, + ) + .into(), + // By default it supports RISCV32IM with IO but no precompiles. + app_vm_config: SdkVmConfig::builder() + .system(Default::default()) + .rv32i(Default::default()) + .rv32m(Default::default()) + .io(Default::default()) + .build(), + leaf_fri_params: FriParameters::standard_with_100_bits_conjectured_security( + DEFAULT_LEAF_LOG_BLOWUP, + ) + .into(), + compiler_options: Default::default(), + } + }; + + Ok(Self { elf, app_config }) + } +} diff --git a/crates/ere-openvm/src/compiler/rust_rv32ima.rs b/crates/ere-openvm/src/compiler/rust_rv32ima.rs new file mode 100644 index 0000000..a8fc0a1 --- /dev/null +++ b/crates/ere-openvm/src/compiler/rust_rv32ima.rs @@ -0,0 +1,81 @@ +use crate::{ + OpenVMProgram, + error::{CompileError, OpenVMError}, +}; +use compile_utils::CargoBuildCmd; +use std::{env, path::Path}; +use zkvm_interface::Compiler; + +const TARGET_TRIPLE: &str = "riscv32ima-unknown-none-elf"; +// Rust flags according to https://github.com/openvm-org/openvm/blob/v1.4.0/crates/toolchain/build/src/lib.rs#L291 +const RUSTFLAGS: &[&str] = &[ + // Replace atomic ops with nonatomic versions since the guest is single threaded. + "-C", + "passes=lower-atomic", + // Specify where to start loading the program in + // memory. The clang linker understands the same + // command line arguments as the GNU linker does; see + // https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html#SEC3 + // for details. + "-C", + "link-arg=-Ttext=0x00200800", + // Apparently not having an entry point is only a linker warning(!), so + // error out in this case. + "-C", + "link-arg=--fatal-warnings", + "-C", + "panic=abort", + // https://docs.rs/getrandom/0.3.2/getrandom/index.html#opt-in-backends + "--cfg", + "getrandom_backend=\"custom\"", +]; +const CARGO_BUILD_OPTIONS: &[&str] = &[ + // For bare metal we have to build core and alloc + "-Zbuild-std=core,alloc", +]; + +/// Compiler for Rust guest program to RV32IMA architecture. +pub struct RustRv32ima; + +impl Compiler for RustRv32ima { + type Error = OpenVMError; + + type Program = OpenVMProgram; + + fn compile(&self, guest_directory: &Path) -> Result { + let toolchain = env::var("ERE_RUST_TOOLCHAIN").unwrap_or_else(|_| "nightly".into()); + let elf = CargoBuildCmd::new() + .toolchain(toolchain) + .build_options(CARGO_BUILD_OPTIONS) + .rustflags(RUSTFLAGS) + .exec(guest_directory, TARGET_TRIPLE) + .map_err(CompileError::CompileUtilError)?; + Ok(OpenVMProgram::from_elf_and_app_config_path( + elf, + guest_directory.join("openvm.toml"), + )?) + } +} + +#[cfg(test)] +mod tests { + use crate::{EreOpenVM, compiler::RustRv32ima}; + use test_utils::host::testing_guest_directory; + use zkvm_interface::{Compiler, Input, ProverResourceType, zkVM}; + + #[test] + fn test_compile() { + let guest_directory = testing_guest_directory("openvm", "stock_nightly_no_std"); + let program = RustRv32ima.compile(&guest_directory).unwrap(); + assert!(!program.elf.is_empty(), "ELF bytes should not be empty."); + } + + #[test] + fn test_execute() { + let guest_directory = testing_guest_directory("openvm", "stock_nightly_no_std"); + let program = RustRv32ima.compile(&guest_directory).unwrap(); + let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap(); + + zkvm.execute(&Input::new()).unwrap(); + } +} diff --git a/crates/ere-openvm/src/compiler/rust_rv32ima_customized.rs b/crates/ere-openvm/src/compiler/rust_rv32ima_customized.rs new file mode 100644 index 0000000..5eef01a --- /dev/null +++ b/crates/ere-openvm/src/compiler/rust_rv32ima_customized.rs @@ -0,0 +1,54 @@ +use crate::{ + compiler::OpenVMProgram, + error::{CompileError, OpenVMError}, +}; +use openvm_build::GuestOptions; +use std::{fs, path::Path}; +use zkvm_interface::Compiler; + +/// Compiler for Rust guest program to RV32IMA architecture, using customized +/// target `riscv32im-risc0-zkvm-elf`. +pub struct RustRv32imaCustomized; + +impl Compiler for RustRv32imaCustomized { + type Error = OpenVMError; + + type Program = OpenVMProgram; + + fn compile(&self, guest_directory: &Path) -> Result { + // Inlining `openvm_sdk::Sdk::build` in order to get raw elf bytes. + let pkg = openvm_build::get_package(guest_directory); + let guest_opts = GuestOptions::default().with_profile("release".to_string()); + let target_dir = match openvm_build::build_guest_package(&pkg, &guest_opts, None, &None) { + Ok(target_dir) => target_dir, + Err(Some(code)) => return Err(CompileError::BuildFailed(code))?, + Err(None) => return Err(CompileError::BuildSkipped)?, + }; + + let elf_path = openvm_build::find_unique_executable(guest_directory, target_dir, &None) + .map_err(|e| CompileError::UniqueElfNotFound(e.into()))?; + let elf = fs::read(&elf_path).map_err(|source| CompileError::ReadElfFailed { + source, + path: elf_path.to_path_buf(), + })?; + + Ok(OpenVMProgram::from_elf_and_app_config_path( + elf, + guest_directory.join("openvm.toml"), + )?) + } +} + +#[cfg(test)] +mod tests { + use crate::compiler::RustRv32imaCustomized; + use test_utils::host::testing_guest_directory; + use zkvm_interface::Compiler; + + #[test] + fn test_compile() { + let guest_directory = testing_guest_directory("openvm", "basic"); + let program = RustRv32imaCustomized.compile(&guest_directory).unwrap(); + assert!(!program.elf.is_empty(), "ELF bytes should not be empty."); + } +} diff --git a/crates/ere-openvm/src/lib.rs b/crates/ere-openvm/src/lib.rs index 3b96a50..f46919c 100644 --- a/crates/ere-openvm/src/lib.rs +++ b/crates/ere-openvm/src/lib.rs @@ -1,114 +1,32 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] -use crate::compile_stock_rust::compile_openvm_program_stock_rust; -use crate::error::{CommonError, CompileError, ExecuteError, OpenVMError, ProveError, VerifyError}; -use openvm_build::GuestOptions; +use crate::{ + compiler::OpenVMProgram, + error::{CommonError, ExecuteError, OpenVMError, ProveError, VerifyError}, +}; use openvm_circuit::arch::instructions::exe::VmExe; use openvm_continuations::verifier::internal::types::VmStarkProof; use openvm_sdk::{ CpuSdk, F, SC, StdIn, codec::{Decode, Encode}, commit::AppExecutionCommit, - config::{AppConfig, DEFAULT_APP_LOG_BLOWUP, DEFAULT_LEAF_LOG_BLOWUP, SdkVmConfig}, + config::{AppConfig, SdkVmConfig}, fs::read_object_from_file, keygen::{AggProvingKey, AggVerifyingKey, AppProvingKey}, }; -use openvm_stark_sdk::{config::FriParameters, openvm_stark_backend::p3_field::PrimeField32}; +use openvm_stark_sdk::openvm_stark_backend::p3_field::PrimeField32; use openvm_transpiler::{elf::Elf, openvm_platform::memory::MEM_SIZE}; -use serde::{Deserialize, Serialize, de::DeserializeOwned}; -use std::{ - env, fs, - io::Read, - path::{Path, PathBuf}, - sync::Arc, - time::Instant, -}; +use serde::de::DeserializeOwned; +use std::{env, io::Read, path::PathBuf, sync::Arc, time::Instant}; use zkvm_interface::{ - Compiler, Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, - ProverResourceType, PublicValues, zkVM, zkVMError, + Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType, + PublicValues, zkVM, zkVMError, }; -mod compile_stock_rust; - include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); -mod error; -#[allow(non_camel_case_types)] -pub struct OPENVM_TARGET; - -#[derive(Clone, Serialize, Deserialize)] -pub struct OpenVMProgram { - elf: Vec, - app_config: AppConfig, -} - -impl Compiler for OPENVM_TARGET { - type Error = OpenVMError; - - type Program = OpenVMProgram; - - fn compile(&self, guest_directory: &Path) -> Result { - let toolchain = env::var("ERE_GUEST_TOOLCHAIN").unwrap_or_else(|_error| "openvm".into()); - match toolchain.as_str() { - "openvm" => Ok(compile_openvm_program(guest_directory)?), - _ => Ok(compile_openvm_program_stock_rust( - guest_directory, - &toolchain, - )?), - } - } -} - -// Inlining `openvm_sdk::Sdk::build` in order to get raw elf bytes. -fn compile_openvm_program(guest_directory: &Path) -> Result { - let pkg = openvm_build::get_package(guest_directory); - let guest_opts = GuestOptions::default().with_profile("release".to_string()); - let target_dir = match openvm_build::build_guest_package(&pkg, &guest_opts, None, &None) { - Ok(target_dir) => target_dir, - Err(Some(code)) => return Err(CompileError::BuildFailed(code))?, - Err(None) => return Err(CompileError::BuildSkipped)?, - }; - - let elf_path = openvm_build::find_unique_executable(guest_directory, target_dir, &None) - .map_err(|e| CompileError::UniqueElfNotFound(e.into()))?; - let elf = fs::read(&elf_path).map_err(|source| CompileError::ReadElfFailed { - source, - path: elf_path.to_path_buf(), - })?; - - let app_config_path = guest_directory.join("openvm.toml"); - let app_config = if app_config_path.exists() { - let toml = fs::read_to_string(&app_config_path).map_err(|source| { - CompileError::ReadConfigFailed { - source, - path: app_config_path.to_path_buf(), - } - })?; - toml::from_str(&toml).map_err(CompileError::DeserializeConfigFailed)? - } else { - // The default `AppConfig` copied from https://github.com/openvm-org/openvm/blob/ca36de3/crates/cli/src/default.rs#L31. - AppConfig { - app_fri_params: FriParameters::standard_with_100_bits_conjectured_security( - DEFAULT_APP_LOG_BLOWUP, - ) - .into(), - // By default it supports RISCV32IM with IO but no precompiles. - app_vm_config: SdkVmConfig::builder() - .system(Default::default()) - .rv32i(Default::default()) - .rv32m(Default::default()) - .io(Default::default()) - .build(), - leaf_fri_params: FriParameters::standard_with_100_bits_conjectured_security( - DEFAULT_LEAF_LOG_BLOWUP, - ) - .into(), - compiler_options: Default::default(), - } - }; - - Ok(OpenVMProgram { elf, app_config }) -} +pub mod compiler; +pub mod error; pub struct EreOpenVM { app_config: AppConfig, @@ -299,37 +217,34 @@ fn extract_public_values(user_public_values: &[F]) -> Result, CommonErro .ok_or(CommonError::InvalidPublicValue) } -pub fn agg_pk_path() -> PathBuf { +fn agg_pk_path() -> PathBuf { PathBuf::from(std::env::var("HOME").expect("env `$HOME` should be set")) .join(".openvm/agg_stark.pk") } #[cfg(test)] mod tests { - use super::*; - use crate::compile_stock_rust::compile_openvm_program_stock_rust; + use crate::{ + EreOpenVM, + compiler::{OpenVMProgram, RustRv32imaCustomized}, + }; use std::sync::OnceLock; use test_utils::host::{ BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, }; + use zkvm_interface::{Compiler, ProverResourceType, zkVM}; fn basic_program() -> OpenVMProgram { static PROGRAM: OnceLock = OnceLock::new(); PROGRAM .get_or_init(|| { - OPENVM_TARGET + RustRv32imaCustomized .compile(&testing_guest_directory("openvm", "basic")) .unwrap() }) .clone() } - #[test] - fn test_compiler_impl() { - let program = basic_program(); - assert!(!program.elf.is_empty(), "ELF bytes should not be empty."); - } - #[test] fn test_execute() { let program = basic_program(); @@ -339,17 +254,6 @@ mod tests { run_zkvm_execute(&zkvm, &io); } - #[test] - fn test_execute_nightly() { - let guest_directory = testing_guest_directory("openvm", "stock_nightly_no_std"); - let program = - compile_openvm_program_stock_rust(&guest_directory, &"nightly".to_string()).unwrap(); - let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap(); - - let result = zkvm.execute(&BasicProgramIo::empty()); - assert!(result.is_ok(), "Openvm execution failure"); - } - #[test] fn test_execute_invalid_inputs() { let program = basic_program(); diff --git a/crates/ere-pico/src/compile_stock_rust.rs b/crates/ere-pico/src/compile_stock_rust.rs deleted file mode 100644 index c7109c9..0000000 --- a/crates/ere-pico/src/compile_stock_rust.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::error::CompileError; -use compile_utils::CargoBuildCmd; -use std::path::Path; - -const TARGET_TRIPLE: &str = "riscv32ima-unknown-none-elf"; -// According to https://github.com/brevis-network/pico/blob/v1.1.7/sdk/cli/src/build/build.rs#L104 -const RUSTFLAGS: &[&str] = &[ - // Replace atomic ops with nonatomic versions since the guest is single threaded. - "-C", - "passes=lower-atomic", - // Specify where to start loading the program in - // memory. The clang linker understands the same - // command line arguments as the GNU linker does; see - // https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html#SEC3 - // for details. - "-C", - "link-arg=-Ttext=0x00200800", - // Apparently not having an entry point is only a linker warning(!), so - // error out in this case. - "-C", - "link-arg=--fatal-warnings", - "-C", - "panic=abort", -]; -const CARGO_BUILD_OPTIONS: &[&str] = &[ - // For bare metal we have to build core and alloc - "-Zbuild-std=core,alloc", -]; - -pub fn compile_pico_program_stock_rust( - guest_directory: &Path, - toolchain: &String, -) -> Result, CompileError> { - compile_program_stock_rust(guest_directory, toolchain) -} - -fn compile_program_stock_rust( - guest_directory: &Path, - toolchain: &String, -) -> Result, CompileError> { - let elf = CargoBuildCmd::new() - .toolchain(toolchain) - .build_options(CARGO_BUILD_OPTIONS) - .rustflags(RUSTFLAGS) - .exec(guest_directory, TARGET_TRIPLE)?; - - Ok(elf) -} - -#[cfg(test)] -mod tests { - use crate::compile_stock_rust::compile_pico_program_stock_rust; - use test_utils::host::testing_guest_directory; - - #[test] - fn test_stock_compiler_impl() { - let guest_directory = testing_guest_directory("pico", "stock_nightly_no_std"); - let result = compile_pico_program_stock_rust(&guest_directory, &"nightly".to_string()); - assert!(result.is_ok(), "Pico guest program compilation failure."); - assert!( - !result.unwrap().is_empty(), - "ELF bytes should not be empty." - ); - } -} diff --git a/crates/ere-pico/src/compiler.rs b/crates/ere-pico/src/compiler.rs new file mode 100644 index 0000000..a4e54e1 --- /dev/null +++ b/crates/ere-pico/src/compiler.rs @@ -0,0 +1,7 @@ +mod rust_rv32ima; +mod rust_rv32ima_customized; + +pub use rust_rv32ima::RustRv32ima; +pub use rust_rv32ima_customized::RustRv32imaCustomized; + +pub type PicoProgram = Vec; diff --git a/crates/ere-pico/src/compiler/rust_rv32ima.rs b/crates/ere-pico/src/compiler/rust_rv32ima.rs new file mode 100644 index 0000000..cd1f180 --- /dev/null +++ b/crates/ere-pico/src/compiler/rust_rv32ima.rs @@ -0,0 +1,75 @@ +use crate::{ + compiler::PicoProgram, + error::{CompileError, PicoError}, +}; +use compile_utils::CargoBuildCmd; +use std::{env, path::Path}; +use zkvm_interface::Compiler; + +const TARGET_TRIPLE: &str = "riscv32ima-unknown-none-elf"; +// According to https://github.com/brevis-network/pico/blob/v1.1.7/sdk/cli/src/build/build.rs#L104 +const RUSTFLAGS: &[&str] = &[ + // Replace atomic ops with nonatomic versions since the guest is single threaded. + "-C", + "passes=lower-atomic", + // Specify where to start loading the program in + // memory. The clang linker understands the same + // command line arguments as the GNU linker does; see + // https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html#SEC3 + // for details. + "-C", + "link-arg=-Ttext=0x00200800", + // Apparently not having an entry point is only a linker warning(!), so + // error out in this case. + "-C", + "link-arg=--fatal-warnings", + "-C", + "panic=abort", +]; +const CARGO_BUILD_OPTIONS: &[&str] = &[ + // For bare metal we have to build core and alloc + "-Zbuild-std=core,alloc", +]; + +/// Compiler for Rust guest program to RV32IMA architecture. +pub struct RustRv32ima; + +impl Compiler for RustRv32ima { + type Error = PicoError; + + type Program = PicoProgram; + + fn compile(&self, guest_directory: &Path) -> Result { + let toolchain = env::var("ERE_RUST_TOOLCHAIN").unwrap_or_else(|_| "nightly".into()); + let elf = CargoBuildCmd::new() + .toolchain(toolchain) + .build_options(CARGO_BUILD_OPTIONS) + .rustflags(RUSTFLAGS) + .exec(guest_directory, TARGET_TRIPLE) + .map_err(CompileError::CompileUtilError)?; + Ok(elf) + } +} + +#[cfg(test)] +mod tests { + use crate::{ErePico, compiler::RustRv32ima}; + use test_utils::host::testing_guest_directory; + use zkvm_interface::{Compiler, Input, ProverResourceType, zkVM}; + + #[test] + fn test_compile() { + let guest_directory = testing_guest_directory("pico", "stock_nightly_no_std"); + let elf = RustRv32ima.compile(&guest_directory).unwrap(); + assert!(!elf.is_empty(), "ELF bytes should not be empty."); + } + + #[test] + fn test_execute() { + let guest_directory = testing_guest_directory("pico", "stock_nightly_no_std"); + let program = RustRv32ima.compile(&guest_directory).unwrap(); + let zkvm = ErePico::new(program, ProverResourceType::Cpu); + + zkvm.execute(&Input::new()).unwrap(); + } +} diff --git a/crates/ere-pico/src/compiler/rust_rv32ima_customized.rs b/crates/ere-pico/src/compiler/rust_rv32ima_customized.rs new file mode 100644 index 0000000..b31b7a8 --- /dev/null +++ b/crates/ere-pico/src/compiler/rust_rv32ima_customized.rs @@ -0,0 +1,65 @@ +use crate::error::{CompileError, PicoError}; +use std::{fs, path::Path, process::Command}; +use tempfile::tempdir; +use zkvm_interface::Compiler; + +/// Compiler for Rust guest program to RV32IMA architecture, using customized +/// Rust toolchain of Pico. +pub struct RustRv32imaCustomized; + +impl Compiler for RustRv32imaCustomized { + type Error = PicoError; + + type Program = Vec; + + fn compile(&self, guest_directory: &Path) -> Result { + let tempdir = tempdir().map_err(CompileError::Tempdir)?; + + // 1. Check guest path + if !guest_directory.exists() { + return Err(CompileError::PathNotFound(guest_directory.to_path_buf()))?; + } + + // 2. Run `cargo pico build` + let status = Command::new("cargo") + .current_dir(guest_directory) + .env("RUST_LOG", "info") + .args(["pico", "build", "--output-directory"]) + .arg(tempdir.path()) + .status() + .map_err(CompileError::CargoPicoBuild)?; + + if !status.success() { + return Err(CompileError::CargoPicoBuildFailed { status })?; + } + + // 3. Locate the ELF file + let elf_path = tempdir.path().join("riscv32im-pico-zkvm-elf"); + + if !elf_path.exists() { + return Err(CompileError::ElfNotFound(elf_path))?; + } + + // 4. Read the ELF file + let elf_bytes = fs::read(&elf_path).map_err(|e| CompileError::ReadElf { + path: elf_path, + source: e, + })?; + + Ok(elf_bytes) + } +} + +#[cfg(test)] +mod tests { + use crate::compiler::RustRv32imaCustomized; + use test_utils::host::testing_guest_directory; + use zkvm_interface::Compiler; + + #[test] + fn test_compile() { + let guest_directory = testing_guest_directory("pico", "basic"); + let elf = RustRv32imaCustomized.compile(&guest_directory).unwrap(); + assert!(!elf.is_empty(), "ELF bytes should not be empty."); + } +} diff --git a/crates/ere-pico/src/lib.rs b/crates/ere-pico/src/lib.rs index c18b69a..8555800 100644 --- a/crates/ere-pico/src/lib.rs +++ b/crates/ere-pico/src/lib.rs @@ -2,88 +2,28 @@ use crate::{ client::{MetaProof, ProverClient}, - compile_stock_rust::compile_pico_program_stock_rust, - error::{CompileError, PicoError, ProveError, VerifyError}, + compiler::PicoProgram, + error::{PicoError, ProveError, VerifyError}, }; use pico_p3_field::PrimeField32; use pico_vm::{configs::stark_config::KoalaBearPoseidon2, emulator::stdin::EmulatorStdinBuilder}; use serde::{Deserialize, Serialize, de::DeserializeOwned}; use sha2::{Digest, Sha256}; use std::{ - env, fs, + env, io::Read, - path::Path, - process::Command, time::{self, Instant}, }; -use tempfile::tempdir; use zkvm_interface::{ - Compiler, Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, - ProverResourceType, PublicValues, zkVM, zkVMError, + Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType, + PublicValues, zkVM, zkVMError, }; include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); -mod client; -mod compile_stock_rust; -mod error; - -#[allow(non_camel_case_types)] -pub struct PICO_TARGET; - -impl Compiler for PICO_TARGET { - type Error = PicoError; - - type Program = Vec; - - fn compile(&self, guest_directory: &Path) -> Result { - let toolchain = env::var("ERE_GUEST_TOOLCHAIN").unwrap_or_else(|_error| "pico".into()); - match toolchain.as_str() { - "pico" => Ok(compile_pico_program(guest_directory)?), - _ => Ok(compile_pico_program_stock_rust( - guest_directory, - &toolchain, - )?), - } - } -} - -fn compile_pico_program(guest_directory: &Path) -> Result, CompileError> { - let tempdir = tempdir().map_err(CompileError::Tempdir)?; - - // 1. Check guest path - if !guest_directory.exists() { - return Err(CompileError::PathNotFound(guest_directory.to_path_buf())); - } - - // 2. Run `cargo pico build` - let status = Command::new("cargo") - .current_dir(guest_directory) - .env("RUST_LOG", "info") - .args(["pico", "build", "--output-directory"]) - .arg(tempdir.path()) - .status() - .map_err(CompileError::CargoPicoBuild)?; - - if !status.success() { - return Err(CompileError::CargoPicoBuildFailed { status }); - } - - // 3. Locate the ELF file - let elf_path = tempdir.path().join("riscv32im-pico-zkvm-elf"); - - if !elf_path.exists() { - return Err(CompileError::ElfNotFound(elf_path)); - } - - // 4. Read the ELF file - let elf_bytes = fs::read(&elf_path).map_err(|e| CompileError::ReadElf { - path: elf_path, - source: e, - })?; - - Ok(elf_bytes) -} +pub mod client; +pub mod compiler; +pub mod error; #[derive(Serialize, Deserialize)] pub struct PicoProofWithPublicValues { @@ -92,11 +32,11 @@ pub struct PicoProofWithPublicValues { } pub struct ErePico { - program: ::Program, + program: PicoProgram, } impl ErePico { - pub fn new(program: ::Program, resource: ProverResourceType) -> Self { + pub fn new(program: PicoProgram, resource: ProverResourceType) -> Self { if !matches!(resource, ProverResourceType::Cpu) { panic!("Network or GPU proving not yet implemented for Pico. Use CPU resource type."); } @@ -224,30 +164,28 @@ fn extract_public_values_sha256_digest(proof: &MetaProof) -> Result<[u8; 32], Ve #[cfg(test)] mod tests { - use super::*; + use crate::{ + ErePico, + compiler::{PicoProgram, RustRv32imaCustomized}, + }; use std::{panic, sync::OnceLock}; use test_utils::host::{ BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, }; + use zkvm_interface::{Compiler, ProverResourceType, zkVM}; - static BASIC_PROGRAM: OnceLock> = OnceLock::new(); + static BASIC_PROGRAM: OnceLock = OnceLock::new(); - fn basic_program() -> Vec { + fn basic_program() -> PicoProgram { BASIC_PROGRAM .get_or_init(|| { - PICO_TARGET + RustRv32imaCustomized .compile(&testing_guest_directory("pico", "basic")) .unwrap() }) .clone() } - #[test] - fn test_compiler_impl() { - let elf_bytes = basic_program(); - assert!(!elf_bytes.is_empty(), "ELF bytes should not be empty."); - } - #[test] fn test_execute() { let program = basic_program(); @@ -257,17 +195,6 @@ mod tests { run_zkvm_execute(&zkvm, &io); } - #[test] - fn test_execute_nightly() { - let guest_directory = testing_guest_directory("pico", "stock_nightly_no_std"); - let program = - compile_pico_program_stock_rust(&guest_directory, &"nightly".to_string()).unwrap(); - let zkvm = ErePico::new(program, ProverResourceType::Cpu); - - let result = zkvm.execute(&BasicProgramIo::empty()); - assert!(result.is_ok(), "Pico execution failure"); - } - #[test] fn test_execute_invalid_inputs() { let program = basic_program(); diff --git a/crates/ere-risc0/src/compile.rs b/crates/ere-risc0/src/compile.rs deleted file mode 100644 index 305ae89..0000000 --- a/crates/ere-risc0/src/compile.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::error::CompileError; -use compile_utils::cargo_metadata; -use risc0_build::GuestOptions; -use risc0_zkvm::Digest; -use serde::{Deserialize, Serialize}; -use std::path::Path; -use tracing::info; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Risc0Program { - pub(crate) elf: Vec, - pub(crate) image_id: Digest, -} - -pub fn compile_risc0_program(guest_directory: &Path) -> Result { - info!("Compiling Risc0 program at {}", guest_directory.display()); - - let metadata = cargo_metadata(guest_directory)?; - let package = metadata.root_package().unwrap(); - - // Use `risc0_build::build_package` to build package instead of calling - // `cargo-risczero build` for the `unstable` features. - let guest = - risc0_build::build_package(package, &metadata.target_directory, GuestOptions::default()) - .map_err(|source| CompileError::BuildFailure { - source, - crate_path: guest_directory.to_path_buf(), - })? - .into_iter() - .next() - .ok_or(CompileError::Risc0BuildMissingGuest)?; - - let elf = guest.elf.to_vec(); - let image_id = guest.image_id; - - info!("Risc0 program compiled OK - {} bytes", elf.len()); - info!("Image ID - {image_id}"); - - Ok(Risc0Program { elf, image_id }) -} - -#[cfg(test)] -mod tests { - use crate::RV32_IM_RISC0_ZKVM_ELF; - use test_utils::host::testing_guest_directory; - use zkvm_interface::Compiler; - - #[test] - fn test_compiler_impl() { - let guest_directory = testing_guest_directory("risc0", "basic"); - let program = RV32_IM_RISC0_ZKVM_ELF.compile(&guest_directory).unwrap(); - assert!(!program.elf.is_empty(), "ELF bytes should not be empty."); - } -} diff --git a/crates/ere-risc0/src/compile_stock_rust.rs b/crates/ere-risc0/src/compile_stock_rust.rs deleted file mode 100644 index 7ef5c10..0000000 --- a/crates/ere-risc0/src/compile_stock_rust.rs +++ /dev/null @@ -1,78 +0,0 @@ -use crate::compile::Risc0Program; -use crate::error::CompileError; -use compile_utils::CargoBuildCmd; -use risc0_binfmt::ProgramBinary; -use std::path::Path; -use tracing::info; - -// TODO: Make this with `zkos` package building to avoid binary file storing in repo. -// File taken from https://github.com/risc0/risc0/blob/v3.0.3/risc0/zkos/v1compat/elfs/v1compat.elf -const V1COMPAT_ELF: &[u8] = include_bytes!("kernel_elf/v1compat.elf"); -const TARGET_TRIPLE: &str = "riscv32ima-unknown-none-elf"; -// Rust flags according to https://github.com/risc0/risc0/blob/v3.0.3/risc0/build/src/lib.rs#L455 -const RUSTFLAGS: &[&str] = &[ - "-C", - "passes=lower-atomic", // Only for rustc > 1.81 - "-C", - // Start of the code section - "link-arg=-Ttext=0x00200800", - "-C", - "link-arg=--fatal-warnings", - "-C", - "panic=abort", - "--cfg", - "getrandom_backend=\"custom\"", -]; -const CARGO_BUILD_OPTIONS: &[&str] = &[ - // For bare metal we have to build core and alloc - "-Zbuild-std=core,alloc", -]; - -pub fn compile_risc0_program_stock_rust( - guest_directory: &Path, - toolchain: &String, -) -> Result { - wrap_into_risc0_program(compile_program_stock_rust(guest_directory, toolchain)?) -} - -fn compile_program_stock_rust( - guest_directory: &Path, - toolchain: &String, -) -> Result, CompileError> { - let elf = CargoBuildCmd::new() - .toolchain(toolchain) - .build_options(CARGO_BUILD_OPTIONS) - .rustflags(RUSTFLAGS) - .exec(guest_directory, TARGET_TRIPLE)?; - - Ok(elf) -} - -fn wrap_into_risc0_program(elf: Vec) -> Result { - let program = ProgramBinary::new(elf.as_slice(), V1COMPAT_ELF); - let image_id = program.compute_image_id()?; - info!("Risc0 program compiled OK - {} bytes", elf.len()); - info!("Image ID - {image_id}"); - - Ok(Risc0Program { - elf: program.encode(), - image_id, - }) -} - -#[cfg(test)] -mod tests { - use crate::compile_stock_rust::compile_risc0_program_stock_rust; - use test_utils::host::testing_guest_directory; - - #[test] - fn test_stock_compiler_impl() { - let guest_directory = testing_guest_directory("risc0", "stock_nightly_no_std"); - let result = compile_risc0_program_stock_rust(&guest_directory, &"nightly".to_string()); - assert!(result.is_ok(), "Risc0 guest program compilation failure."); - assert!( - !result.unwrap().elf.is_empty(), - "ELF bytes should not be empty." - ); - } -} diff --git a/crates/ere-risc0/src/compiler.rs b/crates/ere-risc0/src/compiler.rs new file mode 100644 index 0000000..e387252 --- /dev/null +++ b/crates/ere-risc0/src/compiler.rs @@ -0,0 +1,14 @@ +use risc0_zkvm::Digest; +use serde::{Deserialize, Serialize}; + +mod rust_rv32ima; +mod rust_rv32ima_customized; + +pub use rust_rv32ima::RustRv32ima; +pub use rust_rv32ima_customized::RustRv32imaCustomized; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Risc0Program { + pub(crate) elf: Vec, + pub(crate) image_id: Digest, +} diff --git a/crates/ere-risc0/src/compiler/rust_rv32ima.rs b/crates/ere-risc0/src/compiler/rust_rv32ima.rs new file mode 100644 index 0000000..16ca589 --- /dev/null +++ b/crates/ere-risc0/src/compiler/rust_rv32ima.rs @@ -0,0 +1,86 @@ +use crate::compiler::Risc0Program; +use crate::error::{CompileError, Risc0Error}; +use compile_utils::CargoBuildCmd; +use risc0_binfmt::ProgramBinary; +use std::env; +use std::path::Path; +use tracing::info; +use zkvm_interface::Compiler; + +// TODO: Make this with `zkos` package building to avoid binary file storing in repo. +// File taken from https://github.com/risc0/risc0/blob/v3.0.3/risc0/zkos/v1compat/elfs/v1compat.elf +const V1COMPAT_ELF: &[u8] = include_bytes!("rust_rv32ima/v1compat.elf"); +const TARGET_TRIPLE: &str = "riscv32ima-unknown-none-elf"; +// Rust flags according to https://github.com/risc0/risc0/blob/v3.0.3/risc0/build/src/lib.rs#L455 +const RUSTFLAGS: &[&str] = &[ + "-C", + "passes=lower-atomic", // Only for rustc > 1.81 + "-C", + // Start of the code section + "link-arg=-Ttext=0x00200800", + "-C", + "link-arg=--fatal-warnings", + "-C", + "panic=abort", + "--cfg", + "getrandom_backend=\"custom\"", +]; +const CARGO_BUILD_OPTIONS: &[&str] = &[ + // For bare metal we have to build core and alloc + "-Zbuild-std=core,alloc", +]; + +/// Compiler for Rust guest program to RV32IMA architecture. +pub struct RustRv32ima; + +impl Compiler for RustRv32ima { + type Error = Risc0Error; + + type Program = Risc0Program; + + fn compile(&self, guest_directory: &Path) -> Result { + let toolchain = env::var("ERE_RUST_TOOLCHAIN").unwrap_or_else(|_| "nightly".into()); + let elf = CargoBuildCmd::new() + .toolchain(toolchain) + .build_options(CARGO_BUILD_OPTIONS) + .rustflags(RUSTFLAGS) + .exec(guest_directory, TARGET_TRIPLE) + .map_err(CompileError::CompileUtilError)?; + + let program = ProgramBinary::new(elf.as_slice(), V1COMPAT_ELF); + let image_id = program + .compute_image_id() + .map_err(CompileError::ImageIDCalculationFailure)?; + + info!("Risc0 program compiled OK - {} bytes", elf.len()); + info!("Image ID - {image_id}"); + + Ok(Risc0Program { + elf: program.encode(), + image_id, + }) + } +} + +#[cfg(test)] +mod tests { + use crate::{EreRisc0, compiler::RustRv32ima}; + use test_utils::host::testing_guest_directory; + use zkvm_interface::{Compiler, Input, ProverResourceType, zkVM}; + + #[test] + fn test_compile() { + let guest_directory = testing_guest_directory("risc0", "stock_nightly_no_std"); + let program = RustRv32ima.compile(&guest_directory).unwrap(); + assert!(!program.elf.is_empty(), "ELF bytes should not be empty."); + } + + #[test] + fn test_execute() { + let guest_directory = testing_guest_directory("risc0", "stock_nightly_no_std"); + let program = RustRv32ima.compile(&guest_directory).unwrap(); + let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap(); + + zkvm.execute(&Input::new()).unwrap(); + } +} diff --git a/crates/ere-risc0/src/kernel_elf/v1compat.elf b/crates/ere-risc0/src/compiler/rust_rv32ima/v1compat.elf similarity index 100% rename from crates/ere-risc0/src/kernel_elf/v1compat.elf rename to crates/ere-risc0/src/compiler/rust_rv32ima/v1compat.elf diff --git a/crates/ere-risc0/src/compiler/rust_rv32ima_customized.rs b/crates/ere-risc0/src/compiler/rust_rv32ima_customized.rs new file mode 100644 index 0000000..37df1dd --- /dev/null +++ b/crates/ere-risc0/src/compiler/rust_rv32ima_customized.rs @@ -0,0 +1,63 @@ +use crate::{ + compiler::Risc0Program, + error::{CompileError, Risc0Error}, +}; +use compile_utils::cargo_metadata; +use risc0_build::GuestOptions; +use std::path::Path; +use tracing::info; +use zkvm_interface::Compiler; + +/// Compiler for Rust guest program to RV32IMA architecture, using customized +/// Rust toolchain of Risc0. +pub struct RustRv32imaCustomized; + +impl Compiler for RustRv32imaCustomized { + type Error = Risc0Error; + + type Program = Risc0Program; + + fn compile(&self, guest_directory: &Path) -> Result { + info!("Compiling Risc0 program at {}", guest_directory.display()); + + let metadata = cargo_metadata(guest_directory).map_err(CompileError::CompileUtilError)?; + let package = metadata.root_package().unwrap(); + + // Use `risc0_build::build_package` to build package instead of calling + // `cargo-risczero build` for the `unstable` features. + let guest = risc0_build::build_package( + package, + &metadata.target_directory, + GuestOptions::default(), + ) + .map_err(|source| CompileError::BuildFailure { + source, + crate_path: guest_directory.to_path_buf(), + })? + .into_iter() + .next() + .ok_or(CompileError::Risc0BuildMissingGuest)?; + + let elf = guest.elf.to_vec(); + let image_id = guest.image_id; + + info!("Risc0 program compiled OK - {} bytes", elf.len()); + info!("Image ID - {image_id}"); + + Ok(Risc0Program { elf, image_id }) + } +} + +#[cfg(test)] +mod tests { + use crate::compiler::RustRv32imaCustomized; + use test_utils::host::testing_guest_directory; + use zkvm_interface::Compiler; + + #[test] + fn test_compile() { + let guest_directory = testing_guest_directory("risc0", "basic"); + let program = RustRv32imaCustomized.compile(&guest_directory).unwrap(); + assert!(!program.elf.is_empty(), "ELF bytes should not be empty."); + } +} diff --git a/crates/ere-risc0/src/error.rs b/crates/ere-risc0/src/error.rs index b168e5e..19b0d75 100644 --- a/crates/ere-risc0/src/error.rs +++ b/crates/ere-risc0/src/error.rs @@ -18,7 +18,7 @@ pub enum CompileError { #[error("`risc0_build::build_package` succeeded but failed to find guest")] Risc0BuildMissingGuest, #[error("ELF binary image calculation failure : {0}")] - ImageIDCalculationFailure(#[from] anyhow::Error), + ImageIDCalculationFailure(anyhow::Error), #[error(transparent)] CompileUtilError(#[from] compile_utils::CompileError), } diff --git a/crates/ere-risc0/src/lib.rs b/crates/ere-risc0/src/lib.rs index d92e428..e114fb7 100644 --- a/crates/ere-risc0/src/lib.rs +++ b/crates/ere-risc0/src/lib.rs @@ -1,29 +1,22 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] -use crate::{ - compile::{Risc0Program, compile_risc0_program}, - compile_stock_rust::compile_risc0_program_stock_rust, - error::Risc0Error, - output::deserialize_from, -}; +use crate::{compiler::Risc0Program, output::deserialize_from}; use borsh::{BorshDeserialize, BorshSerialize}; use risc0_zkvm::{ DEFAULT_MAX_PO2, DefaultProver, ExecutorEnv, ExecutorEnvBuilder, ExternalProver, InnerReceipt, Journal, ProverOpts, Receipt, ReceiptClaim, SuccinctReceipt, default_executor, default_prover, }; use serde::{Deserialize, Serialize, de::DeserializeOwned}; -use std::{env, io::Read, ops::RangeInclusive, path::Path, rc::Rc, time::Instant}; +use std::{env, io::Read, ops::RangeInclusive, rc::Rc, time::Instant}; use zkvm_interface::{ - Compiler, Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, - ProverResourceType, PublicValues, zkVM, zkVMError, + Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType, + PublicValues, zkVM, zkVMError, }; include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); -mod compile; -mod compile_stock_rust; - -mod error; +pub mod compiler; +pub mod error; mod output; /// Default logarithmic segment size from [`DEFAULT_SEGMENT_LIMIT_PO2`]. @@ -52,26 +45,6 @@ const DEFAULT_KECCAK_PO2: usize = 17; /// [`KECCAK_PO2_RANGE`]: https://github.com/risc0/risc0/blob/v3.0.3/risc0/circuit/keccak/src/lib.rs#L29. const KECCAK_PO2_RANGE: RangeInclusive = 14..=18; -#[allow(non_camel_case_types)] -pub struct RV32_IM_RISC0_ZKVM_ELF; - -impl Compiler for RV32_IM_RISC0_ZKVM_ELF { - type Error = Risc0Error; - - type Program = Risc0Program; - - fn compile(&self, guest_directory: &Path) -> Result { - let toolchain = env::var("ERE_GUEST_TOOLCHAIN").unwrap_or_else(|_error| "risc0".into()); - match toolchain.as_str() { - "risc0" => Ok(compile_risc0_program(guest_directory)?), - _ => Ok(compile_risc0_program_stock_rust( - guest_directory, - &toolchain, - )?), - } - } -} - #[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)] pub struct Risc0ProofWithPublicValues { /// The aggregated proof generated by the Risc0 zkVM. @@ -100,17 +73,14 @@ impl From for Receipt { } pub struct EreRisc0 { - program: ::Program, + program: Risc0Program, resource: ProverResourceType, segment_po2: usize, keccak_po2: usize, } impl EreRisc0 { - pub fn new( - program: ::Program, - resource: ProverResourceType, - ) -> Result { + pub fn new(program: Risc0Program, resource: ProverResourceType) -> Result { if matches!(resource, ProverResourceType::Network(_)) { panic!( "Network proving not yet implemented for RISC Zero. Use CPU or GPU resource type." @@ -267,18 +237,22 @@ fn serialize_inputs(env: &mut ExecutorEnvBuilder, inputs: &Input) -> Result<(), #[cfg(test)] mod tests { - use super::*; + use crate::{ + EreRisc0, + compiler::{Risc0Program, RustRv32imaCustomized}, + }; use std::sync::OnceLock; use test_utils::host::{ BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, }; + use zkvm_interface::{Compiler, Input, ProverResourceType, zkVM}; static BASIC_PROGRAM: OnceLock = OnceLock::new(); fn basic_program() -> Risc0Program { BASIC_PROGRAM .get_or_init(|| { - RV32_IM_RISC0_ZKVM_ELF + RustRv32imaCustomized .compile(&testing_guest_directory("risc0", "basic")) .unwrap() }) @@ -294,17 +268,6 @@ mod tests { run_zkvm_execute(&zkvm, &io); } - #[test] - fn test_execute_nightly() { - let guest_directory = testing_guest_directory("risc0", "stock_nightly_no_std"); - let program = - compile_risc0_program_stock_rust(&guest_directory, &"nightly".to_string()).unwrap(); - let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap(); - - let result = zkvm.execute(&BasicProgramIo::empty()); - assert!(result.is_ok(), "Risc0 execution failure"); - } - #[test] fn test_execute_invalid_inputs() { let program = basic_program(); @@ -344,7 +307,7 @@ mod tests { #[test] fn test_aligned_allocs() { - let program = RV32_IM_RISC0_ZKVM_ELF + let program = RustRv32imaCustomized .compile(&testing_guest_directory("risc0", "allocs_alignment")) .unwrap(); diff --git a/crates/ere-sp1/src/compile.rs b/crates/ere-sp1/src/compile.rs deleted file mode 100644 index 0356797..0000000 --- a/crates/ere-sp1/src/compile.rs +++ /dev/null @@ -1,96 +0,0 @@ -use crate::compile_stock_rust::stock_rust_compile; -use crate::error::CompileError; -use std::{fs, path::Path, process::Command}; -use tempfile::tempdir; -use tracing::info; - -fn sp1_compile(guest_directory: &Path) -> Result, CompileError> { - info!("Compiling SP1 program at {}", guest_directory.display()); - - if !guest_directory.exists() || !guest_directory.is_dir() { - return Err(CompileError::InvalidProgramPath( - guest_directory.to_path_buf(), - )); - } - - let guest_manifest_path = guest_directory.join("Cargo.toml"); - if !guest_manifest_path.exists() { - return Err(CompileError::CargoTomlMissing { - program_dir: guest_directory.to_path_buf(), - manifest_path: guest_manifest_path.clone(), - }); - } - - // ── build into a temp dir ───────────────────────────────────────────── - let temp_output_dir = tempdir()?; - let temp_output_dir_path = temp_output_dir.path(); - - info!( - "Running `cargo prove build` → dir: {}", - temp_output_dir_path.display(), - ); - - let status = Command::new("cargo") - .current_dir(guest_directory) - .args([ - "prove", - "build", - "--output-directory", - temp_output_dir_path.to_str().unwrap(), - "--elf-name", - "guest.elf", - ]) - .status() - .map_err(|e| CompileError::CargoProveBuild { - cwd: guest_directory.to_path_buf(), - source: e, - })?; - - if !status.success() { - return Err(CompileError::CargoProveBuildFailed { - status, - path: guest_directory.to_path_buf(), - }); - } - - let elf_path = temp_output_dir_path.join("guest.elf"); - if !elf_path.exists() { - return Err(CompileError::ElfNotFound(elf_path)); - } - - let elf_bytes = fs::read(&elf_path).map_err(|e| CompileError::ReadFile { - path: elf_path, - source: e, - })?; - info!("SP1 program compiled OK - {} bytes", elf_bytes.len()); - - Ok(elf_bytes) -} - -pub fn compile(guest_directory: &Path, toolchain: &String) -> Result, CompileError> { - match toolchain.as_str() { - "succinct" => sp1_compile(guest_directory), - _ => stock_rust_compile(guest_directory, toolchain), - } -} - -#[cfg(test)] -mod tests { - use crate::RV32_IM_SUCCINCT_ZKVM_ELF; - use crate::compile::compile; - use test_utils::host::testing_guest_directory; - use zkvm_interface::Compiler; - - #[test] - fn test_compiler_impl() { - let guest_directory = testing_guest_directory("sp1", "basic"); - let elf_bytes = RV32_IM_SUCCINCT_ZKVM_ELF.compile(&guest_directory).unwrap(); - assert!(!elf_bytes.is_empty(), "ELF bytes should not be empty."); - } - #[test] - fn test_stock_compiler_impl() { - let guest_directory = testing_guest_directory("sp1", "stock_nightly_no_std"); - let elf_bytes = compile(&guest_directory, &"nightly".to_string()).unwrap(); - assert!(!elf_bytes.is_empty(), "ELF bytes should not be empty."); - } -} diff --git a/crates/ere-sp1/src/compile_stock_rust.rs b/crates/ere-sp1/src/compile_stock_rust.rs deleted file mode 100644 index 93d1118..0000000 --- a/crates/ere-sp1/src/compile_stock_rust.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::error::CompileError; -use compile_utils::CargoBuildCmd; -use std::path::Path; - -const TARGET_TRIPLE: &str = "riscv32ima-unknown-none-elf"; -const RUSTFLAGS: &[&str] = &[ - "-C", - "passes=lower-atomic", // Only for rustc > 1.81 - "-C", - // Start of the code section - "link-arg=-Ttext=0x00201000", - "-C", - // The lowest memory location that will be used when your program is loaded - "link-arg=--image-base=0x00200800", - "-C", - "panic=abort", - "--cfg", - "getrandom_backend=\"custom\"", - "-C", - "llvm-args=-misched-prera-direction=bottomup", - "-C", - "llvm-args=-misched-postra-direction=bottomup", -]; - -const CARGO_BUILD_OPTIONS: &[&str] = &[ - // For bare metal we have to build core and alloc - "-Zbuild-std=core,alloc", -]; - -pub fn stock_rust_compile( - guest_directory: &Path, - toolchain: &String, -) -> Result, CompileError> { - let elf = CargoBuildCmd::new() - .toolchain(toolchain) - .build_options(CARGO_BUILD_OPTIONS) - .rustflags(RUSTFLAGS) - .exec(guest_directory, TARGET_TRIPLE)?; - - Ok(elf) -} diff --git a/crates/ere-sp1/src/compiler.rs b/crates/ere-sp1/src/compiler.rs new file mode 100644 index 0000000..6c0c1cf --- /dev/null +++ b/crates/ere-sp1/src/compiler.rs @@ -0,0 +1,7 @@ +mod rust_rv32ima; +mod rust_rv32ima_customized; + +pub use rust_rv32ima::RustRv32ima; +pub use rust_rv32ima_customized::RustRv32imaCustomized; + +pub type SP1Program = Vec; diff --git a/crates/ere-sp1/src/compiler/rust_rv32ima.rs b/crates/ere-sp1/src/compiler/rust_rv32ima.rs new file mode 100644 index 0000000..6ffe4c0 --- /dev/null +++ b/crates/ere-sp1/src/compiler/rust_rv32ima.rs @@ -0,0 +1,75 @@ +use crate::{ + compiler::SP1Program, + error::{CompileError, SP1Error}, +}; +use compile_utils::CargoBuildCmd; +use std::{env, path::Path}; +use zkvm_interface::Compiler; + +const TARGET_TRIPLE: &str = "riscv32ima-unknown-none-elf"; +const RUSTFLAGS: &[&str] = &[ + "-C", + "passes=lower-atomic", // Only for rustc > 1.81 + "-C", + // Start of the code section + "link-arg=-Ttext=0x00201000", + "-C", + // The lowest memory location that will be used when your program is loaded + "link-arg=--image-base=0x00200800", + "-C", + "panic=abort", + "--cfg", + "getrandom_backend=\"custom\"", + "-C", + "llvm-args=-misched-prera-direction=bottomup", + "-C", + "llvm-args=-misched-postra-direction=bottomup", +]; + +const CARGO_BUILD_OPTIONS: &[&str] = &[ + // For bare metal we have to build core and alloc + "-Zbuild-std=core,alloc", +]; + +/// Compiler for Rust guest program to RV32IMA architecture. +pub struct RustRv32ima; + +impl Compiler for RustRv32ima { + type Error = SP1Error; + + type Program = SP1Program; + + fn compile(&self, guest_directory: &Path) -> Result { + let toolchain = env::var("ERE_RUST_TOOLCHAIN").unwrap_or_else(|_| "nightly".into()); + let elf = CargoBuildCmd::new() + .toolchain(toolchain) + .build_options(CARGO_BUILD_OPTIONS) + .rustflags(RUSTFLAGS) + .exec(guest_directory, TARGET_TRIPLE) + .map_err(CompileError::CompileUtilError)?; + Ok(elf) + } +} + +#[cfg(test)] +mod tests { + use crate::{EreSP1, compiler::RustRv32ima}; + use test_utils::host::testing_guest_directory; + use zkvm_interface::{Compiler, Input, ProverResourceType, zkVM}; + + #[test] + fn test_compile() { + let guest_directory = testing_guest_directory("sp1", "stock_nightly_no_std"); + let elf = RustRv32ima.compile(&guest_directory).unwrap(); + assert!(!elf.is_empty(), "ELF bytes should not be empty."); + } + + #[test] + fn test_execute() { + let guest_directory = testing_guest_directory("sp1", "stock_nightly_no_std"); + let program = RustRv32ima.compile(&guest_directory).unwrap(); + let zkvm = EreSP1::new(program, ProverResourceType::Cpu); + + zkvm.execute(&Input::new()).unwrap(); + } +} diff --git a/crates/ere-sp1/src/compiler/rust_rv32ima_customized.rs b/crates/ere-sp1/src/compiler/rust_rv32ima_customized.rs new file mode 100644 index 0000000..29a59fd --- /dev/null +++ b/crates/ere-sp1/src/compiler/rust_rv32ima_customized.rs @@ -0,0 +1,95 @@ +use crate::{ + compiler::SP1Program, + error::{CompileError, SP1Error}, +}; +use std::{fs, path::Path, process::Command}; +use tempfile::tempdir; +use tracing::info; +use zkvm_interface::Compiler; + +/// Compiler for Rust guest program to RV32IMA architecture, using customized +/// Rust toolchain of Succinct. +pub struct RustRv32imaCustomized; + +impl Compiler for RustRv32imaCustomized { + type Error = SP1Error; + + type Program = SP1Program; + + fn compile(&self, guest_directory: &Path) -> Result { + info!("Compiling SP1 program at {}", guest_directory.display()); + + if !guest_directory.exists() || !guest_directory.is_dir() { + return Err(CompileError::InvalidProgramPath( + guest_directory.to_path_buf(), + ))?; + } + + let guest_manifest_path = guest_directory.join("Cargo.toml"); + if !guest_manifest_path.exists() { + return Err(CompileError::CargoTomlMissing { + program_dir: guest_directory.to_path_buf(), + manifest_path: guest_manifest_path.clone(), + })?; + } + + // ── build into a temp dir ───────────────────────────────────────────── + let temp_output_dir = tempdir().map_err(CompileError::TempDir)?; + let temp_output_dir_path = temp_output_dir.path(); + + info!( + "Running `cargo prove build` → dir: {}", + temp_output_dir_path.display(), + ); + + let status = Command::new("cargo") + .current_dir(guest_directory) + .args([ + "prove", + "build", + "--output-directory", + temp_output_dir_path.to_str().unwrap(), + "--elf-name", + "guest.elf", + ]) + .status() + .map_err(|e| CompileError::CargoProveBuild { + cwd: guest_directory.to_path_buf(), + source: e, + })?; + + if !status.success() { + return Err(CompileError::CargoProveBuildFailed { + status, + path: guest_directory.to_path_buf(), + })?; + } + + let elf_path = temp_output_dir_path.join("guest.elf"); + if !elf_path.exists() { + return Err(CompileError::ElfNotFound(elf_path))?; + } + + let elf_bytes = fs::read(&elf_path).map_err(|e| CompileError::ReadFile { + path: elf_path, + source: e, + })?; + info!("SP1 program compiled OK - {} bytes", elf_bytes.len()); + + Ok(elf_bytes) + } +} + +#[cfg(test)] +mod tests { + use crate::compiler::RustRv32imaCustomized; + use test_utils::host::testing_guest_directory; + use zkvm_interface::Compiler; + + #[test] + fn test_compile() { + let guest_directory = testing_guest_directory("sp1", "basic"); + let elf_bytes = RustRv32imaCustomized.compile(&guest_directory).unwrap(); + assert!(!elf_bytes.is_empty(), "ELF bytes should not be empty."); + } +} diff --git a/crates/ere-sp1/src/lib.rs b/crates/ere-sp1/src/lib.rs index f1aa5c4..049cc53 100644 --- a/crates/ere-sp1/src/lib.rs +++ b/crates/ere-sp1/src/lib.rs @@ -1,26 +1,27 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] -use std::{io::Read, path::Path, time::Instant}; - +use crate::{ + compiler::SP1Program, + error::{ExecuteError, ProveError, SP1Error, VerifyError}, +}; use serde::de::DeserializeOwned; use sp1_sdk::{ CpuProver, CudaProver, NetworkProver, Prover, ProverClient, SP1ProofWithPublicValues, SP1ProvingKey, SP1Stdin, SP1VerifyingKey, }; +use std::{io::Read, time::Instant}; use tracing::info; use zkvm_interface::{ - Compiler, Input, InputItem, NetworkProverConfig, ProgramExecutionReport, ProgramProvingReport, - Proof, ProverResourceType, PublicValues, zkVM, zkVMError, + Input, InputItem, NetworkProverConfig, ProgramExecutionReport, ProgramProvingReport, Proof, + ProverResourceType, PublicValues, zkVM, zkVMError, }; include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); -mod compile; -mod compile_stock_rust; - -mod error; -use error::{ExecuteError, ProveError, SP1Error, VerifyError}; +pub mod compiler; +pub mod error; +#[allow(clippy::large_enum_variant)] enum ProverType { Cpu(CpuProver), Gpu(CudaProver), @@ -28,10 +29,7 @@ enum ProverType { } impl ProverType { - fn setup( - &self, - program: &::Program, - ) -> (SP1ProvingKey, SP1VerifyingKey) { + fn setup(&self, program: &SP1Program) -> (SP1ProvingKey, SP1VerifyingKey) { match self { ProverType::Cpu(cpu_prover) => cpu_prover.setup(program), ProverType::Gpu(cuda_prover) => cuda_prover.setup(program), @@ -41,7 +39,7 @@ impl ProverType { fn execute( &self, - program: &::Program, + program: &SP1Program, input: &SP1Stdin, ) -> Result<(sp1_sdk::SP1PublicValues, sp1_sdk::ExecutionReport), SP1Error> { let cpu_executor_builder = match self { @@ -83,10 +81,8 @@ impl ProverType { } } -#[allow(non_camel_case_types)] -pub struct RV32_IM_SUCCINCT_ZKVM_ELF; pub struct EreSP1 { - program: ::Program, + program: SP1Program, /// Proving key pk: SP1ProvingKey, /// Verification key @@ -103,18 +99,6 @@ pub struct EreSP1 { // For more context see: https://github.com/eth-act/zkevm-benchmark-workload/issues/54 } -impl Compiler for RV32_IM_SUCCINCT_ZKVM_ELF { - type Error = SP1Error; - - type Program = Vec; - - fn compile(&self, guest_directory: &Path) -> Result { - let toolchain = - std::env::var("ERE_GUEST_TOOLCHAIN").unwrap_or_else(|_error| "succinct".into()); - compile::compile(guest_directory, &toolchain).map_err(SP1Error::from) - } -} - impl EreSP1 { fn create_network_prover(config: &NetworkProverConfig) -> NetworkProver { let mut builder = ProverClient::builder().network(); @@ -148,10 +132,7 @@ impl EreSP1 { } } - pub fn new( - program: ::Program, - resource: ProverResourceType, - ) -> Self { + pub fn new(program: SP1Program, resource: ProverResourceType) -> Self { let (pk, vk) = Self::create_client(&resource).setup(&program); Self { @@ -249,19 +230,19 @@ fn serialize_inputs(stdin: &mut SP1Stdin, inputs: &Input) { #[cfg(test)] mod tests { - use super::*; - use crate::compile::compile; + use crate::{EreSP1, compiler::RustRv32imaCustomized}; use std::{panic, sync::OnceLock}; use test_utils::host::{ BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, }; + use zkvm_interface::{Compiler, NetworkProverConfig, ProverResourceType, zkVM}; static BASIC_PROGRAM: OnceLock> = OnceLock::new(); fn basic_program() -> Vec { BASIC_PROGRAM .get_or_init(|| { - RV32_IM_SUCCINCT_ZKVM_ELF + RustRv32imaCustomized .compile(&testing_guest_directory("sp1", "basic")) .unwrap() }) @@ -277,15 +258,6 @@ mod tests { run_zkvm_execute(&zkvm, &io); } - #[test] - fn test_execute_nightly() { - let guest_directory = testing_guest_directory("sp1", "stock_nightly_no_std"); - let program = compile(&guest_directory, &"nightly".to_string()).unwrap(); - let zkvm = EreSP1::new(program, ProverResourceType::Cpu); - - zkvm.execute(&Input::new()).unwrap(); - } - #[test] fn test_execute_invalid_inputs() { let program = basic_program(); diff --git a/crates/ere-ziren/Cargo.toml b/crates/ere-ziren/Cargo.toml index 74aae77..0012e84 100644 --- a/crates/ere-ziren/Cargo.toml +++ b/crates/ere-ziren/Cargo.toml @@ -7,7 +7,6 @@ license.workspace = true [dependencies] bincode.workspace = true -cargo_metadata.workspace = true serde.workspace = true thiserror.workspace = true tracing.workspace = true @@ -16,6 +15,7 @@ tracing.workspace = true zkm-sdk = { workspace = true } # Local dependencies +compile-utils.workspace = true zkvm-interface = { workspace = true } [dev-dependencies] diff --git a/crates/ere-ziren/src/compiler.rs b/crates/ere-ziren/src/compiler.rs new file mode 100644 index 0000000..8664ba7 --- /dev/null +++ b/crates/ere-ziren/src/compiler.rs @@ -0,0 +1,5 @@ +mod rust_mips32r2_customized; + +pub use rust_mips32r2_customized::RustMips32r2Customized; + +pub type ZirenProgram = Vec; diff --git a/crates/ere-ziren/src/compiler/rust_mips32r2_customized.rs b/crates/ere-ziren/src/compiler/rust_mips32r2_customized.rs new file mode 100644 index 0000000..276877b --- /dev/null +++ b/crates/ere-ziren/src/compiler/rust_mips32r2_customized.rs @@ -0,0 +1,99 @@ +use crate::{ + compiler::ZirenProgram, + error::{CompileError, ZirenError}, +}; +use compile_utils::cargo_metadata; +use std::{ + fs, + path::{Path, PathBuf}, + process::Command, +}; +use zkvm_interface::Compiler; + +const ZKM_TOOLCHAIN: &str = "zkm"; + +/// Compiler for Rust guest program to MIPS32R2 architecture, using customized +/// Rust toolchain of ZKM. +pub struct RustMips32r2Customized; + +impl Compiler for RustMips32r2Customized { + type Error = ZirenError; + + type Program = ZirenProgram; + + fn compile(&self, guest_directory: &Path) -> Result { + let metadata = cargo_metadata(guest_directory).map_err(CompileError::CompileUtilError)?; + let package = metadata.root_package().unwrap(); + + let rustc = { + let output = Command::new("rustc") + .env("RUSTUP_TOOLCHAIN", ZKM_TOOLCHAIN) + .args(["--print", "sysroot"]) + .output() + .map_err(CompileError::RustcSysrootFailed)?; + + if !output.status.success() { + return Err(CompileError::RustcSysrootExitNonZero { + status: output.status, + stdout: String::from_utf8_lossy(&output.stdout).to_string(), + stderr: String::from_utf8_lossy(&output.stderr).to_string(), + })?; + } + + PathBuf::from(String::from_utf8_lossy(&output.stdout).trim()) + .join("bin") + .join("rustc") + }; + + // Use `cargo ziren build` instead of using crate `zkm-build`, because + // it exits if the underlying `cargo build` fails, and there is no way + // to recover. + let output = Command::new("cargo") + .current_dir(guest_directory) + .env("RUSTC", rustc) + .env("ZIREN_ZKM_CC", "mipsel-zkm-zkvm-elf-gcc") + .args(["ziren", "build"]) + .output() + .map_err(CompileError::CargoZirenBuildFailed)?; + + if !output.status.success() { + return Err(CompileError::CargoZirenBuildExitNonZero { + status: output.status, + stdout: String::from_utf8_lossy(&output.stdout).to_string(), + stderr: String::from_utf8_lossy(&output.stderr).to_string(), + })?; + } + + let elf_path = String::from_utf8_lossy(&output.stdout) + .lines() + .find_map(|line| { + let line = line.strip_prefix("cargo:rustc-env=ZKM_ELF_")?; + let (package_name, elf_path) = line.split_once("=")?; + (package_name == package.name).then(|| PathBuf::from(elf_path)) + }) + .ok_or_else(|| CompileError::GuestNotFound { + name: package.name.clone(), + })?; + + let elf = fs::read(&elf_path).map_err(|source| CompileError::ReadFile { + path: elf_path, + source, + })?; + + Ok(elf) + } +} + +#[cfg(test)] +mod tests { + use crate::compiler::RustMips32r2Customized; + use test_utils::host::testing_guest_directory; + use zkvm_interface::Compiler; + + #[test] + fn test_compile() { + let guest_directory = testing_guest_directory("ziren", "basic"); + let elf_bytes = RustMips32r2Customized.compile(&guest_directory).unwrap(); + assert!(!elf_bytes.is_empty(), "ELF bytes should not be empty."); + } +} diff --git a/crates/ere-ziren/src/error.rs b/crates/ere-ziren/src/error.rs index 8b20041..98b6595 100644 --- a/crates/ere-ziren/src/error.rs +++ b/crates/ere-ziren/src/error.rs @@ -26,10 +26,6 @@ pub enum ZirenError { #[derive(Debug, Error)] pub enum CompileError { - #[error("`cargo metadata` failed: {0}")] - MetadataCommand(#[from] cargo_metadata::Error), - #[error("Failed to find root package")] - MissingRootPackage, #[error("`RUSTUP_TOOLCHAIN=zkm rustc --print sysroot` failed to execute: {0}")] RustcSysrootFailed(#[source] io::Error), #[error( @@ -58,6 +54,8 @@ pub enum CompileError { #[source] source: std::io::Error, }, + #[error(transparent)] + CompileUtilError(#[from] compile_utils::CompileError), } #[derive(Debug, Error)] diff --git a/crates/ere-ziren/src/lib.rs b/crates/ere-ziren/src/lib.rs index e16f246..335954b 100644 --- a/crates/ere-ziren/src/lib.rs +++ b/crates/ere-ziren/src/lib.rs @@ -1,114 +1,34 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] -use crate::error::{CompileError, ExecuteError, ProveError, VerifyError, ZirenError}; -use cargo_metadata::MetadataCommand; -use serde::de::DeserializeOwned; -use std::{ - fs, - io::Read, - path::{Path, PathBuf}, - process::Command, - time::Instant, +use crate::{ + compiler::ZirenProgram, + error::{ExecuteError, ProveError, VerifyError, ZirenError}, }; +use serde::de::DeserializeOwned; +use std::{io::Read, time::Instant}; use tracing::info; use zkm_sdk::{ CpuProver, Prover, ZKMProofKind, ZKMProofWithPublicValues, ZKMProvingKey, ZKMStdin, ZKMVerifyingKey, }; use zkvm_interface::{ - Compiler, Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, - ProverResourceType, PublicValues, zkVM, zkVMError, + Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType, + PublicValues, zkVM, zkVMError, }; include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); -mod error; -const ZKM_TOOLCHAIN: &str = "zkm"; - -#[allow(non_camel_case_types)] -pub struct MIPS32R2_ZKM_ZKVM_ELF; - -impl Compiler for MIPS32R2_ZKM_ZKVM_ELF { - type Error = CompileError; - - type Program = Vec; - - fn compile(&self, guest_directory: &Path) -> Result { - let metadata = MetadataCommand::new().current_dir(guest_directory).exec()?; - let package = metadata - .root_package() - .ok_or(CompileError::MissingRootPackage)?; - - let rustc = { - let output = Command::new("rustc") - .env("RUSTUP_TOOLCHAIN", ZKM_TOOLCHAIN) - .args(["--print", "sysroot"]) - .output() - .map_err(CompileError::RustcSysrootFailed)?; - - if !output.status.success() { - return Err(CompileError::RustcSysrootExitNonZero { - status: output.status, - stdout: String::from_utf8_lossy(&output.stdout).to_string(), - stderr: String::from_utf8_lossy(&output.stderr).to_string(), - }); - } - - PathBuf::from(String::from_utf8_lossy(&output.stdout).trim()) - .join("bin") - .join("rustc") - }; - - // Use `cargo ziren build` instead of using crate `zkm-build`, because - // it exits if the underlying `cargo build` fails, and there is no way - // to recover. - let output = Command::new("cargo") - .current_dir(guest_directory) - .env("RUSTC", rustc) - .env("ZIREN_ZKM_CC", "mipsel-zkm-zkvm-elf-gcc") - .args(["ziren", "build"]) - .output() - .map_err(CompileError::CargoZirenBuildFailed)?; - - if !output.status.success() { - return Err(CompileError::CargoZirenBuildExitNonZero { - status: output.status, - stdout: String::from_utf8_lossy(&output.stdout).to_string(), - stderr: String::from_utf8_lossy(&output.stderr).to_string(), - }); - } - - let elf_path = String::from_utf8_lossy(&output.stdout) - .lines() - .find_map(|line| { - let line = line.strip_prefix("cargo:rustc-env=ZKM_ELF_")?; - let (package_name, elf_path) = line.split_once("=")?; - (package_name == package.name).then(|| PathBuf::from(elf_path)) - }) - .ok_or_else(|| CompileError::GuestNotFound { - name: package.name.clone(), - })?; - - let elf = fs::read(&elf_path).map_err(|source| CompileError::ReadFile { - path: elf_path, - source, - })?; - - Ok(elf) - } -} +pub mod compiler; +pub mod error; pub struct EreZiren { - program: ::Program, + program: ZirenProgram, pk: ZKMProvingKey, vk: ZKMVerifyingKey, } impl EreZiren { - pub fn new( - program: ::Program, - resource: ProverResourceType, - ) -> Self { + pub fn new(program: ZirenProgram, resource: ProverResourceType) -> Self { if matches!( resource, ProverResourceType::Gpu | ProverResourceType::Network(_) @@ -212,18 +132,19 @@ fn serialize_inputs(stdin: &mut ZKMStdin, inputs: &Input) { #[cfg(test)] mod tests { - use super::*; + use crate::{EreZiren, compiler::RustMips32r2Customized}; use std::{panic, sync::OnceLock}; use test_utils::host::{ BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, }; + use zkvm_interface::{Compiler, ProverResourceType, zkVM}; static BASIC_PROGRAM: OnceLock> = OnceLock::new(); fn basic_program() -> Vec { BASIC_PROGRAM .get_or_init(|| { - MIPS32R2_ZKM_ZKVM_ELF + RustMips32r2Customized .compile(&testing_guest_directory("ziren", "basic")) .unwrap() }) diff --git a/crates/ere-zisk/Cargo.toml b/crates/ere-zisk/Cargo.toml index 7a4d90c..aef6050 100644 --- a/crates/ere-zisk/Cargo.toml +++ b/crates/ere-zisk/Cargo.toml @@ -13,10 +13,10 @@ serde = { workspace = true, features = ["derive"] } strum = { workspace = true, features = ["derive"] } tempfile.workspace = true thiserror.workspace = true -toml.workspace = true tracing.workspace = true # Local dependencies +compile-utils.workspace = true zkvm-interface.workspace = true [dev-dependencies] diff --git a/crates/ere-zisk/src/compile.rs b/crates/ere-zisk/src/compile.rs deleted file mode 100644 index f1a503e..0000000 --- a/crates/ere-zisk/src/compile.rs +++ /dev/null @@ -1,137 +0,0 @@ -use crate::error::ZiskError; -use std::{ - fs, - path::{Path, PathBuf}, - process::Command, -}; -use toml::Value as TomlValue; -use tracing::info; - -const ZISK_TOOLCHAIN: &str = "zisk"; -const ZISK_TARGET: &str = "riscv64ima-zisk-zkvm-elf"; - -/// Compile the guest crate and return raw ELF bytes. -pub fn compile_zisk_program(program_crate_path: &Path) -> Result, ZiskError> { - info!("Compiling ZisK program at {}", program_crate_path.display()); - - if !program_crate_path.exists() || !program_crate_path.is_dir() { - return Err(ZiskError::InvalidProgramPath( - program_crate_path.to_path_buf(), - )); - } - - let guest_manifest_path = program_crate_path.join("Cargo.toml"); - if !guest_manifest_path.exists() { - return Err(ZiskError::CargoTomlMissing { - program_dir: program_crate_path.to_path_buf(), - manifest_path: guest_manifest_path.clone(), - }); - } - - // ── read + parse Cargo.toml ─────────────────────────────────────────── - let manifest_content = - fs::read_to_string(&guest_manifest_path).map_err(|e| ZiskError::ReadFile { - path: guest_manifest_path.clone(), - source: e, - })?; - - let manifest_toml: TomlValue = - manifest_content - .parse::() - .map_err(|e| ZiskError::ParseCargoToml { - path: guest_manifest_path.clone(), - source: e, - })?; - - let program_name = manifest_toml - .get("package") - .and_then(|p| p.get("name")) - .and_then(|n| n.as_str()) - .ok_or_else(|| ZiskError::MissingPackageName { - path: guest_manifest_path.clone(), - })?; - - info!("Parsed program name: {program_name}"); - - // ── build ───────────────────────────────────────────────────────────── - // Get the path to ZisK toolchain's `rustc` so we could set the env - // `RUSTC=...` for `cargo` instead of using `cargo +zisk ...`. - let zisk_rustc = { - let output = Command::new("rustc") - .env("RUSTUP_TOOLCHAIN", ZISK_TOOLCHAIN) - .arg("--print") - .arg("sysroot") - .output() - .map_err(ZiskError::RustcSysroot)?; - PathBuf::from(String::from_utf8_lossy(&output.stdout).trim()) - .join("bin") - .join("rustc") - }; - - let status = Command::new("cargo") - .current_dir(program_crate_path) - .env("RUSTC", zisk_rustc) - .args(["build", "--release", "--target", ZISK_TARGET]) - .status() - .map_err(|e| ZiskError::CargoBuild { - cwd: program_crate_path.to_path_buf(), - source: e, - })?; - - if !status.success() { - return Err(ZiskError::CargoBuildFailed { - status, - path: program_crate_path.to_path_buf(), - }); - } - - // Get the workspace directory. - let program_workspace_path = { - let output = Command::new("cargo") - .current_dir(program_crate_path) - .arg("locate-project") - .arg("--workspace") - .arg("--message-format=plain") - .output() - .map_err(ZiskError::CargoLocateProject)?; - PathBuf::from( - String::from_utf8_lossy(&output.stdout) - .trim() - .strip_suffix("Cargo.toml") - .expect("location to be path to Cargo.toml"), - ) - }; - - let elf_path = program_workspace_path - .join("target") - .join("riscv64ima-zisk-zkvm-elf") - .join("release") - .join(program_name); - let elf_bytes = fs::read(&elf_path).map_err(|e| ZiskError::ReadFile { - path: elf_path.clone(), - source: e, - })?; - - Ok(elf_bytes) -} - -#[cfg(test)] -mod tests { - use crate::{RV64_IMA_ZISK_ZKVM_ELF, compile::compile_zisk_program}; - use test_utils::host::testing_guest_directory; - use zkvm_interface::Compiler; - - #[test] - fn test_compile() { - let guest_directory = testing_guest_directory("zisk", "basic"); - let elf_bytes = compile_zisk_program(&guest_directory).unwrap(); - assert!(!elf_bytes.is_empty(), "ELF bytes should not be empty."); - } - - #[test] - fn test_compiler_impl() { - let guest_directory = testing_guest_directory("zisk", "basic"); - let elf_bytes = RV64_IMA_ZISK_ZKVM_ELF.compile(&guest_directory).unwrap(); - assert!(!elf_bytes.is_empty(), "ELF bytes should not be empty."); - } -} diff --git a/crates/ere-zisk/src/compiler.rs b/crates/ere-zisk/src/compiler.rs new file mode 100644 index 0000000..d985201 --- /dev/null +++ b/crates/ere-zisk/src/compiler.rs @@ -0,0 +1,5 @@ +mod rust_rv64ima_customized; + +pub use rust_rv64ima_customized::RustRv64imaCustomized; + +pub type ZiskProgram = Vec; diff --git a/crates/ere-zisk/src/compiler/rust_rv64ima_customized.rs b/crates/ere-zisk/src/compiler/rust_rv64ima_customized.rs new file mode 100644 index 0000000..83ca7e7 --- /dev/null +++ b/crates/ere-zisk/src/compiler/rust_rv64ima_customized.rs @@ -0,0 +1,106 @@ +use crate::error::ZiskError; +use compile_utils::cargo_metadata; +use std::{ + fs, + path::{Path, PathBuf}, + process::Command, +}; +use tracing::info; +use zkvm_interface::Compiler; + +const ZISK_TOOLCHAIN: &str = "zisk"; +const ZISK_TARGET: &str = "riscv64ima-zisk-zkvm-elf"; + +/// Compiler for Rust guest program to RV64IMA architecture, using customized +/// Rust toolchain of ZisK. +pub struct RustRv64imaCustomized; + +impl Compiler for RustRv64imaCustomized { + type Error = ZiskError; + + type Program = Vec; + + fn compile(&self, guest_directory: &Path) -> Result { + info!("Compiling ZisK program at {}", guest_directory.display()); + + let metadata = cargo_metadata(guest_directory)?; + let package_name = &metadata.root_package().unwrap().name; + + info!("Parsed program name: {package_name}"); + + // ── build ───────────────────────────────────────────────────────────── + // Get the path to ZisK toolchain's `rustc` so we could set the env + // `RUSTC=...` for `cargo` instead of using `cargo +zisk ...`. + let zisk_rustc = { + let output = Command::new("rustc") + .env("RUSTUP_TOOLCHAIN", ZISK_TOOLCHAIN) + .arg("--print") + .arg("sysroot") + .output() + .map_err(ZiskError::RustcSysroot)?; + PathBuf::from(String::from_utf8_lossy(&output.stdout).trim()) + .join("bin") + .join("rustc") + }; + + let status = Command::new("cargo") + .current_dir(guest_directory) + .env("RUSTC", zisk_rustc) + .args(["build", "--release", "--target", ZISK_TARGET]) + .status() + .map_err(|e| ZiskError::CargoBuild { + cwd: guest_directory.to_path_buf(), + source: e, + })?; + + if !status.success() { + return Err(ZiskError::CargoBuildFailed { + status, + path: guest_directory.to_path_buf(), + }); + } + + // Get the workspace directory. + let program_workspace_path = { + let output = Command::new("cargo") + .current_dir(guest_directory) + .arg("locate-project") + .arg("--workspace") + .arg("--message-format=plain") + .output() + .map_err(ZiskError::CargoLocateProject)?; + PathBuf::from( + String::from_utf8_lossy(&output.stdout) + .trim() + .strip_suffix("Cargo.toml") + .expect("location to be path to Cargo.toml"), + ) + }; + + let elf_path = program_workspace_path + .join("target") + .join("riscv64ima-zisk-zkvm-elf") + .join("release") + .join(package_name); + let elf_bytes = fs::read(&elf_path).map_err(|e| ZiskError::ReadFile { + path: elf_path.clone(), + source: e, + })?; + + Ok(elf_bytes) + } +} + +#[cfg(test)] +mod tests { + use crate::compiler::RustRv64imaCustomized; + use test_utils::host::testing_guest_directory; + use zkvm_interface::Compiler; + + #[test] + fn test_compile() { + let guest_directory = testing_guest_directory("zisk", "basic"); + let elf_bytes = RustRv64imaCustomized.compile(&guest_directory).unwrap(); + assert!(!elf_bytes.is_empty(), "ELF bytes should not be empty."); + } +} diff --git a/crates/ere-zisk/src/error.rs b/crates/ere-zisk/src/error.rs index 936af32..de0b998 100644 --- a/crates/ere-zisk/src/error.rs +++ b/crates/ere-zisk/src/error.rs @@ -31,23 +31,6 @@ pub enum ZiskError { }, // Compilation - #[error("Program path does not exist or is not a directory: {0}")] - InvalidProgramPath(PathBuf), - #[error( - "Cargo.toml not found in program directory: {program_dir}. Expected at: {manifest_path}" - )] - CargoTomlMissing { - program_dir: PathBuf, - manifest_path: PathBuf, - }, - #[error("Could not find `[package].name` in guest Cargo.toml at {path}")] - MissingPackageName { path: PathBuf }, - #[error("Failed to parse guest Cargo.toml at {path}: {source}")] - ParseCargoToml { - path: PathBuf, - #[source] - source: toml::de::Error, - }, #[error("Failed to execute `RUSTUP_TOOLCHAIN=zisk rustc --print sysroot`")] RustcSysroot(#[source] io::Error), #[error("Failed to execute `cargo locate-project --workspace --message-format=plain`")] @@ -62,6 +45,8 @@ pub enum ZiskError { "`RUSTC=$ZISK_RUSTC cargo build --release ...` failed with status: {status} for program at {path}" )] CargoBuildFailed { status: ExitStatus, path: PathBuf }, + #[error(transparent)] + CompileUtilError(#[from] compile_utils::CompileError), // Serialization #[error("Bincode serialization/deserialization failed: {0}")] diff --git a/crates/ere-zisk/src/lib.rs b/crates/ere-zisk/src/lib.rs index 4989acf..fb3d113 100644 --- a/crates/ere-zisk/src/lib.rs +++ b/crates/ere-zisk/src/lib.rs @@ -2,39 +2,25 @@ use crate::{ client::{ZiskOptions, ZiskSdk, ZiskServer}, - compile::compile_zisk_program, + compiler::ZiskProgram, error::ZiskError, }; use serde::de::DeserializeOwned; use std::{ io::Read, - path::Path, sync::{Mutex, MutexGuard}, time::Instant, }; use zkvm_interface::{ - Compiler, Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, - ProverResourceType, PublicValues, zkVM, zkVMError, + Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType, + PublicValues, zkVM, zkVMError, }; include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); mod client; -mod compile; -mod error; - -#[allow(non_camel_case_types)] -pub struct RV64_IMA_ZISK_ZKVM_ELF; - -impl Compiler for RV64_IMA_ZISK_ZKVM_ELF { - type Error = ZiskError; - - type Program = Vec; - - fn compile(&self, guest_directory: &Path) -> Result { - compile_zisk_program(guest_directory) - } -} +pub mod compiler; +pub mod error; pub struct EreZisk { sdk: ZiskSdk, @@ -46,7 +32,7 @@ pub struct EreZisk { } impl EreZisk { - pub fn new(elf: Vec, resource: ProverResourceType) -> Result { + pub fn new(elf: ZiskProgram, resource: ProverResourceType) -> Result { if matches!(resource, ProverResourceType::Network(_)) { panic!("Network proving not yet implemented for ZisK. Use CPU or GPU resource type."); } @@ -151,11 +137,12 @@ fn serialize_inputs(inputs: &Input) -> Result, ZiskError> { #[cfg(test)] mod tests { - use super::*; + use crate::{EreZisk, compiler::RustRv64imaCustomized}; use std::sync::{Mutex, OnceLock}; use test_utils::host::{ BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, }; + use zkvm_interface::{Compiler, ProverResourceType, zkVM}; /// It fails if multiple servers created concurrently using the same port, /// so we have a lock to avoid that. @@ -166,7 +153,7 @@ mod tests { fn basic_program() -> Vec { BASIC_PROGRAM .get_or_init(|| { - RV64_IMA_ZISK_ZKVM_ELF + RustRv64imaCustomized .compile(&testing_guest_directory("zisk", "basic")) .unwrap() })