mirror of
https://github.com/eth-act/ere.git
synced 2026-02-19 11:54:42 -05:00
Reorganize compiler modules (#156)
This commit is contained in:
29
Cargo.lock
generated
29
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Vec<u8>, CompileError> {
|
||||
compile_program_stock_rust(guest_directory, toolchain)
|
||||
}
|
||||
|
||||
fn compile_program_stock_rust(
|
||||
guest_directory: &Path,
|
||||
toolchain: &String,
|
||||
) -> Result<Vec<u8>, 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."
|
||||
);
|
||||
}
|
||||
}
|
||||
7
crates/ere-jolt/src/compiler.rs
Normal file
7
crates/ere-jolt/src/compiler.rs
Normal file
@@ -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<u8>;
|
||||
80
crates/ere-jolt/src/compiler/rust_rv32ima.rs
Normal file
80
crates/ere-jolt/src/compiler/rust_rv32ima.rs
Normal file
@@ -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<Self::Program, Self::Error> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
68
crates/ere-jolt/src/compiler/rust_rv32ima_customized.rs
Normal file
68
crates/ere-jolt/src/compiler/rust_rv32ima_customized.rs
Normal file
@@ -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<Self::Program, Self::Error> {
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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<u8>;
|
||||
|
||||
fn compile(&self, guest_directory: &Path) -> Result<Self::Program, Self::Error> {
|
||||
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<Vec<u8>, 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<u8>,
|
||||
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<u8>, _resource: ProverResourceType) -> Result<Self, zkVMError> {
|
||||
pub fn new(elf: JoltProgram, _resource: ProverResourceType) -> Result<Self, zkVMError> {
|
||||
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<Vec<u8>> = OnceLock::new();
|
||||
|
||||
fn basic_program() -> Vec<u8> {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
31
crates/ere-miden/src/compiler.rs
Normal file
31
crates/ere-miden/src/compiler.rs
Normal file
@@ -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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_bytes(&self.0.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for MidenProgram {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let bytes = Vec::<u8>::deserialize(deserializer)?;
|
||||
miden_core::Program::read_from_bytes(&bytes)
|
||||
.map(Self)
|
||||
.map_err(D::Error::custom)
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<u8>,
|
||||
}
|
||||
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<Self, MidenError> {
|
||||
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<DefaultHost, MidenError> {
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
5
crates/ere-nexus/src/compiler.rs
Normal file
5
crates/ere-nexus/src/compiler.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod rust_rv32i;
|
||||
|
||||
pub use rust_rv32i::RustRv32i;
|
||||
|
||||
pub type NexusProgram = Vec<u8>;
|
||||
51
crates/ere-nexus/src/compiler/rust_rv32i.rs
Normal file
51
crates/ere-nexus/src/compiler/rust_rv32i.rs
Normal file
@@ -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<Self::Program, Self::Error> {
|
||||
// 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::<CargoPackager>::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.");
|
||||
}
|
||||
}
|
||||
@@ -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<dyn std::error::Error + Send + Sync + 'static>),
|
||||
/// 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)]
|
||||
|
||||
@@ -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<Self::Program, Self::Error> {
|
||||
// 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::<CargoPackager>::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: <NEXUS_TARGET as Compiler>::Program,
|
||||
program: NexusProgram,
|
||||
}
|
||||
|
||||
impl EreNexus {
|
||||
pub fn new(
|
||||
program: <NEXUS_TARGET as Compiler>::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<Local> = Stwo::new_from_file(&self.program.to_string_lossy().to_string())
|
||||
let prover: Stwo<Local> = 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<Local> = Stwo::new_from_file(&self.program.to_string_lossy().to_string())
|
||||
let prover: Stwo<Local> = 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<PathBuf> = 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."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
use std::fs;
|
||||
use toml::Table;
|
||||
|
||||
pub fn get_cargo_package_name(crate_path: &std::path::Path) -> Option<String> {
|
||||
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())
|
||||
}
|
||||
@@ -8,7 +8,6 @@ license.workspace = true
|
||||
[dependencies]
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror.workspace = true
|
||||
tracing.workspace = true
|
||||
toml.workspace = true
|
||||
|
||||
# OpenVM dependencies
|
||||
|
||||
@@ -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<OpenVMProgram, CompileError> {
|
||||
wrap_into_openvm_program(
|
||||
compile_program_stock_rust(guest_directory, toolchain)?,
|
||||
guest_directory,
|
||||
)
|
||||
}
|
||||
|
||||
fn compile_program_stock_rust(
|
||||
guest_directory: &Path,
|
||||
toolchain: &String,
|
||||
) -> Result<Vec<u8>, 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<u8>,
|
||||
guest_directory: &Path,
|
||||
) -> Result<OpenVMProgram, CompileError> {
|
||||
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."
|
||||
);
|
||||
}
|
||||
}
|
||||
56
crates/ere-openvm/src/compiler.rs
Normal file
56
crates/ere-openvm/src/compiler.rs
Normal file
@@ -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<u8>,
|
||||
pub app_config: AppConfig<SdkVmConfig>,
|
||||
}
|
||||
|
||||
impl OpenVMProgram {
|
||||
fn from_elf_and_app_config_path(
|
||||
elf: Vec<u8>,
|
||||
app_config_path: impl AsRef<Path>,
|
||||
) -> Result<Self, CompileError> {
|
||||
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 })
|
||||
}
|
||||
}
|
||||
81
crates/ere-openvm/src/compiler/rust_rv32ima.rs
Normal file
81
crates/ere-openvm/src/compiler/rust_rv32ima.rs
Normal file
@@ -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<Self::Program, Self::Error> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
54
crates/ere-openvm/src/compiler/rust_rv32ima_customized.rs
Normal file
54
crates/ere-openvm/src/compiler/rust_rv32ima_customized.rs
Normal file
@@ -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<Self::Program, Self::Error> {
|
||||
// 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.");
|
||||
}
|
||||
}
|
||||
@@ -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<u8>,
|
||||
app_config: AppConfig<SdkVmConfig>,
|
||||
}
|
||||
|
||||
impl Compiler for OPENVM_TARGET {
|
||||
type Error = OpenVMError;
|
||||
|
||||
type Program = OpenVMProgram;
|
||||
|
||||
fn compile(&self, guest_directory: &Path) -> Result<Self::Program, Self::Error> {
|
||||
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<OpenVMProgram, OpenVMError> {
|
||||
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<SdkVmConfig>,
|
||||
@@ -299,37 +217,34 @@ fn extract_public_values(user_public_values: &[F]) -> Result<Vec<u8>, 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<OpenVMProgram> = 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();
|
||||
|
||||
@@ -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<Vec<u8>, CompileError> {
|
||||
compile_program_stock_rust(guest_directory, toolchain)
|
||||
}
|
||||
|
||||
fn compile_program_stock_rust(
|
||||
guest_directory: &Path,
|
||||
toolchain: &String,
|
||||
) -> Result<Vec<u8>, 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."
|
||||
);
|
||||
}
|
||||
}
|
||||
7
crates/ere-pico/src/compiler.rs
Normal file
7
crates/ere-pico/src/compiler.rs
Normal file
@@ -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<u8>;
|
||||
75
crates/ere-pico/src/compiler/rust_rv32ima.rs
Normal file
75
crates/ere-pico/src/compiler/rust_rv32ima.rs
Normal file
@@ -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<Self::Program, Self::Error> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
65
crates/ere-pico/src/compiler/rust_rv32ima_customized.rs
Normal file
65
crates/ere-pico/src/compiler/rust_rv32ima_customized.rs
Normal file
@@ -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<u8>;
|
||||
|
||||
fn compile(&self, guest_directory: &Path) -> Result<Self::Program, Self::Error> {
|
||||
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.");
|
||||
}
|
||||
}
|
||||
@@ -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<u8>;
|
||||
|
||||
fn compile(&self, guest_directory: &Path) -> Result<Self::Program, Self::Error> {
|
||||
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<Vec<u8>, 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: <PICO_TARGET as Compiler>::Program,
|
||||
program: PicoProgram,
|
||||
}
|
||||
|
||||
impl ErePico {
|
||||
pub fn new(program: <PICO_TARGET as Compiler>::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<Vec<u8>> = OnceLock::new();
|
||||
static BASIC_PROGRAM: OnceLock<PicoProgram> = OnceLock::new();
|
||||
|
||||
fn basic_program() -> Vec<u8> {
|
||||
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();
|
||||
|
||||
@@ -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<u8>,
|
||||
pub(crate) image_id: Digest,
|
||||
}
|
||||
|
||||
pub fn compile_risc0_program(guest_directory: &Path) -> Result<Risc0Program, CompileError> {
|
||||
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.");
|
||||
}
|
||||
}
|
||||
@@ -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<Risc0Program, CompileError> {
|
||||
wrap_into_risc0_program(compile_program_stock_rust(guest_directory, toolchain)?)
|
||||
}
|
||||
|
||||
fn compile_program_stock_rust(
|
||||
guest_directory: &Path,
|
||||
toolchain: &String,
|
||||
) -> Result<Vec<u8>, 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<u8>) -> Result<Risc0Program, CompileError> {
|
||||
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."
|
||||
);
|
||||
}
|
||||
}
|
||||
14
crates/ere-risc0/src/compiler.rs
Normal file
14
crates/ere-risc0/src/compiler.rs
Normal file
@@ -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<u8>,
|
||||
pub(crate) image_id: Digest,
|
||||
}
|
||||
86
crates/ere-risc0/src/compiler/rust_rv32ima.rs
Normal file
86
crates/ere-risc0/src/compiler/rust_rv32ima.rs
Normal file
@@ -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<Self::Program, Self::Error> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
63
crates/ere-risc0/src/compiler/rust_rv32ima_customized.rs
Normal file
63
crates/ere-risc0/src/compiler/rust_rv32ima_customized.rs
Normal file
@@ -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<Self::Program, Self::Error> {
|
||||
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.");
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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<usize> = 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<Self::Program, Self::Error> {
|
||||
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<Risc0ProofWithPublicValues> for Receipt {
|
||||
}
|
||||
|
||||
pub struct EreRisc0 {
|
||||
program: <RV32_IM_RISC0_ZKVM_ELF as Compiler>::Program,
|
||||
program: Risc0Program,
|
||||
resource: ProverResourceType,
|
||||
segment_po2: usize,
|
||||
keccak_po2: usize,
|
||||
}
|
||||
|
||||
impl EreRisc0 {
|
||||
pub fn new(
|
||||
program: <RV32_IM_RISC0_ZKVM_ELF as Compiler>::Program,
|
||||
resource: ProverResourceType,
|
||||
) -> Result<Self, zkVMError> {
|
||||
pub fn new(program: Risc0Program, resource: ProverResourceType) -> Result<Self, zkVMError> {
|
||||
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<Risc0Program> = 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();
|
||||
|
||||
|
||||
@@ -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<Vec<u8>, 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<Vec<u8>, 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.");
|
||||
}
|
||||
}
|
||||
@@ -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<Vec<u8>, CompileError> {
|
||||
let elf = CargoBuildCmd::new()
|
||||
.toolchain(toolchain)
|
||||
.build_options(CARGO_BUILD_OPTIONS)
|
||||
.rustflags(RUSTFLAGS)
|
||||
.exec(guest_directory, TARGET_TRIPLE)?;
|
||||
|
||||
Ok(elf)
|
||||
}
|
||||
7
crates/ere-sp1/src/compiler.rs
Normal file
7
crates/ere-sp1/src/compiler.rs
Normal file
@@ -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<u8>;
|
||||
75
crates/ere-sp1/src/compiler/rust_rv32ima.rs
Normal file
75
crates/ere-sp1/src/compiler/rust_rv32ima.rs
Normal file
@@ -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<Self::Program, Self::Error> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
95
crates/ere-sp1/src/compiler/rust_rv32ima_customized.rs
Normal file
95
crates/ere-sp1/src/compiler/rust_rv32ima_customized.rs
Normal file
@@ -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<Self::Program, Self::Error> {
|
||||
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.");
|
||||
}
|
||||
}
|
||||
@@ -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: &<RV32_IM_SUCCINCT_ZKVM_ELF as Compiler>::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: &<RV32_IM_SUCCINCT_ZKVM_ELF as Compiler>::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: <RV32_IM_SUCCINCT_ZKVM_ELF as Compiler>::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<u8>;
|
||||
|
||||
fn compile(&self, guest_directory: &Path) -> Result<Self::Program, Self::Error> {
|
||||
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: <RV32_IM_SUCCINCT_ZKVM_ELF as Compiler>::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<Vec<u8>> = OnceLock::new();
|
||||
|
||||
fn basic_program() -> Vec<u8> {
|
||||
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();
|
||||
|
||||
@@ -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]
|
||||
|
||||
5
crates/ere-ziren/src/compiler.rs
Normal file
5
crates/ere-ziren/src/compiler.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod rust_mips32r2_customized;
|
||||
|
||||
pub use rust_mips32r2_customized::RustMips32r2Customized;
|
||||
|
||||
pub type ZirenProgram = Vec<u8>;
|
||||
99
crates/ere-ziren/src/compiler/rust_mips32r2_customized.rs
Normal file
99
crates/ere-ziren/src/compiler/rust_mips32r2_customized.rs
Normal file
@@ -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<Self::Program, Self::Error> {
|
||||
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.");
|
||||
}
|
||||
}
|
||||
@@ -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)]
|
||||
|
||||
@@ -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<u8>;
|
||||
|
||||
fn compile(&self, guest_directory: &Path) -> Result<Self::Program, Self::Error> {
|
||||
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: <MIPS32R2_ZKM_ZKVM_ELF as Compiler>::Program,
|
||||
program: ZirenProgram,
|
||||
pk: ZKMProvingKey,
|
||||
vk: ZKMVerifyingKey,
|
||||
}
|
||||
|
||||
impl EreZiren {
|
||||
pub fn new(
|
||||
program: <MIPS32R2_ZKM_ZKVM_ELF as Compiler>::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<Vec<u8>> = OnceLock::new();
|
||||
|
||||
fn basic_program() -> Vec<u8> {
|
||||
BASIC_PROGRAM
|
||||
.get_or_init(|| {
|
||||
MIPS32R2_ZKM_ZKVM_ELF
|
||||
RustMips32r2Customized
|
||||
.compile(&testing_guest_directory("ziren", "basic"))
|
||||
.unwrap()
|
||||
})
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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<Vec<u8>, 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::<TomlValue>()
|
||||
.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.");
|
||||
}
|
||||
}
|
||||
5
crates/ere-zisk/src/compiler.rs
Normal file
5
crates/ere-zisk/src/compiler.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod rust_rv64ima_customized;
|
||||
|
||||
pub use rust_rv64ima_customized::RustRv64imaCustomized;
|
||||
|
||||
pub type ZiskProgram = Vec<u8>;
|
||||
106
crates/ere-zisk/src/compiler/rust_rv64ima_customized.rs
Normal file
106
crates/ere-zisk/src/compiler/rust_rv64ima_customized.rs
Normal file
@@ -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<u8>;
|
||||
|
||||
fn compile(&self, guest_directory: &Path) -> Result<Self::Program, Self::Error> {
|
||||
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.");
|
||||
}
|
||||
}
|
||||
@@ -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}")]
|
||||
|
||||
@@ -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<u8>;
|
||||
|
||||
fn compile(&self, guest_directory: &Path) -> Result<Self::Program, Self::Error> {
|
||||
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<u8>, resource: ProverResourceType) -> Result<Self, zkVMError> {
|
||||
pub fn new(elf: ZiskProgram, resource: ProverResourceType) -> Result<Self, zkVMError> {
|
||||
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<Vec<u8>, 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<u8> {
|
||||
BASIC_PROGRAM
|
||||
.get_or_init(|| {
|
||||
RV64_IMA_ZISK_ZKVM_ELF
|
||||
RustRv64imaCustomized
|
||||
.compile(&testing_guest_directory("zisk", "basic"))
|
||||
.unwrap()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user