mirror of
https://github.com/eth-act/ere.git
synced 2026-02-19 11:54:42 -05:00
Refactor zkVM error handling (#179)
This commit is contained in:
15
Cargo.lock
generated
15
Cargo.lock
generated
@@ -3731,6 +3731,7 @@ dependencies = [
|
||||
name = "ere-airbender"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode 2.0.1",
|
||||
"ere-build-utils",
|
||||
"ere-compile-utils",
|
||||
@@ -3755,6 +3756,7 @@ dependencies = [
|
||||
name = "ere-compile-utils"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_metadata 0.19.2",
|
||||
"tempfile",
|
||||
"thiserror 2.0.12",
|
||||
@@ -3786,6 +3788,7 @@ dependencies = [
|
||||
name = "ere-dockerized"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ere-build-utils",
|
||||
"ere-server",
|
||||
"ere-test-utils",
|
||||
@@ -3809,6 +3812,7 @@ dependencies = [
|
||||
name = "ere-jolt"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ark-serialize 0.5.0",
|
||||
"common",
|
||||
"ere-build-utils",
|
||||
@@ -3824,7 +3828,7 @@ dependencies = [
|
||||
name = "ere-miden"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"bincode 2.0.1",
|
||||
"anyhow",
|
||||
"ere-build-utils",
|
||||
"ere-test-utils",
|
||||
"ere-zkvm-interface",
|
||||
@@ -3842,6 +3846,7 @@ dependencies = [
|
||||
name = "ere-nexus"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode 2.0.1",
|
||||
"ere-build-utils",
|
||||
"ere-compile-utils",
|
||||
@@ -3860,10 +3865,12 @@ dependencies = [
|
||||
name = "ere-openvm"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ere-build-utils",
|
||||
"ere-compile-utils",
|
||||
"ere-test-utils",
|
||||
"ere-zkvm-interface",
|
||||
"eyre",
|
||||
"openvm-build",
|
||||
"openvm-circuit",
|
||||
"openvm-continuations",
|
||||
@@ -3906,6 +3913,7 @@ dependencies = [
|
||||
"ere-zkvm-interface",
|
||||
"risc0-binfmt",
|
||||
"risc0-build",
|
||||
"risc0-zkp",
|
||||
"risc0-zkvm",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
@@ -3946,6 +3954,7 @@ dependencies = [
|
||||
name = "ere-sp1"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode 2.0.1",
|
||||
"ere-build-utils",
|
||||
"ere-compile-utils",
|
||||
@@ -3972,6 +3981,7 @@ dependencies = [
|
||||
name = "ere-ziren"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode 2.0.1",
|
||||
"ere-build-utils",
|
||||
"ere-compile-utils",
|
||||
@@ -3986,7 +3996,7 @@ dependencies = [
|
||||
name = "ere-zisk"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"bincode 2.0.1",
|
||||
"anyhow",
|
||||
"blake3",
|
||||
"bytemuck",
|
||||
"ere-build-utils",
|
||||
@@ -4003,6 +4013,7 @@ dependencies = [
|
||||
name = "ere-zkvm-interface"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auto_impl",
|
||||
"bincode 2.0.1",
|
||||
"clap",
|
||||
|
||||
@@ -43,6 +43,7 @@ bytemuck = "1.23.1"
|
||||
cargo_metadata = "0.19.0"
|
||||
clap = "4.5.42"
|
||||
dashmap = "6.1.0"
|
||||
eyre = "0.6.12"
|
||||
indexmap = "2.10.0"
|
||||
postcard = "1.0.8"
|
||||
prost = "0.13"
|
||||
@@ -98,6 +99,7 @@ pico-vm = { git = "https://github.com/brevis-network/pico.git", tag = "v1.1.7" }
|
||||
|
||||
# Risc0 dependencies
|
||||
risc0-build = "3.0.3"
|
||||
risc0-zkp = { version = "3.0.2", default-features = false }
|
||||
risc0-zkvm = { version = "3.0.3", default-features = false }
|
||||
risc0-binfmt = { version = "3.0.2", default-features = false }
|
||||
|
||||
@@ -130,6 +132,9 @@ ere-build-utils = { path = "crates/build-utils" }
|
||||
ere-compile-utils = { path = "crates/compile-utils" }
|
||||
ere-test-utils = { path = "crates/test-utils" }
|
||||
|
||||
[profile.dev.package.openvm-stark-backend]
|
||||
opt-level = 3
|
||||
|
||||
[patch.crates-io]
|
||||
# These patches are only needed by Jolt
|
||||
ark-ff = { git = "https://github.com/a16z/arkworks-algebra", branch = "v0.5.0-optimize-mul-u64" }
|
||||
|
||||
@@ -92,7 +92,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let program = compiler.compile(guest_directory)?;
|
||||
|
||||
// Create zkVM instance
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu)?;
|
||||
|
||||
// Serialize input
|
||||
let input = 42u32.to_le_bytes();
|
||||
|
||||
@@ -6,6 +6,7 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
cargo_metadata.workspace = true
|
||||
tempfile.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
@@ -1,23 +1,122 @@
|
||||
use std::{io, path::PathBuf, process::ExitStatus};
|
||||
use std::{
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, ExitStatus, Output},
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
pub enum CommonError {
|
||||
#[error("{ctx}: {err}")]
|
||||
Io {
|
||||
ctx: String,
|
||||
#[source]
|
||||
err: io::Error,
|
||||
},
|
||||
|
||||
#[error("Deserialize {id} with `{lib}` failed: {err}")]
|
||||
Deserialize {
|
||||
id: String,
|
||||
lib: String,
|
||||
#[source]
|
||||
err: anyhow::Error,
|
||||
},
|
||||
|
||||
#[error("Failed to run command `{cmd}`: {err}")]
|
||||
Command {
|
||||
cmd: String,
|
||||
#[source]
|
||||
err: io::Error,
|
||||
},
|
||||
|
||||
#[error("Command `{cmd}` exit with {status}{stdout}{stderr}",
|
||||
stdout = if stdout.is_empty() { String::new() } else { format!("\nstdout: {}", String::from_utf8_lossy(stdout)) },
|
||||
stderr = if stderr.is_empty() { String::new() } else { format!("\nstdout: {}", String::from_utf8_lossy(stderr)) })]
|
||||
CommandExitNonZero {
|
||||
cmd: String,
|
||||
status: ExitStatus,
|
||||
stdout: Vec<u8>,
|
||||
stderr: Vec<u8>,
|
||||
},
|
||||
|
||||
#[error("`cargo metadata` in {manifest_dir} failed: {err}")]
|
||||
CargoMetadata {
|
||||
manifest_dir: PathBuf,
|
||||
#[source]
|
||||
err: cargo_metadata::Error,
|
||||
},
|
||||
#[error("Root package not found in {0}")]
|
||||
RootPackageNotFound(PathBuf),
|
||||
#[error("Failed to create temporary directory: {0}")]
|
||||
Tempdir(io::Error),
|
||||
#[error("Failed to create linker script: {0}")]
|
||||
CreateLinkerScript(io::Error),
|
||||
#[error("Failed to run `cargo build`: {0}")]
|
||||
CargoBuild(io::Error),
|
||||
#[error("`cargo build` failed: {0}")]
|
||||
CargoBuildFailed(ExitStatus),
|
||||
#[error("Failed to read built ELF: {0}")]
|
||||
ReadElf(io::Error),
|
||||
|
||||
#[error("Root package not found in {manifest_dir}")]
|
||||
CargoRootPackageNotFound { manifest_dir: PathBuf },
|
||||
}
|
||||
|
||||
impl CommonError {
|
||||
pub fn io(ctx: impl AsRef<str>, err: io::Error) -> Self {
|
||||
let ctx = ctx.as_ref().to_string();
|
||||
Self::Io { ctx, err }
|
||||
}
|
||||
|
||||
pub fn tempdir(err: io::Error) -> Self {
|
||||
Self::io("Failed to create temporary dir", err)
|
||||
}
|
||||
|
||||
pub fn create_dir(id: impl AsRef<str>, path: impl AsRef<Path>, err: io::Error) -> Self {
|
||||
let (id, path) = (id.as_ref(), path.as_ref().display());
|
||||
Self::io(format!("Failed to create dir {id} at {path}"), err)
|
||||
}
|
||||
|
||||
pub fn read_file(id: impl AsRef<str>, path: impl AsRef<Path>, err: io::Error) -> Self {
|
||||
let (id, path) = (id.as_ref(), path.as_ref().display());
|
||||
Self::io(format!("Failed to write {id} to {path}"), err)
|
||||
}
|
||||
|
||||
pub fn write_file(id: impl AsRef<str>, path: impl AsRef<Path>, err: io::Error) -> Self {
|
||||
let (id, path) = (id.as_ref(), path.as_ref().display());
|
||||
Self::io(format!("Failed to read {id} from {path}"), err)
|
||||
}
|
||||
|
||||
pub fn deserialize(
|
||||
id: impl AsRef<str>,
|
||||
lib: impl AsRef<str>,
|
||||
err: impl Into<anyhow::Error>,
|
||||
) -> Self {
|
||||
let id = id.as_ref().to_string();
|
||||
let lib = lib.as_ref().to_string();
|
||||
let err = err.into();
|
||||
Self::Deserialize { id, lib, err }
|
||||
}
|
||||
|
||||
pub fn command(cmd: &Command, err: io::Error) -> Self {
|
||||
Self::Command {
|
||||
cmd: format!("{cmd:?}"),
|
||||
err,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn command_exit_non_zero(
|
||||
cmd: &Command,
|
||||
status: ExitStatus,
|
||||
output: Option<&Output>,
|
||||
) -> Self {
|
||||
Self::CommandExitNonZero {
|
||||
cmd: format!("{cmd:?}"),
|
||||
status,
|
||||
stdout: output
|
||||
.map(|output| &output.stdout)
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
stderr: output
|
||||
.map(|output| &output.stderr)
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cargo_metadata(manifest_dir: PathBuf, err: cargo_metadata::Error) -> Self {
|
||||
Self::CargoMetadata { manifest_dir, err }
|
||||
}
|
||||
|
||||
pub fn cargo_root_package_not_found(manifest_dir: PathBuf) -> Self {
|
||||
Self::CargoRootPackageNotFound { manifest_dir }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ mod error;
|
||||
mod rust;
|
||||
|
||||
pub use {
|
||||
error::CompileError,
|
||||
rust::{CargoBuildCmd, cargo_metadata},
|
||||
error::CommonError,
|
||||
rust::{CargoBuildCmd, cargo_metadata, rustc_path},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use crate::CompileError;
|
||||
use crate::CommonError;
|
||||
use cargo_metadata::{Metadata, MetadataCommand};
|
||||
use std::{fs, iter, path::Path, process::Command};
|
||||
use std::{
|
||||
fs, iter,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
use tempfile::tempdir;
|
||||
|
||||
const CARGO_ENCODED_RUSTFLAGS_SEPARATOR: &str = "\x1f";
|
||||
@@ -74,20 +78,20 @@ impl CargoBuildCmd {
|
||||
&self,
|
||||
manifest_dir: impl AsRef<Path>,
|
||||
target: impl AsRef<str>,
|
||||
) -> Result<Vec<u8>, CompileError> {
|
||||
) -> Result<Vec<u8>, CommonError> {
|
||||
let metadata = cargo_metadata(manifest_dir.as_ref())?;
|
||||
|
||||
let package = metadata.root_package().unwrap();
|
||||
|
||||
let tempdir = tempdir().map_err(CompileError::Tempdir)?;
|
||||
let tempdir = tempdir().map_err(CommonError::tempdir)?;
|
||||
let linker_script_path = tempdir
|
||||
.path()
|
||||
.join("linker_script")
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
if let Some(linker_script) = &self.linker_script {
|
||||
fs::write(&linker_script_path, linker_script.as_bytes())
|
||||
.map_err(CompileError::CreateLinkerScript)?;
|
||||
fs::write(&linker_script_path, linker_script.as_bytes()).map_err(|err| {
|
||||
CommonError::write_file("linker_script", &linker_script_path, err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let encoded_rustflags = iter::empty()
|
||||
@@ -110,14 +114,15 @@ impl CargoBuildCmd {
|
||||
.chain(["--target".into(), target.as_ref().into()])
|
||||
.chain(["--manifest-path".into(), package.manifest_path.to_string()]);
|
||||
|
||||
let status = Command::new("cargo")
|
||||
let mut cmd = Command::new("cargo");
|
||||
let status = cmd
|
||||
.env("CARGO_ENCODED_RUSTFLAGS", encoded_rustflags)
|
||||
.args(args)
|
||||
.status()
|
||||
.map_err(CompileError::CargoBuild)?;
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(CompileError::CargoBuildFailed(status));
|
||||
return Err(CommonError::command_exit_non_zero(&cmd, status, None));
|
||||
}
|
||||
|
||||
let elf_path = metadata
|
||||
@@ -125,28 +130,49 @@ impl CargoBuildCmd {
|
||||
.join(target.as_ref())
|
||||
.join(&self.profile)
|
||||
.join(&package.name);
|
||||
let elf = fs::read(elf_path).map_err(CompileError::ReadElf)?;
|
||||
let elf =
|
||||
fs::read(&elf_path).map_err(|err| CommonError::read_file("elf", &elf_path, err))?;
|
||||
|
||||
Ok(elf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Metadata` of `manifest_dir` and guarantees the `root_package` can be resolved.
|
||||
pub fn cargo_metadata(manifest_dir: impl AsRef<Path>) -> Result<Metadata, CompileError> {
|
||||
let manifest_path = manifest_dir.as_ref().join("Cargo.toml");
|
||||
let metadata = MetadataCommand::new()
|
||||
.manifest_path(&manifest_path)
|
||||
.exec()
|
||||
.map_err(|err| CompileError::CargoMetadata {
|
||||
err,
|
||||
manifest_dir: manifest_dir.as_ref().to_path_buf(),
|
||||
})?;
|
||||
pub fn cargo_metadata(manifest_dir: impl AsRef<Path>) -> Result<Metadata, CommonError> {
|
||||
let manifest_dir = manifest_dir.as_ref().to_path_buf();
|
||||
let manifest_path = manifest_dir.join("Cargo.toml");
|
||||
let metadata = match MetadataCommand::new().manifest_path(&manifest_path).exec() {
|
||||
Ok(metadata) => metadata,
|
||||
Err(err) => return Err(CommonError::CargoMetadata { err, manifest_dir }),
|
||||
};
|
||||
|
||||
if metadata.root_package().is_none() {
|
||||
return Err(CompileError::RootPackageNotFound(
|
||||
manifest_dir.as_ref().to_path_buf(),
|
||||
));
|
||||
return Err(CommonError::CargoRootPackageNotFound { manifest_dir });
|
||||
}
|
||||
|
||||
Ok(metadata)
|
||||
}
|
||||
|
||||
/// Returns the path to `rustc` executable of the given toolchain.
|
||||
pub fn rustc_path(toolchain: &str) -> Result<PathBuf, CommonError> {
|
||||
let mut cmd = Command::new("rustc");
|
||||
let output = cmd
|
||||
.env("RUSTUP_TOOLCHAIN", toolchain)
|
||||
.args(["--print", "sysroot"])
|
||||
.output()
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(
|
||||
PathBuf::from(String::from_utf8_lossy(&output.stdout).trim())
|
||||
.join("bin")
|
||||
.join("rustc"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
tempfile.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
use ere_server::client::{TwirpErrorResponse, zkVMClientError};
|
||||
use ere_zkvm_interface::zkVMError;
|
||||
use std::{io, path::PathBuf};
|
||||
use thiserror::Error;
|
||||
|
||||
impl From<DockerizedError> for zkVMError {
|
||||
fn from(value: DockerizedError) -> Self {
|
||||
zkVMError::Other(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<zkVMClientError> for DockerizedError {
|
||||
fn from(value: zkVMClientError) -> Self {
|
||||
match value {
|
||||
|
||||
@@ -70,7 +70,7 @@ use crate::{
|
||||
use ere_server::client::{Url, zkVMClient};
|
||||
use ere_zkvm_interface::{
|
||||
Compiler, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
PublicValues, zkVM,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
@@ -463,13 +463,13 @@ impl EreDockerizedzkVM {
|
||||
zkvm: ErezkVM,
|
||||
program: SerializedProgram,
|
||||
resource: ProverResourceType,
|
||||
) -> Result<Self, zkVMError> {
|
||||
) -> Result<Self, DockerizedError> {
|
||||
zkvm.build_docker_image(matches!(resource, ProverResourceType::Gpu))?;
|
||||
|
||||
let server_container = zkvm.spawn_server(&program, &resource)?;
|
||||
|
||||
let url = Url::parse(&format!("http://127.0.0.1:{}", zkvm.server_port())).unwrap();
|
||||
let client = block_on(zkVMClient::new(url)).map_err(zkVMError::other)?;
|
||||
let client = block_on(zkVMClient::new(url))?;
|
||||
|
||||
Ok(Self {
|
||||
zkvm,
|
||||
@@ -494,7 +494,7 @@ impl EreDockerizedzkVM {
|
||||
}
|
||||
|
||||
impl zkVM for EreDockerizedzkVM {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, input: &[u8]) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
|
||||
let (public_values, report) =
|
||||
block_on(self.client.execute(input.to_vec())).map_err(DockerizedError::from)?;
|
||||
|
||||
@@ -505,7 +505,7 @@ impl zkVM for EreDockerizedzkVM {
|
||||
&self,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
|
||||
let (public_values, proof, report) =
|
||||
block_on(self.client.prove(input.to_vec(), proof_kind))
|
||||
.map_err(DockerizedError::from)?;
|
||||
@@ -513,7 +513,7 @@ impl zkVM for EreDockerizedzkVM {
|
||||
Ok((public_values, proof, report))
|
||||
}
|
||||
|
||||
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
|
||||
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
|
||||
let public_values = block_on(self.client.verify(proof)).map_err(DockerizedError::from)?;
|
||||
|
||||
Ok(public_values)
|
||||
@@ -554,7 +554,7 @@ mod test {
|
||||
workspace_dir,
|
||||
};
|
||||
use ere_test_utils::{host::*, program::basic::BasicProgramInput};
|
||||
use ere_zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM, zkVMError};
|
||||
use ere_zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
|
||||
use std::sync::{Mutex, MutexGuard, OnceLock};
|
||||
|
||||
macro_rules! test_compile {
|
||||
@@ -603,16 +603,11 @@ mod test {
|
||||
|
||||
// Invalid test cases
|
||||
for input in $invalid_test_cases {
|
||||
let Err(zkVMError::Other(err)) = zkvm.execute(&input) else {
|
||||
unreachable!();
|
||||
};
|
||||
assert!(
|
||||
matches!(
|
||||
err.downcast_ref::<DockerizedError>().unwrap(),
|
||||
DockerizedError::zkVM(_)
|
||||
),
|
||||
"Unexpected err: {err:?}"
|
||||
);
|
||||
let err = zkvm.execute(&input).unwrap_err();
|
||||
assert!(matches!(
|
||||
err.downcast::<DockerizedError>().unwrap(),
|
||||
DockerizedError::zkVM(_)
|
||||
),);
|
||||
}
|
||||
|
||||
drop(zkvm);
|
||||
@@ -631,17 +626,11 @@ mod test {
|
||||
|
||||
// Invalid test cases
|
||||
for input in $invalid_test_cases {
|
||||
let Err(zkVMError::Other(err)) = zkvm.prove(&input, ProofKind::default())
|
||||
else {
|
||||
unreachable!();
|
||||
};
|
||||
assert!(
|
||||
matches!(
|
||||
err.downcast_ref::<DockerizedError>().unwrap(),
|
||||
DockerizedError::zkVM(_)
|
||||
),
|
||||
"Unexpected err: {err:?}"
|
||||
);
|
||||
let err = zkvm.prove(&input, ProofKind::default()).unwrap_err();
|
||||
assert!(matches!(
|
||||
err.downcast::<DockerizedError>().unwrap(),
|
||||
DockerizedError::zkVM(_)
|
||||
),);
|
||||
}
|
||||
|
||||
drop(zkvm);
|
||||
|
||||
@@ -113,7 +113,7 @@ fn construct_zkvm(program: Vec<u8>, resource: ProverResourceType) -> Result<impl
|
||||
.with_context(|| "Failed to deserialize program")?;
|
||||
|
||||
#[cfg(feature = "airbender")]
|
||||
let zkvm = Ok::<_, Error>(ere_airbender::EreAirbender::new(program, resource));
|
||||
let zkvm = ere_airbender::EreAirbender::new(program, resource);
|
||||
|
||||
#[cfg(feature = "jolt")]
|
||||
let zkvm = ere_jolt::EreJolt::new(program, resource);
|
||||
@@ -122,22 +122,22 @@ fn construct_zkvm(program: Vec<u8>, resource: ProverResourceType) -> Result<impl
|
||||
let zkvm = ere_miden::EreMiden::new(program, resource);
|
||||
|
||||
#[cfg(feature = "nexus")]
|
||||
let zkvm = Ok::<_, Error>(ere_nexus::EreNexus::new(program, resource));
|
||||
let zkvm = ere_nexus::EreNexus::new(program, resource);
|
||||
|
||||
#[cfg(feature = "openvm")]
|
||||
let zkvm = ere_openvm::EreOpenVM::new(program, resource);
|
||||
|
||||
#[cfg(feature = "pico")]
|
||||
let zkvm = Ok::<_, Error>(ere_pico::ErePico::new(program, resource));
|
||||
let zkvm = ere_pico::ErePico::new(program, resource);
|
||||
|
||||
#[cfg(feature = "risc0")]
|
||||
let zkvm = ere_risc0::EreRisc0::new(program, resource);
|
||||
|
||||
#[cfg(feature = "sp1")]
|
||||
let zkvm = Ok::<_, Error>(ere_sp1::EreSP1::new(program, resource));
|
||||
let zkvm = ere_sp1::EreSP1::new(program, resource);
|
||||
|
||||
#[cfg(feature = "ziren")]
|
||||
let zkvm = Ok::<_, Error>(ere_ziren::EreZiren::new(program, resource));
|
||||
let zkvm = ere_ziren::EreZiren::new(program, resource);
|
||||
|
||||
#[cfg(feature = "zisk")]
|
||||
let zkvm = ere_zisk::EreZisk::new(program, resource);
|
||||
|
||||
@@ -12,8 +12,7 @@ sha2.workspace = true
|
||||
|
||||
# Local dependencies
|
||||
ere-zkvm-interface = { workspace = true, optional = true }
|
||||
ere-io-serde = { workspace = true }
|
||||
|
||||
ere-io-serde.workspace = true
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
auto_impl.workspace = true
|
||||
indexmap = { workspace = true, features = ["serde"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
148
crates/zkvm-interface/src/error.rs
Normal file
148
crates/zkvm-interface/src/error.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
use crate::ProofKind;
|
||||
use std::{
|
||||
io,
|
||||
path::Path,
|
||||
process::{Command, ExitStatus, Output},
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CommonError {
|
||||
#[error("{ctx}: {err}")]
|
||||
Io {
|
||||
ctx: String,
|
||||
#[source]
|
||||
err: io::Error,
|
||||
},
|
||||
|
||||
#[error("Serialize {id} with `{lib}` failed: {err}")]
|
||||
Serialize {
|
||||
id: String,
|
||||
lib: String,
|
||||
#[source]
|
||||
err: anyhow::Error,
|
||||
},
|
||||
|
||||
#[error("Deserialize {id} with `{lib}` failed: {err}")]
|
||||
Deserialize {
|
||||
id: String,
|
||||
lib: String,
|
||||
#[source]
|
||||
err: anyhow::Error,
|
||||
},
|
||||
|
||||
#[error("Failed to run command `{cmd}`: {err}")]
|
||||
Command {
|
||||
cmd: String,
|
||||
#[source]
|
||||
err: io::Error,
|
||||
},
|
||||
|
||||
#[error("Command `{cmd}` exit with {status}{stdout}{stderr}",
|
||||
stdout = if stdout.is_empty() { String::new() } else { format!("\nstdout: {}", String::from_utf8_lossy(stdout)) },
|
||||
stderr = if stderr.is_empty() { String::new() } else { format!("\nstdout: {}", String::from_utf8_lossy(stderr)) })]
|
||||
CommandExitNonZero {
|
||||
cmd: String,
|
||||
status: ExitStatus,
|
||||
stdout: Vec<u8>,
|
||||
stderr: Vec<u8>,
|
||||
},
|
||||
|
||||
#[error("Unsupported proof kind {unsupported:?}, expect one of {supported:?}")]
|
||||
UnsupportedProofKind {
|
||||
unsupported: ProofKind,
|
||||
supported: Vec<ProofKind>,
|
||||
},
|
||||
}
|
||||
|
||||
impl CommonError {
|
||||
pub fn io(ctx: impl AsRef<str>, err: io::Error) -> Self {
|
||||
let ctx = ctx.as_ref().to_string();
|
||||
Self::Io { ctx, err }
|
||||
}
|
||||
|
||||
pub fn tempdir(err: io::Error) -> Self {
|
||||
Self::io("Failed to create temporary dir", err)
|
||||
}
|
||||
|
||||
pub fn file_not_found(id: impl AsRef<str>, path: impl AsRef<Path>) -> Self {
|
||||
let (id, path) = (id.as_ref(), path.as_ref().display());
|
||||
Self::io(
|
||||
format!("Failed to find {id} at {path}"),
|
||||
io::ErrorKind::NotFound.into(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_dir(id: impl AsRef<str>, path: impl AsRef<Path>, err: io::Error) -> Self {
|
||||
let (id, path) = (id.as_ref(), path.as_ref().display());
|
||||
Self::io(format!("Failed to create dir {id} at {path}"), err)
|
||||
}
|
||||
|
||||
pub fn read_file(id: impl AsRef<str>, path: impl AsRef<Path>, err: io::Error) -> Self {
|
||||
let (id, path) = (id.as_ref(), path.as_ref().display());
|
||||
Self::io(format!("Failed to write {id} to {path}"), err)
|
||||
}
|
||||
|
||||
pub fn write_file(id: impl AsRef<str>, path: impl AsRef<Path>, err: io::Error) -> Self {
|
||||
let (id, path) = (id.as_ref(), path.as_ref().display());
|
||||
Self::io(format!("Failed to read {id} from {path}"), err)
|
||||
}
|
||||
|
||||
pub fn serialize(
|
||||
id: impl AsRef<str>,
|
||||
lib: impl AsRef<str>,
|
||||
err: impl Into<anyhow::Error>,
|
||||
) -> Self {
|
||||
let id = id.as_ref().to_string();
|
||||
let lib = lib.as_ref().to_string();
|
||||
let err = err.into();
|
||||
Self::Serialize { id, lib, err }
|
||||
}
|
||||
|
||||
pub fn deserialize(
|
||||
id: impl AsRef<str>,
|
||||
lib: impl AsRef<str>,
|
||||
err: impl Into<anyhow::Error>,
|
||||
) -> Self {
|
||||
let id = id.as_ref().to_string();
|
||||
let lib = lib.as_ref().to_string();
|
||||
let err = err.into();
|
||||
Self::Deserialize { id, lib, err }
|
||||
}
|
||||
|
||||
pub fn command(cmd: &Command, err: io::Error) -> Self {
|
||||
Self::Command {
|
||||
cmd: format!("{cmd:?}"),
|
||||
err,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn command_exit_non_zero(
|
||||
cmd: &Command,
|
||||
status: ExitStatus,
|
||||
output: Option<&Output>,
|
||||
) -> Self {
|
||||
Self::CommandExitNonZero {
|
||||
cmd: format!("{cmd:?}"),
|
||||
status,
|
||||
stdout: output
|
||||
.map(|output| &output.stdout)
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
stderr: output
|
||||
.map(|output| &output.stderr)
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unsupported_proof_kind(
|
||||
unsupported: ProofKind,
|
||||
supported: impl IntoIterator<Item = ProofKind>,
|
||||
) -> Self {
|
||||
Self::UnsupportedProofKind {
|
||||
unsupported,
|
||||
supported: supported.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,9 @@
|
||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||
use std::path::Path;
|
||||
use strum::{EnumDiscriminants, EnumIs, EnumTryAs, FromRepr};
|
||||
use thiserror::Error;
|
||||
|
||||
mod error;
|
||||
pub use error::CommonError;
|
||||
|
||||
mod reports;
|
||||
pub use reports::{ProgramExecutionReport, ProgramProvingReport};
|
||||
@@ -48,46 +50,6 @@ impl ProverResourceType {
|
||||
}
|
||||
}
|
||||
|
||||
/// An error that can occur during prove, execute or verification
|
||||
/// of a zkVM.
|
||||
///
|
||||
/// Note: We use a concrete error type here, so that downstream crates
|
||||
/// can do patterns such as Vec<dyn zkVM>
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum zkVMError {
|
||||
/// Network-related errors
|
||||
#[error("Network error: {0}")]
|
||||
Network(String),
|
||||
|
||||
/// Authentication error
|
||||
#[error("Authentication failed: {0}")]
|
||||
Authentication(String),
|
||||
|
||||
/// Timeout error
|
||||
#[error("Operation timed out after {0:?}")]
|
||||
Timeout(std::time::Duration),
|
||||
|
||||
/// Service unavailable
|
||||
#[error("Prover service unavailable: {0}")]
|
||||
ServiceUnavailable(String),
|
||||
|
||||
/// Invalid response from network
|
||||
#[error("Invalid response from prover network: {0}")]
|
||||
InvalidResponse(String),
|
||||
|
||||
// TODO: We can add more variants as time goes by.
|
||||
// TODO: for now, we use this catch-all as a way to prototype faster
|
||||
#[error(transparent)]
|
||||
Other(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
impl zkVMError {
|
||||
pub fn other(error: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
|
||||
Self::Other(error.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Public values committed/revealed by guest program.
|
||||
///
|
||||
/// Use [`zkVM::deserialize_from`] to deserialize object from the bytes.
|
||||
@@ -142,19 +104,19 @@ impl Proof {
|
||||
/// implementation will have their own construction function.
|
||||
pub trait zkVM {
|
||||
/// Executes the program with the given input.
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError>;
|
||||
fn execute(&self, input: &[u8]) -> anyhow::Result<(PublicValues, ProgramExecutionReport)>;
|
||||
|
||||
/// Creates a proof of the program execution with given input.
|
||||
fn prove(
|
||||
&self,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError>;
|
||||
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)>;
|
||||
|
||||
/// Verifies a proof of the program used to create this zkVM instance, then
|
||||
/// returns the public values extracted from the proof.
|
||||
#[must_use = "Public values must be used"]
|
||||
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError>;
|
||||
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues>;
|
||||
|
||||
/// Returns the name of the zkVM
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
@@ -6,6 +6,7 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
bincode = { workspace = true, features = ["alloc", "serde"] }
|
||||
serde_json.workspace = true
|
||||
tempfile.workspace = true
|
||||
|
||||
@@ -3,7 +3,7 @@ use airbender_execution_utils::{
|
||||
Machine, ProgramProof, compute_chain_encoding, generate_params_for_binary,
|
||||
universal_circuit_verifier_vk, verify_recursion_log_23_layer,
|
||||
};
|
||||
use ere_zkvm_interface::PublicValues;
|
||||
use ere_zkvm_interface::{CommonError, PublicValues};
|
||||
use std::{array, fs, io::BufRead, iter, process::Command};
|
||||
use tempfile::tempdir;
|
||||
|
||||
@@ -43,17 +43,18 @@ impl AirbenderSdk {
|
||||
}
|
||||
|
||||
pub fn execute(&self, input: &[u8]) -> Result<(PublicValues, u64), AirbenderError> {
|
||||
let tempdir = tempdir().map_err(AirbenderError::TempDir)?;
|
||||
let tempdir = tempdir().map_err(CommonError::tempdir)?;
|
||||
|
||||
let bin_path = tempdir.path().join("guest.bin");
|
||||
fs::write(&bin_path, &self.bin)
|
||||
.map_err(|err| AirbenderError::write_file(err, "guest.bin", &bin_path))?;
|
||||
.map_err(|err| CommonError::write_file("guest.bin", &bin_path, err))?;
|
||||
|
||||
let input_path = tempdir.path().join("input.hex");
|
||||
fs::write(&input_path, encode_input(input))
|
||||
.map_err(|err| AirbenderError::write_file(err, "input.hex", &input_path))?;
|
||||
.map_err(|err| CommonError::write_file("input.hex", &input_path, err))?;
|
||||
|
||||
let output = Command::new("airbender-cli")
|
||||
let mut cmd = Command::new("airbender-cli");
|
||||
let output = cmd
|
||||
.arg("run")
|
||||
.arg("--bin")
|
||||
.arg(&bin_path)
|
||||
@@ -61,13 +62,14 @@ impl AirbenderSdk {
|
||||
.arg(&input_path)
|
||||
.args(["--cycles", &u64::MAX.to_string()])
|
||||
.output()
|
||||
.map_err(AirbenderError::AirbenderRun)?;
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(AirbenderError::AirbenderRunFailed {
|
||||
status: output.status,
|
||||
stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
|
||||
});
|
||||
Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
))?
|
||||
}
|
||||
|
||||
// Parse public values 8 u32 words (32 bytes) from stdout in format of:
|
||||
@@ -110,22 +112,23 @@ impl AirbenderSdk {
|
||||
}
|
||||
|
||||
pub fn prove(&self, input: &[u8]) -> Result<(PublicValues, ProgramProof), AirbenderError> {
|
||||
let tempdir = tempdir().map_err(AirbenderError::TempDir)?;
|
||||
let tempdir = tempdir().map_err(CommonError::tempdir)?;
|
||||
|
||||
let bin_path = tempdir.path().join("guest.bin");
|
||||
fs::write(&bin_path, &self.bin)
|
||||
.map_err(|err| AirbenderError::write_file(err, "guest.bin", &bin_path))?;
|
||||
.map_err(|err| CommonError::write_file("guest.bin", &bin_path, err))?;
|
||||
|
||||
let input_path = tempdir.path().join("input.hex");
|
||||
fs::write(&input_path, encode_input(input))
|
||||
.map_err(|err| AirbenderError::write_file(err, "input.hex", &input_path))?;
|
||||
.map_err(|err| CommonError::write_file("input.hex", &input_path, err))?;
|
||||
|
||||
let output_dir = tempdir.path().join("output");
|
||||
fs::create_dir_all(&output_dir)
|
||||
.map_err(|err| AirbenderError::create_dir(err, "output", &output_dir))?;
|
||||
.map_err(|err| CommonError::create_dir("output", &output_dir, err))?;
|
||||
|
||||
// Prove guest program + 1st recursion layer (tree of recursive proofs until root).
|
||||
let output = Command::new("airbender-cli")
|
||||
let mut cmd = Command::new("airbender-cli");
|
||||
let output = cmd
|
||||
.arg("prove")
|
||||
.arg("--bin")
|
||||
.arg(&bin_path)
|
||||
@@ -137,22 +140,24 @@ impl AirbenderSdk {
|
||||
.args(["--cycles", &u64::MAX.to_string()])
|
||||
.args(self.gpu.then_some("--gpu"))
|
||||
.output()
|
||||
.map_err(AirbenderError::AirbenderProve)?;
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(AirbenderError::AirbenderProveFailed {
|
||||
status: output.status,
|
||||
stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
|
||||
});
|
||||
Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
))?
|
||||
}
|
||||
|
||||
let proof_path = output_dir.join("recursion_program_proof.json");
|
||||
if !proof_path.exists() {
|
||||
return Err(AirbenderError::RecursionProofNotFound { path: proof_path });
|
||||
Err(CommonError::file_not_found("proof", &proof_path))?
|
||||
}
|
||||
|
||||
// Prove 2nd recursion layer (wrapping root of 1st recursion layer)
|
||||
let output = Command::new("airbender-cli")
|
||||
let mut cmd = Command::new("airbender-cli");
|
||||
let output = cmd
|
||||
.arg("prove-final")
|
||||
.arg("--input-file")
|
||||
.arg(&proof_path)
|
||||
@@ -160,26 +165,22 @@ impl AirbenderSdk {
|
||||
.arg(&output_dir)
|
||||
.args(self.gpu.then_some("--gpu"))
|
||||
.output()
|
||||
.map_err(AirbenderError::AirbenderProveFinal)?;
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(AirbenderError::AirbenderProveFinalFailed {
|
||||
status: output.status,
|
||||
stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
|
||||
});
|
||||
Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
))?
|
||||
}
|
||||
|
||||
let proof_path = output_dir.join("final_program_proof.json");
|
||||
if !proof_path.exists() {
|
||||
return Err(AirbenderError::FinalProofNotFound { path: proof_path });
|
||||
}
|
||||
let proof_bytes = fs::read(&proof_path)
|
||||
.map_err(|err| CommonError::read_file("proof", &proof_path, err))?;
|
||||
|
||||
let proof_bytes = fs::read(&proof_path).map_err(|err| {
|
||||
AirbenderError::read_file(err, "final_program_proof.json", &proof_path)
|
||||
})?;
|
||||
|
||||
let proof: ProgramProof =
|
||||
serde_json::from_slice(&proof_bytes).map_err(AirbenderError::JsonDeserialize)?;
|
||||
let proof: ProgramProof = serde_json::from_slice(&proof_bytes)
|
||||
.map_err(|err| CommonError::deserialize("proof", "serde_json", err))?;
|
||||
|
||||
let (public_values, vk_hash_chain) = extract_public_values_and_vk_hash_chain(&proof)?;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{compiler::AirbenderProgram, error::AirbenderError};
|
||||
use ere_compile_utils::CargoBuildCmd;
|
||||
use crate::{compiler::AirbenderProgram, error::CompileError};
|
||||
use ere_compile_utils::{CargoBuildCmd, CommonError};
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use std::{
|
||||
env,
|
||||
@@ -35,7 +35,7 @@ const LINKER_SCRIPT: &str = concat!(
|
||||
pub struct RustRv32ima;
|
||||
|
||||
impl Compiler for RustRv32ima {
|
||||
type Error = AirbenderError;
|
||||
type Error = CompileError;
|
||||
|
||||
type Program = AirbenderProgram;
|
||||
|
||||
@@ -52,31 +52,33 @@ impl Compiler for RustRv32ima {
|
||||
}
|
||||
}
|
||||
|
||||
fn objcopy_binary(elf: &[u8]) -> Result<Vec<u8>, AirbenderError> {
|
||||
let mut child = Command::new("rust-objcopy")
|
||||
fn objcopy_binary(elf: &[u8]) -> Result<Vec<u8>, CompileError> {
|
||||
let mut cmd = Command::new("rust-objcopy");
|
||||
let mut child = cmd
|
||||
.args(["-O", "binary", "-", "-"])
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(AirbenderError::RustObjcopy)?;
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
child
|
||||
.stdin
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.write_all(elf)
|
||||
.map_err(AirbenderError::RustObjcopyStdin)?;
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
let output = child
|
||||
.wait_with_output()
|
||||
.map_err(AirbenderError::RustObjcopy)?;
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(AirbenderError::RustObjcopyFailed {
|
||||
status: output.status,
|
||||
stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
|
||||
});
|
||||
Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
))?
|
||||
}
|
||||
|
||||
Ok(output.stdout)
|
||||
|
||||
@@ -1,73 +1,32 @@
|
||||
use crate::client::VkHashChain;
|
||||
use ere_zkvm_interface::zkVMError;
|
||||
use std::{
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
process::ExitStatus,
|
||||
};
|
||||
use ere_compile_utils::CommonError;
|
||||
use thiserror::Error;
|
||||
|
||||
impl From<AirbenderError> for zkVMError {
|
||||
fn from(value: AirbenderError) -> Self {
|
||||
zkVMError::Other(Box::new(value))
|
||||
}
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error(transparent)]
|
||||
CommonError(#[from] CommonError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum AirbenderError {
|
||||
// Compilation
|
||||
#[error(transparent)]
|
||||
CompileError(#[from] ere_compile_utils::CompileError),
|
||||
#[error("Failed to execute `rust-objcopy`: {0}")]
|
||||
RustObjcopy(#[source] io::Error),
|
||||
#[error("`rust-objcopy` failed with status: {status}\nstderr: {stderr}")]
|
||||
RustObjcopyFailed { status: ExitStatus, stderr: String },
|
||||
#[error("Failed to write ELF to `rust-objcopy` stdin: {0}")]
|
||||
RustObjcopyStdin(#[source] io::Error),
|
||||
|
||||
// IO and file system
|
||||
#[error("IO failure: {0}")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("IO failure in temporary directory: {0}")]
|
||||
TempDir(io::Error),
|
||||
|
||||
// Serialization
|
||||
#[error("Bincode encode failed: {0}")]
|
||||
BincodeEncode(#[from] bincode::error::EncodeError),
|
||||
#[error("Bincode decode failed: {0}")]
|
||||
BincodeDecode(#[from] bincode::error::DecodeError),
|
||||
#[error("JSON deserialization failed: {0}")]
|
||||
JsonDeserialize(#[from] serde_json::Error),
|
||||
CommonError(#[from] ere_zkvm_interface::CommonError),
|
||||
|
||||
// Execution
|
||||
#[error("Failed to execute `airbender-cli run`: {0}")]
|
||||
AirbenderRun(#[source] io::Error),
|
||||
#[error("`airbender-cli run` failed with status: {status}\nstderr: {stderr}")]
|
||||
AirbenderRunFailed { status: ExitStatus, stderr: String },
|
||||
#[error("Failed to parse public value from stdout: {0}")]
|
||||
ParsePublicValue(String),
|
||||
|
||||
#[error("Failed to parse cycles from stdout: {0}")]
|
||||
ParseCycles(String),
|
||||
|
||||
// Proving
|
||||
#[error("Failed to execute `airbender-cli prove`: {0}")]
|
||||
AirbenderProve(#[source] io::Error),
|
||||
#[error("`airbender-cli prove` failed with status: {status}\nstderr: {stderr}")]
|
||||
AirbenderProveFailed { status: ExitStatus, stderr: String },
|
||||
#[error("Failed to execute `airbender-cli prove-final`: {0}")]
|
||||
AirbenderProveFinal(#[source] io::Error),
|
||||
#[error("`airbender-cli prove-final` failed with status: {status}\nstderr: {stderr}")]
|
||||
AirbenderProveFinalFailed { status: ExitStatus, stderr: String },
|
||||
#[error("Recursion proof not found at {path}")]
|
||||
RecursionProofNotFound { path: PathBuf },
|
||||
#[error("Final proof not found at {path}")]
|
||||
FinalProofNotFound { path: PathBuf },
|
||||
|
||||
// Verification
|
||||
#[error("Proof verification failed")]
|
||||
ProofVerificationFailed,
|
||||
|
||||
#[error("Invalid final register count, expected 32 but got {0}")]
|
||||
InvalidRegisterCount(usize),
|
||||
|
||||
#[error(
|
||||
"Unexpected verification key hash chain - preprocessed: {preprocessed:?}, proved: {proved:?}"
|
||||
)]
|
||||
@@ -76,24 +35,3 @@ pub enum AirbenderError {
|
||||
proved: VkHashChain,
|
||||
},
|
||||
}
|
||||
|
||||
impl AirbenderError {
|
||||
pub fn io(err: io::Error, context: impl Into<String>) -> Self {
|
||||
Self::Io(io::Error::other(format!("{}: {}", context.into(), err)))
|
||||
}
|
||||
|
||||
pub fn create_dir(err: io::Error, id: &str, path: impl AsRef<Path>) -> Self {
|
||||
let ctx = format!("Failed to create dir {id} at {}", path.as_ref().display());
|
||||
Self::io(err, ctx)
|
||||
}
|
||||
|
||||
pub fn write_file(err: io::Error, id: &str, path: impl AsRef<Path>) -> Self {
|
||||
let ctx = format!("Failed to write {id} to {}", path.as_ref().display());
|
||||
Self::io(err, ctx)
|
||||
}
|
||||
|
||||
pub fn read_file(err: io::Error, id: &str, path: impl AsRef<Path>) -> Self {
|
||||
let ctx = format!("Failed to read {id} from {}", path.as_ref().display());
|
||||
Self::io(err, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
use crate::{client::AirbenderSdk, compiler::AirbenderProgram, error::AirbenderError};
|
||||
use airbender_execution_utils::ProgramProof;
|
||||
use anyhow::bail;
|
||||
use ere_zkvm_interface::{
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
CommonError, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM,
|
||||
};
|
||||
use std::time::Instant;
|
||||
|
||||
@@ -19,15 +20,18 @@ pub struct EreAirbender {
|
||||
}
|
||||
|
||||
impl EreAirbender {
|
||||
pub fn new(bin: AirbenderProgram, resource: ProverResourceType) -> Self {
|
||||
pub fn new(
|
||||
bin: AirbenderProgram,
|
||||
resource: ProverResourceType,
|
||||
) -> Result<Self, AirbenderError> {
|
||||
let gpu = matches!(resource, ProverResourceType::Gpu);
|
||||
let sdk = AirbenderSdk::new(&bin, gpu);
|
||||
Self { sdk }
|
||||
Ok(Self { sdk })
|
||||
}
|
||||
}
|
||||
|
||||
impl zkVM for EreAirbender {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, input: &[u8]) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
|
||||
let start = Instant::now();
|
||||
let (public_values, cycles) = self.sdk.execute(input)?;
|
||||
let execution_duration = start.elapsed();
|
||||
@@ -46,17 +50,19 @@ impl zkVM for EreAirbender {
|
||||
&self,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
|
||||
if proof_kind != ProofKind::Compressed {
|
||||
panic!("Only Compressed proof kind is supported.");
|
||||
bail!(CommonError::unsupported_proof_kind(
|
||||
proof_kind,
|
||||
[ProofKind::Compressed]
|
||||
))
|
||||
}
|
||||
|
||||
let start = Instant::now();
|
||||
let (public_values, proof) = self.sdk.prove(input)?;
|
||||
let proving_time = start.elapsed();
|
||||
|
||||
let proof_bytes = bincode::serde::encode_to_vec(&proof, bincode::config::legacy())
|
||||
.map_err(AirbenderError::BincodeEncode)?;
|
||||
.map_err(|err| CommonError::serialize("proof", "bincode", err))?;
|
||||
|
||||
Ok((
|
||||
public_values,
|
||||
@@ -65,14 +71,17 @@ impl zkVM for EreAirbender {
|
||||
))
|
||||
}
|
||||
|
||||
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
|
||||
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
|
||||
let Proof::Compressed(proof) = proof else {
|
||||
return Err(zkVMError::other("Only Compressed proof kind is supported."));
|
||||
bail!(CommonError::unsupported_proof_kind(
|
||||
proof.kind(),
|
||||
[ProofKind::Compressed]
|
||||
))
|
||||
};
|
||||
|
||||
let (proof, _): (ProgramProof, _) =
|
||||
bincode::serde::decode_from_slice(proof, bincode::config::legacy())
|
||||
.map_err(AirbenderError::BincodeDecode)?;
|
||||
.map_err(|err| CommonError::deserialize("proof", "bincode", err))?;
|
||||
|
||||
let public_values = self.sdk.verify(&proof)?;
|
||||
|
||||
@@ -115,7 +124,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_execute() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let test_case = BasicProgramInput::valid().into_output_sha256();
|
||||
run_zkvm_execute(&zkvm, &test_case);
|
||||
@@ -124,7 +133,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_execute_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.execute(&input).unwrap_err();
|
||||
@@ -134,7 +143,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prove() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let test_case = BasicProgramInput::valid().into_output_sha256();
|
||||
run_zkvm_execute(&zkvm, &test_case);
|
||||
@@ -144,7 +153,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prove_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreAirbender::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.prove(&input, ProofKind::default()).unwrap_err();
|
||||
|
||||
@@ -6,6 +6,7 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
tempfile.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
compiler::JoltProgram,
|
||||
error::{CompileError, JoltError},
|
||||
};
|
||||
use crate::{compiler::JoltProgram, error::CompileError};
|
||||
use ere_compile_utils::CargoBuildCmd;
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use std::{env, path::Path};
|
||||
@@ -39,7 +36,7 @@ fn make_linker_script() -> String {
|
||||
pub struct RustRv32ima;
|
||||
|
||||
impl Compiler for RustRv32ima {
|
||||
type Error = JoltError;
|
||||
type Error = CompileError;
|
||||
|
||||
type Program = JoltProgram;
|
||||
|
||||
@@ -50,8 +47,7 @@ impl Compiler for RustRv32ima {
|
||||
.toolchain(toolchain)
|
||||
.build_options(CARGO_BUILD_OPTIONS)
|
||||
.rustflags(RUSTFLAGS)
|
||||
.exec(guest_directory, TARGET_TRIPLE)
|
||||
.map_err(CompileError::CompileUtilError)?;
|
||||
.exec(guest_directory, TARGET_TRIPLE)?;
|
||||
Ok(elf)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
use crate::{
|
||||
compiler::JoltProgram,
|
||||
error::{CompileError, JoltError},
|
||||
};
|
||||
use ere_compile_utils::cargo_metadata;
|
||||
use crate::{compiler::JoltProgram, error::CompileError};
|
||||
use ere_compile_utils::{CommonError, cargo_metadata};
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use jolt::host::DEFAULT_TARGET_DIR;
|
||||
use std::{env::set_current_dir, fs, path::Path};
|
||||
@@ -12,18 +9,18 @@ use std::{env::set_current_dir, fs, path::Path};
|
||||
pub struct RustRv32imaCustomized;
|
||||
|
||||
impl Compiler for RustRv32imaCustomized {
|
||||
type Error = JoltError;
|
||||
type Error = CompileError;
|
||||
|
||||
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,
|
||||
set_current_dir(guest_directory).map_err(|err| CompileError::SetCurrentDirFailed {
|
||||
err,
|
||||
path: guest_directory.to_path_buf(),
|
||||
})?;
|
||||
|
||||
let metadata = cargo_metadata(guest_directory).map_err(CompileError::CompileUtilError)?;
|
||||
let metadata = cargo_metadata(guest_directory)?;
|
||||
let package_name = &metadata.root_package().unwrap().name;
|
||||
|
||||
// Note that if this fails, it will panic, hence we need to catch it.
|
||||
@@ -35,10 +32,8 @@ impl Compiler for RustRv32imaCustomized {
|
||||
})
|
||||
.map_err(|_| CompileError::BuildFailed)?;
|
||||
|
||||
let elf = fs::read(&elf_path).map_err(|source| CompileError::ReadElfFailed {
|
||||
source,
|
||||
path: elf_path.to_path_buf(),
|
||||
})?;
|
||||
let elf =
|
||||
fs::read(&elf_path).map_err(|err| CommonError::read_file("elf", &elf_path, err))?;
|
||||
|
||||
Ok(elf)
|
||||
}
|
||||
|
||||
@@ -1,55 +1,30 @@
|
||||
use ark_serialize::SerializationError;
|
||||
use ere_zkvm_interface::zkVMError;
|
||||
use ere_compile_utils::CommonError;
|
||||
use jolt::jolt_core::utils::errors::ProofVerifyError;
|
||||
use std::{io, path::PathBuf};
|
||||
use thiserror::Error;
|
||||
|
||||
impl From<JoltError> for zkVMError {
|
||||
fn from(value: JoltError) -> Self {
|
||||
zkVMError::Other(Box::new(value))
|
||||
}
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error(transparent)]
|
||||
CommonError(#[from] CommonError),
|
||||
|
||||
#[error("Failed to set current directory to {path}: {err}")]
|
||||
SetCurrentDirFailed {
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
err: io::Error,
|
||||
},
|
||||
|
||||
#[error("Failed to build guest")]
|
||||
BuildFailed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum JoltError {
|
||||
#[error(transparent)]
|
||||
Compile(#[from] CompileError),
|
||||
CommonError(#[from] ere_zkvm_interface::CommonError),
|
||||
|
||||
#[error(transparent)]
|
||||
Execute(#[from] ExecuteError),
|
||||
|
||||
#[error(transparent)]
|
||||
Prove(#[from] ProveError),
|
||||
|
||||
#[error(transparent)]
|
||||
Verify(#[from] VerifyError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error("Failed to build guest")]
|
||||
BuildFailed,
|
||||
#[error("Failed to read elf at {path}: {source}")]
|
||||
ReadElfFailed { source: io::Error, path: PathBuf },
|
||||
#[error("Failed to set current directory to {path}: {source}")]
|
||||
SetCurrentDirFailed { source: io::Error, path: PathBuf },
|
||||
#[error(transparent)]
|
||||
CompileUtilError(#[from] ere_compile_utils::CompileError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExecuteError {}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ProveError {
|
||||
#[error("Serialization failed")]
|
||||
Serialization(#[from] SerializationError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum VerifyError {
|
||||
#[error("Deserialization failed")]
|
||||
Serialization(#[from] SerializationError),
|
||||
// Verify
|
||||
#[error("Failed to verify proof: {0}")]
|
||||
VerifyProofFailed(#[from] ProofVerifyError),
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{EreJoltProof, error::VerifyError};
|
||||
use crate::{EreJoltProof, error::JoltError};
|
||||
use common::constants::{DEFAULT_MAX_BYTECODE_SIZE, DEFAULT_MAX_TRACE_LENGTH, DEFAULT_MEMORY_SIZE};
|
||||
use jolt::{
|
||||
Jolt, JoltHyperKZGProof, JoltProverPreprocessing, JoltVerifierPreprocessing, MemoryConfig,
|
||||
@@ -67,7 +67,7 @@ pub fn prove_generic(
|
||||
pub fn verify_generic(
|
||||
proof: EreJoltProof,
|
||||
preprocessing: JoltVerifierPreprocessing<4, jolt::F, jolt::PCS, jolt::ProofTranscript>,
|
||||
) -> Result<(), VerifyError> {
|
||||
) -> Result<(), JoltError> {
|
||||
let mut io_device = JoltDevice::new(&MemoryConfig {
|
||||
max_input_size: preprocessing.memory_layout.max_input_size,
|
||||
max_output_size: preprocessing.memory_layout.max_output_size,
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
|
||||
use crate::{
|
||||
compiler::JoltProgram,
|
||||
error::{JoltError, ProveError, VerifyError},
|
||||
error::JoltError,
|
||||
jolt_methods::{preprocess_prover, preprocess_verifier, prove_generic, verify_generic},
|
||||
};
|
||||
use anyhow::bail;
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
|
||||
use ere_zkvm_interface::{
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
CommonError, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM,
|
||||
};
|
||||
use jolt::{JoltHyperKZGProof, JoltProverPreprocessing, JoltVerifierPreprocessing};
|
||||
use std::{env, fs, io::Cursor};
|
||||
@@ -34,7 +35,10 @@ pub struct EreJolt {
|
||||
}
|
||||
|
||||
impl EreJolt {
|
||||
pub fn new(elf: JoltProgram, _resource: ProverResourceType) -> Result<Self, zkVMError> {
|
||||
pub fn new(elf: JoltProgram, resource: ProverResourceType) -> Result<Self, JoltError> {
|
||||
if !matches!(resource, ProverResourceType::Cpu) {
|
||||
panic!("Network or GPU proving not yet implemented for Miden. Use CPU resource type.");
|
||||
}
|
||||
let (_tempdir, program) = program(&elf)?;
|
||||
let prover_preprocessing = preprocess_prover(&program);
|
||||
let verifier_preprocessing = preprocess_verifier(&program);
|
||||
@@ -42,13 +46,13 @@ impl EreJolt {
|
||||
elf,
|
||||
prover_preprocessing,
|
||||
verifier_preprocessing,
|
||||
_resource,
|
||||
_resource: resource,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl zkVM for EreJolt {
|
||||
fn execute(&self, _input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, _input: &[u8]) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
|
||||
let (_tempdir, program) = program(&self.elf)?;
|
||||
|
||||
// TODO: Check how to pass private input to jolt, issue for tracking:
|
||||
@@ -66,9 +70,12 @@ impl zkVM for EreJolt {
|
||||
&self,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
|
||||
if proof_kind != ProofKind::Compressed {
|
||||
panic!("Only Compressed proof kind is supported.");
|
||||
bail!(CommonError::unsupported_proof_kind(
|
||||
proof_kind,
|
||||
[ProofKind::Compressed]
|
||||
))
|
||||
}
|
||||
|
||||
let (_tempdir, program) = program(&self.elf)?;
|
||||
@@ -80,7 +87,7 @@ impl zkVM for EreJolt {
|
||||
let mut proof_bytes = Vec::new();
|
||||
proof
|
||||
.serialize_compressed(&mut proof_bytes)
|
||||
.map_err(|err| JoltError::Prove(ProveError::Serialization(err)))?;
|
||||
.map_err(|err| CommonError::serialize("proof", "jolt", err))?;
|
||||
|
||||
// TODO: Public values
|
||||
let public_values = Vec::new();
|
||||
@@ -92,15 +99,18 @@ impl zkVM for EreJolt {
|
||||
))
|
||||
}
|
||||
|
||||
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
|
||||
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
|
||||
let Proof::Compressed(proof) = proof else {
|
||||
return Err(zkVMError::other("Only Compressed proof kind is supported."));
|
||||
bail!(CommonError::unsupported_proof_kind(
|
||||
proof.kind(),
|
||||
[ProofKind::Compressed]
|
||||
))
|
||||
};
|
||||
|
||||
let proof = EreJoltProof::deserialize_compressed(&mut Cursor::new(proof))
|
||||
.map_err(|err| JoltError::Verify(VerifyError::Serialization(err)))?;
|
||||
.map_err(|err| CommonError::deserialize("proof", "jolt", err))?;
|
||||
|
||||
verify_generic(proof, self.verifier_preprocessing.clone()).map_err(JoltError::Verify)?;
|
||||
verify_generic(proof, self.verifier_preprocessing.clone())?;
|
||||
|
||||
// TODO: Public values
|
||||
let public_values = Vec::new();
|
||||
@@ -120,10 +130,10 @@ impl zkVM for EreJolt {
|
||||
/// Create `jolt::host::Program` by storing the compiled `elf` to a temporary
|
||||
/// 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(zkVMError::other)?;
|
||||
pub fn program(elf: &[u8]) -> Result<(TempDir, jolt::host::Program), JoltError> {
|
||||
let tempdir = TempDir::new().map_err(CommonError::tempdir)?;
|
||||
let elf_path = tempdir.path().join("guest.elf");
|
||||
fs::write(&elf_path, elf).map_err(zkVMError::other)?;
|
||||
fs::write(&elf_path, elf).map_err(|err| CommonError::write_file("elf", &elf_path, err))?;
|
||||
// Set a dummy package name because we don't need to compile anymore.
|
||||
let mut program = jolt::host::Program::new("");
|
||||
program.elf = Some(elf_path);
|
||||
|
||||
@@ -6,7 +6,7 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bincode = { workspace = true, features = ["alloc", "serde"] }
|
||||
anyhow.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror.workspace = true
|
||||
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
use crate::{
|
||||
compiler::MidenProgram,
|
||||
error::{CompileError, MidenError},
|
||||
};
|
||||
use crate::{compiler::MidenProgram, error::CompileError};
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use miden_assembly::Assembler;
|
||||
use miden_stdlib::StdLibrary;
|
||||
use std::{fs, path::Path};
|
||||
use std::{env, fs, path::Path};
|
||||
|
||||
/// Compiler for Miden assembly guest program.
|
||||
pub struct MidenAsm;
|
||||
|
||||
impl Compiler for MidenAsm {
|
||||
type Error = MidenError;
|
||||
type Error = CompileError;
|
||||
type Program = MidenProgram;
|
||||
|
||||
fn compile(&self, guest_directory: &Path) -> Result<Self::Program, Self::Error> {
|
||||
@@ -21,29 +18,29 @@ impl Compiler for MidenAsm {
|
||||
.ok_or(CompileError::InvalidProgramPath)?;
|
||||
|
||||
let entrypoint = format!("{dir_name}.masm");
|
||||
let main_path = guest_directory.join(&entrypoint);
|
||||
if !main_path.exists() {
|
||||
let entrypoint_path = guest_directory.join(&entrypoint);
|
||||
if !entrypoint_path.exists() {
|
||||
return Err(CompileError::MissingEntrypoint {
|
||||
program_dir: guest_directory.display().to_string(),
|
||||
entrypoint,
|
||||
}
|
||||
.into());
|
||||
});
|
||||
}
|
||||
let source =
|
||||
fs::read_to_string(&entrypoint_path).map_err(|err| CompileError::ReadEntrypoint {
|
||||
entrypoint_path,
|
||||
err,
|
||||
})?;
|
||||
|
||||
// Compile using Miden assembler
|
||||
let mut assembler = Assembler::default().with_debug_mode(true);
|
||||
let mut assembler =
|
||||
Assembler::default().with_debug_mode(env::var_os("MIDEN_DEBUG").is_some());
|
||||
assembler
|
||||
.link_dynamic_library(StdLibrary::default())
|
||||
.map_err(|e| CompileError::LoadStdLibrary(e.to_string()))?;
|
||||
|
||||
let source = fs::read_to_string(&main_path).map_err(|e| CompileError::ReadSource {
|
||||
path: main_path.clone(),
|
||||
source: e,
|
||||
})?;
|
||||
.map_err(CompileError::LoadStdLibrary)?;
|
||||
|
||||
let program = assembler
|
||||
.assemble_program(&source)
|
||||
.map_err(|e| CompileError::AssemblyCompilation(e.to_string()))?;
|
||||
.map_err(CompileError::AssemblyCompilation)?;
|
||||
|
||||
Ok(MidenProgram(program))
|
||||
}
|
||||
|
||||
@@ -1,79 +1,48 @@
|
||||
use ere_zkvm_interface::zkVMError;
|
||||
use miden_core::utils::DeserializationError;
|
||||
use miden_assembly::Report;
|
||||
use miden_processor::ExecutionError;
|
||||
use miden_verifier::VerificationError;
|
||||
use std::path::PathBuf;
|
||||
use thiserror::Error;
|
||||
|
||||
impl From<MidenError> for zkVMError {
|
||||
fn from(value: MidenError) -> Self {
|
||||
zkVMError::Other(Box::new(value))
|
||||
}
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error("Invalid program directory name")]
|
||||
InvalidProgramPath,
|
||||
|
||||
#[error("Entrypoint `{entrypoint}` not found in {program_dir}")]
|
||||
MissingEntrypoint {
|
||||
program_dir: String,
|
||||
entrypoint: String,
|
||||
},
|
||||
|
||||
#[error("Failed to read assembly source at {entrypoint_path}: {err}")]
|
||||
ReadEntrypoint {
|
||||
entrypoint_path: PathBuf,
|
||||
#[source]
|
||||
err: std::io::Error,
|
||||
},
|
||||
|
||||
#[error("Failed to load Miden standard library: {0}")]
|
||||
LoadStdLibrary(Report),
|
||||
|
||||
#[error("Miden assembly compilation failed: {0}")]
|
||||
AssemblyCompilation(Report),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum MidenError {
|
||||
#[error(transparent)]
|
||||
Compile(#[from] CompileError),
|
||||
#[error(transparent)]
|
||||
Execute(#[from] ExecuteError),
|
||||
#[error(transparent)]
|
||||
Prove(#[from] ProveError),
|
||||
#[error(transparent)]
|
||||
Verify(#[from] VerifyError),
|
||||
}
|
||||
CommonError(#[from] ere_zkvm_interface::CommonError),
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error("Invalid program directory name")]
|
||||
InvalidProgramPath,
|
||||
#[error("Entrypoint '{entrypoint}' not found in {program_dir}")]
|
||||
MissingEntrypoint {
|
||||
program_dir: String,
|
||||
entrypoint: String,
|
||||
},
|
||||
#[error("Failed to read assembly source at {path}")]
|
||||
ReadSource {
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
},
|
||||
#[error("Miden assembly compilation failed: {0}")]
|
||||
AssemblyCompilation(String),
|
||||
#[error("Failed to load Miden standard library: {0}")]
|
||||
LoadStdLibrary(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExecuteError {
|
||||
// Execute
|
||||
#[error("Miden execution failed")]
|
||||
Execution(#[from] ExecutionError),
|
||||
#[error("Invalid input format: {0}")]
|
||||
InvalidInput(String),
|
||||
#[error("Serialization failed")]
|
||||
Serialization(#[from] bincode::error::EncodeError),
|
||||
#[error("Failed to deserialize Miden program")]
|
||||
ProgramDeserialization(#[from] DeserializationError),
|
||||
}
|
||||
Execute(#[from] ExecutionError),
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ProveError {
|
||||
#[error("Miden proving failed")]
|
||||
Proving(#[from] ExecutionError),
|
||||
#[error("Invalid input format: {0}")]
|
||||
InvalidInput(String),
|
||||
#[error("Output serialization failed")]
|
||||
OutputSerialization(#[from] bincode::error::EncodeError),
|
||||
}
|
||||
// Prove
|
||||
#[error("Miden proving failed: {0}")]
|
||||
Prove(#[source] ExecutionError),
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum VerifyError {
|
||||
// Verify
|
||||
#[error("Miden verification failed")]
|
||||
Verification(#[from] VerificationError),
|
||||
#[error("Proof or associated data deserialization failed")]
|
||||
MidenDeserialization(#[from] DeserializationError),
|
||||
#[error("Proof bundle deserialization failed")]
|
||||
BundleDeserialization(#[from] bincode::error::DecodeError),
|
||||
#[error("Output serialization failed")]
|
||||
OutputSerialization(#[from] bincode::error::EncodeError),
|
||||
Verify(#[from] VerificationError),
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::{
|
||||
compiler::MidenProgram,
|
||||
error::{ExecuteError, MidenError, ProveError, VerifyError},
|
||||
};
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
|
||||
use crate::{compiler::MidenProgram, error::MidenError};
|
||||
use anyhow::bail;
|
||||
use ere_zkvm_interface::{
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
CommonError, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM,
|
||||
};
|
||||
use miden_core::{
|
||||
Program,
|
||||
@@ -16,7 +16,6 @@ use miden_processor::{
|
||||
use miden_prover::{AdviceInputs, ExecutionProof, ProvingOptions, prove as miden_prove};
|
||||
use miden_stdlib::StdLibrary;
|
||||
use miden_verifier::verify as miden_verify;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{env, time::Instant};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
|
||||
@@ -26,13 +25,6 @@ pub mod error;
|
||||
|
||||
pub use miden_core::{Felt, FieldElement};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct MidenProofBundle {
|
||||
stack_inputs: Vec<u8>,
|
||||
stack_outputs: Vec<u8>,
|
||||
proof: Vec<u8>,
|
||||
}
|
||||
|
||||
/// [`zkVM`] implementation for Miden.
|
||||
///
|
||||
/// Miden VM takes list of field elements as input instead of bytes, so in
|
||||
@@ -43,18 +35,24 @@ struct MidenProofBundle {
|
||||
/// [`felts_to_bytes`] as well.
|
||||
pub struct EreMiden {
|
||||
program: Program,
|
||||
_resource: ProverResourceType,
|
||||
}
|
||||
|
||||
impl EreMiden {
|
||||
pub fn new(program: MidenProgram, _resource: ProverResourceType) -> Result<Self, MidenError> {
|
||||
Ok(Self { program: program.0 })
|
||||
pub fn new(program: MidenProgram, resource: ProverResourceType) -> Result<Self, MidenError> {
|
||||
if !matches!(resource, ProverResourceType::Cpu) {
|
||||
panic!("Network or GPU proving not yet implemented for Miden. Use CPU resource type.");
|
||||
}
|
||||
Ok(Self {
|
||||
program: program.0,
|
||||
_resource: resource,
|
||||
})
|
||||
}
|
||||
|
||||
fn setup_host() -> Result<DefaultHost, MidenError> {
|
||||
let mut host = DefaultHost::default();
|
||||
|
||||
host.load_library(&StdLibrary::default())
|
||||
.map_err(ExecuteError::Execution)
|
||||
.map_err(MidenError::Execute)?;
|
||||
|
||||
Ok(host)
|
||||
@@ -62,12 +60,9 @@ impl EreMiden {
|
||||
}
|
||||
|
||||
impl zkVM for EreMiden {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, input: &[u8]) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
|
||||
let stack_inputs = StackInputs::default();
|
||||
let advice_inputs = AdviceInputs::default().with_stack(
|
||||
bytes_to_felts(input)
|
||||
.map_err(|err| MidenError::Execute(ExecuteError::InvalidInput(err)))?,
|
||||
);
|
||||
let advice_inputs = AdviceInputs::default().with_stack(bytes_to_felts(input)?);
|
||||
let mut host = Self::setup_host()?;
|
||||
|
||||
let start = Instant::now();
|
||||
@@ -78,7 +73,7 @@ impl zkVM for EreMiden {
|
||||
&mut host,
|
||||
ExecutionOptions::default(),
|
||||
)
|
||||
.map_err(|e| MidenError::Execute(e.into()))?;
|
||||
.map_err(MidenError::Execute)?;
|
||||
|
||||
let public_values = felts_to_bytes(trace.stack_outputs().as_slice());
|
||||
|
||||
@@ -95,20 +90,21 @@ impl zkVM for EreMiden {
|
||||
&self,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
|
||||
if proof_kind != ProofKind::Compressed {
|
||||
panic!("Only Compressed proof kind is supported.");
|
||||
bail!(CommonError::unsupported_proof_kind(
|
||||
proof_kind,
|
||||
[ProofKind::Compressed]
|
||||
))
|
||||
}
|
||||
|
||||
let stack_inputs = StackInputs::default();
|
||||
let advice_inputs = AdviceInputs::default().with_stack(
|
||||
bytes_to_felts(input)
|
||||
.map_err(|err| MidenError::Prove(ProveError::InvalidInput(err)))?,
|
||||
);
|
||||
let advice_inputs = AdviceInputs::default().with_stack(bytes_to_felts(input)?);
|
||||
let mut host = Self::setup_host()?;
|
||||
|
||||
let start = Instant::now();
|
||||
let proving_options = ProvingOptions::with_96_bit_security(env::var("MIDEN_DEBUG").is_ok());
|
||||
let proving_options =
|
||||
ProvingOptions::with_96_bit_security(env::var_os("MIDEN_DEBUG").is_some());
|
||||
|
||||
let (stack_outputs, proof) = miden_prove(
|
||||
&self.program,
|
||||
@@ -117,18 +113,10 @@ impl zkVM for EreMiden {
|
||||
&mut host,
|
||||
proving_options,
|
||||
)
|
||||
.map_err(|e| MidenError::Prove(e.into()))?;
|
||||
.map_err(MidenError::Prove)?;
|
||||
|
||||
let public_values = felts_to_bytes(stack_outputs.as_slice());
|
||||
|
||||
let bundle = MidenProofBundle {
|
||||
stack_inputs: stack_inputs.to_bytes(),
|
||||
stack_outputs: stack_outputs.to_bytes(),
|
||||
proof: proof.to_bytes(),
|
||||
};
|
||||
|
||||
let proof_bytes = bincode::serde::encode_to_vec(&bundle, bincode::config::legacy())
|
||||
.map_err(|e| MidenError::Prove(e.into()))?;
|
||||
let proof_bytes = (stack_outputs, proof).to_bytes();
|
||||
|
||||
Ok((
|
||||
public_values,
|
||||
@@ -137,31 +125,23 @@ impl zkVM for EreMiden {
|
||||
))
|
||||
}
|
||||
|
||||
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
|
||||
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
|
||||
let Proof::Compressed(proof) = proof else {
|
||||
return Err(zkVMError::other("Only Compressed proof kind is supported."));
|
||||
bail!(CommonError::unsupported_proof_kind(
|
||||
proof.kind(),
|
||||
[ProofKind::Compressed]
|
||||
))
|
||||
};
|
||||
|
||||
let (bundle, _): (MidenProofBundle, _) =
|
||||
bincode::serde::decode_from_slice(proof, bincode::config::legacy())
|
||||
.map_err(|e| MidenError::Verify(VerifyError::BundleDeserialization(e)))?;
|
||||
|
||||
let program_info: ProgramInfo = self.program.clone().into();
|
||||
|
||||
let stack_inputs = StackInputs::read_from_bytes(&bundle.stack_inputs)
|
||||
.map_err(|e| MidenError::Verify(VerifyError::MidenDeserialization(e)))?;
|
||||
let stack_outputs = StackOutputs::read_from_bytes(&bundle.stack_outputs)
|
||||
.map_err(|e| MidenError::Verify(VerifyError::MidenDeserialization(e)))?;
|
||||
let execution_proof = ExecutionProof::from_bytes(&bundle.proof)
|
||||
.map_err(|e| MidenError::Verify(VerifyError::MidenDeserialization(e)))?;
|
||||
let stack_inputs = StackInputs::default();
|
||||
let (stack_outputs, proof): (StackOutputs, ExecutionProof) =
|
||||
Deserializable::read_from_bytes(proof)
|
||||
.map_err(|err| CommonError::deserialize("proof", "miden", err))?;
|
||||
|
||||
miden_verify(
|
||||
program_info,
|
||||
stack_inputs,
|
||||
stack_outputs.clone(),
|
||||
execution_proof,
|
||||
)
|
||||
.map_err(|e| MidenError::Verify(e.into()))?;
|
||||
miden_verify(program_info, stack_inputs, stack_outputs.clone(), proof)
|
||||
.map_err(MidenError::Verify)?;
|
||||
|
||||
Ok(felts_to_bytes(stack_outputs.as_slice()))
|
||||
}
|
||||
@@ -184,18 +164,19 @@ pub fn felts_to_bytes(felts: &[Felt]) -> Vec<u8> {
|
||||
}
|
||||
|
||||
/// Convert bytes into Miden field elements.
|
||||
pub fn bytes_to_felts(bytes: &[u8]) -> Result<Vec<Felt>, String> {
|
||||
pub fn bytes_to_felts(bytes: &[u8]) -> Result<Vec<Felt>, MidenError> {
|
||||
if bytes.len() % 8 != 0 {
|
||||
return Err(format!(
|
||||
let err = anyhow::anyhow!(
|
||||
"Invalid bytes length {}, expected multiple of 8",
|
||||
bytes.len()
|
||||
));
|
||||
);
|
||||
Err(CommonError::serialize("input", "miden", err))?;
|
||||
}
|
||||
bytes
|
||||
Ok(bytes
|
||||
.chunks(8)
|
||||
.map(|bytes| Felt::try_from(u64::from_le_bytes(bytes.try_into().unwrap())))
|
||||
.collect::<Result<Vec<Felt>, _>>()
|
||||
.map_err(|err| err.to_string())
|
||||
.map_err(|err| CommonError::serialize("input", "miden", anyhow::anyhow!(err)))?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -6,6 +6,7 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
bincode = { workspace = true, features = ["alloc", "serde"] }
|
||||
postcard.workspace = true
|
||||
serde.workspace = true
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
compiler::NexusProgram,
|
||||
error::{CompileError, NexusError},
|
||||
};
|
||||
use crate::{compiler::NexusProgram, error::CompileError};
|
||||
use ere_compile_utils::CargoBuildCmd;
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use std::path::Path;
|
||||
@@ -20,7 +17,7 @@ const CARGO_BUILD_OPTIONS: &[&str] = &[
|
||||
pub struct RustRv32i;
|
||||
|
||||
impl Compiler for RustRv32i {
|
||||
type Error = NexusError;
|
||||
type Error = CompileError;
|
||||
|
||||
type Program = NexusProgram;
|
||||
|
||||
@@ -32,8 +29,7 @@ impl Compiler for RustRv32i {
|
||||
.toolchain("nightly-2025-04-06")
|
||||
.build_options(CARGO_BUILD_OPTIONS)
|
||||
.rustflags(RUSTFLAGS)
|
||||
.exec(guest_directory, TARGET_TRIPLE)
|
||||
.map_err(CompileError::CompileUtilError)?;
|
||||
.exec(guest_directory, TARGET_TRIPLE)?;
|
||||
Ok(elf)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,30 @@
|
||||
use ere_zkvm_interface::zkVMError;
|
||||
use std::path::PathBuf;
|
||||
use nexus_sdk::stwo::seq::Error as StwoError;
|
||||
use nexus_vm::error::VMError;
|
||||
use thiserror::Error;
|
||||
|
||||
impl From<NexusError> for zkVMError {
|
||||
fn from(value: NexusError) -> Self {
|
||||
zkVMError::Other(Box::new(value))
|
||||
}
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error(transparent)]
|
||||
CommonError(#[from] ere_compile_utils::CommonError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum NexusError {
|
||||
#[error(transparent)]
|
||||
Compile(#[from] CompileError),
|
||||
CommonError(#[from] ere_zkvm_interface::CommonError),
|
||||
|
||||
#[error(transparent)]
|
||||
Prove(#[from] ProveError),
|
||||
#[error("Parse ELF failed: {0}")]
|
||||
ParseElf(#[source] VMError),
|
||||
|
||||
#[error(transparent)]
|
||||
Verify(#[from] VerifyError),
|
||||
}
|
||||
|
||||
#[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] ere_compile_utils::CompileError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ProveError {
|
||||
#[error("nexus execution failed: {0}")]
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
#[error("Serialising proof with `bincode` failed: {0}")]
|
||||
Bincode(#[from] bincode::error::EncodeError),
|
||||
#[error("Serialising input with `postcard` failed: {0}")]
|
||||
Postcard(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum VerifyError {
|
||||
#[error("nexus verification failed: {0}")]
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
#[error("Deserialising proof failed: {0}")]
|
||||
Bincode(#[from] bincode::error::DecodeError),
|
||||
// Execute
|
||||
#[error("Nexus execution failed: {0}")]
|
||||
Execute(#[source] VMError),
|
||||
|
||||
// Prove
|
||||
#[error("Nexus proving failed: {0}")]
|
||||
Prove(#[source] StwoError),
|
||||
|
||||
// Verify
|
||||
#[error("Nexus verification failed: {0}")]
|
||||
Verify(#[source] StwoError),
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
|
||||
use crate::{
|
||||
compiler::NexusProgram,
|
||||
error::{NexusError, ProveError, VerifyError},
|
||||
};
|
||||
use crate::{compiler::NexusProgram, error::NexusError};
|
||||
use anyhow::bail;
|
||||
use ere_zkvm_interface::{
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
CommonError, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM,
|
||||
};
|
||||
use nexus_core::nvm::{self, ElfFile};
|
||||
use nexus_sdk::{
|
||||
@@ -34,18 +32,17 @@ pub struct EreNexus {
|
||||
}
|
||||
|
||||
impl EreNexus {
|
||||
pub fn new(elf: NexusProgram, resource: ProverResourceType) -> Self {
|
||||
pub fn new(elf: NexusProgram, resource: ProverResourceType) -> Result<Self, NexusError> {
|
||||
if !matches!(resource, ProverResourceType::Cpu) {
|
||||
panic!("Network or GPU proving not yet implemented for Nexus. Use CPU resource type.");
|
||||
}
|
||||
Self { elf }
|
||||
Ok(Self { elf })
|
||||
}
|
||||
}
|
||||
|
||||
impl zkVM for EreNexus {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
let elf = ElfFile::from_bytes(&self.elf)
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
fn execute(&self, input: &[u8]) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
|
||||
let elf = ElfFile::from_bytes(&self.elf).map_err(NexusError::ParseElf)?;
|
||||
|
||||
// Nexus sdk does not provide a trace, so we need to use core `nvm`
|
||||
// Encoding is copied directly from `prove_with_input`
|
||||
@@ -53,7 +50,7 @@ impl zkVM for EreNexus {
|
||||
Vec::new()
|
||||
} else {
|
||||
postcard::to_stdvec_cobs(&input)
|
||||
.map_err(|e| NexusError::Prove(ProveError::Postcard(e.to_string())))?
|
||||
.map_err(|err| CommonError::serialize("input", "postcard", err))?
|
||||
};
|
||||
|
||||
if !private_encoded.is_empty() {
|
||||
@@ -64,12 +61,12 @@ impl zkVM for EreNexus {
|
||||
|
||||
let start = Instant::now();
|
||||
let (view, trace) = nvm::k_trace(elf, &[], &[], private_encoded.as_slice(), 1)
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
.map_err(NexusError::Execute)?;
|
||||
let execution_duration = start.elapsed();
|
||||
|
||||
let public_values = view
|
||||
.public_output()
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
.map_err(|err| CommonError::deserialize("public_values", "postcard", err))?;
|
||||
|
||||
Ok((
|
||||
public_values,
|
||||
@@ -85,26 +82,27 @@ impl zkVM for EreNexus {
|
||||
&self,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
|
||||
if proof_kind != ProofKind::Compressed {
|
||||
panic!("Only Compressed proof kind is supported.");
|
||||
bail!(CommonError::unsupported_proof_kind(
|
||||
proof_kind,
|
||||
[ProofKind::Compressed]
|
||||
))
|
||||
}
|
||||
|
||||
let elf = ElfFile::from_bytes(&self.elf)
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
let elf = ElfFile::from_bytes(&self.elf).map_err(NexusError::ParseElf)?;
|
||||
|
||||
let prover =
|
||||
Stwo::new(&elf).map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
let prover = Stwo::new(&elf).map_err(NexusError::Prove)?;
|
||||
|
||||
let start = Instant::now();
|
||||
let (view, proof) = prover
|
||||
.prove_with_input(&input, &())
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
.map_err(NexusError::Prove)?;
|
||||
let proving_time = start.elapsed();
|
||||
|
||||
let public_values = view
|
||||
.public_output()
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
.map_err(|err| CommonError::deserialize("public_values", "postcard", err))?;
|
||||
|
||||
let proof_bundle = NexusProofBundle {
|
||||
proof,
|
||||
@@ -112,7 +110,7 @@ impl zkVM for EreNexus {
|
||||
};
|
||||
|
||||
let proof_bytes = bincode::serde::encode_to_vec(&proof_bundle, bincode::config::legacy())
|
||||
.map_err(|err| NexusError::Prove(ProveError::Bincode(err)))?;
|
||||
.map_err(|err| CommonError::serialize("proof", "bincode", err))?;
|
||||
|
||||
Ok((
|
||||
proof_bundle.public_values,
|
||||
@@ -121,16 +119,19 @@ impl zkVM for EreNexus {
|
||||
))
|
||||
}
|
||||
|
||||
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
|
||||
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
|
||||
let Proof::Compressed(proof) = proof else {
|
||||
return Err(zkVMError::other("Only Compressed proof kind is supported."));
|
||||
bail!(CommonError::unsupported_proof_kind(
|
||||
proof.kind(),
|
||||
[ProofKind::Compressed]
|
||||
))
|
||||
};
|
||||
|
||||
info!("Verifying proof...");
|
||||
|
||||
let (proof_bundle, _): (NexusProofBundle, _) =
|
||||
bincode::serde::decode_from_slice(proof, bincode::config::legacy())
|
||||
.map_err(|err| NexusError::Verify(VerifyError::Bincode(err)))?;
|
||||
.map_err(|err| CommonError::deserialize("proof", "bincode", err))?;
|
||||
|
||||
proof_bundle
|
||||
.proof
|
||||
@@ -141,7 +142,7 @@ impl zkVM for EreNexus {
|
||||
&self.elf,
|
||||
&[],
|
||||
)
|
||||
.map_err(|e| NexusError::Verify(VerifyError::Client(e.into())))?;
|
||||
.map_err(NexusError::Verify)?;
|
||||
|
||||
info!("Verify Succeeded!");
|
||||
|
||||
@@ -181,7 +182,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_execute() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_execute(&zkvm, &test_case);
|
||||
@@ -190,7 +191,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_execute_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.execute(&input).unwrap_err();
|
||||
@@ -200,7 +201,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prove() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_prove(&zkvm, &test_case);
|
||||
@@ -209,7 +210,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prove_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.prove(&input, ProofKind::default()).unwrap_err();
|
||||
|
||||
@@ -6,6 +6,8 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
eyre.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror.workspace = true
|
||||
toml.workspace = true
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::error::CompileError;
|
||||
use ere_compile_utils::CommonError;
|
||||
use openvm_sdk::config::{AppConfig, DEFAULT_APP_LOG_BLOWUP, DEFAULT_LEAF_LOG_BLOWUP, SdkVmConfig};
|
||||
use openvm_stark_sdk::config::FriParameters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -22,13 +23,10 @@ impl OpenVMProgram {
|
||||
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)?
|
||||
let toml = fs::read_to_string(app_config_path.as_ref())
|
||||
.map_err(|err| CommonError::read_file("app_config", &app_config_path, err))?;
|
||||
toml::from_str(&toml)
|
||||
.map_err(|err| CommonError::deserialize("app_config", "toml", err))?
|
||||
} else {
|
||||
// The default `AppConfig` copied from https://github.com/openvm-org/openvm/blob/v1.4.0/crates/cli/src/default.rs#L35.
|
||||
AppConfig {
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
compiler::OpenVMProgram,
|
||||
error::{CompileError, OpenVMError},
|
||||
};
|
||||
use crate::{compiler::OpenVMProgram, error::CompileError};
|
||||
use ere_compile_utils::CargoBuildCmd;
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use std::{env, path::Path};
|
||||
@@ -38,7 +35,7 @@ const CARGO_BUILD_OPTIONS: &[&str] = &[
|
||||
pub struct RustRv32ima;
|
||||
|
||||
impl Compiler for RustRv32ima {
|
||||
type Error = OpenVMError;
|
||||
type Error = CompileError;
|
||||
|
||||
type Program = OpenVMProgram;
|
||||
|
||||
@@ -48,12 +45,8 @@ impl Compiler for RustRv32ima {
|
||||
.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"),
|
||||
)?)
|
||||
.exec(guest_directory, TARGET_TRIPLE)?;
|
||||
OpenVMProgram::from_elf_and_app_config_path(elf, guest_directory.join("openvm.toml"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use crate::{
|
||||
compiler::OpenVMProgram,
|
||||
error::{CompileError, OpenVMError},
|
||||
};
|
||||
use crate::{compiler::OpenVMProgram, error::CompileError};
|
||||
use ere_compile_utils::CommonError;
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use openvm_build::GuestOptions;
|
||||
use std::{fs, path::Path};
|
||||
@@ -11,7 +9,7 @@ use std::{fs, path::Path};
|
||||
pub struct RustRv32imaCustomized;
|
||||
|
||||
impl Compiler for RustRv32imaCustomized {
|
||||
type Error = OpenVMError;
|
||||
type Error = CompileError;
|
||||
|
||||
type Program = OpenVMProgram;
|
||||
|
||||
@@ -26,16 +24,11 @@ impl Compiler for RustRv32imaCustomized {
|
||||
};
|
||||
|
||||
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(),
|
||||
})?;
|
||||
.map_err(CompileError::UniqueElfNotFound)?;
|
||||
let elf =
|
||||
fs::read(&elf_path).map_err(|err| CommonError::read_file("elf", &elf_path, err))?;
|
||||
|
||||
Ok(OpenVMProgram::from_elf_and_app_config_path(
|
||||
elf,
|
||||
guest_directory.join("openvm.toml"),
|
||||
)?)
|
||||
OpenVMProgram::from_elf_and_app_config_path(elf, guest_directory.join("openvm.toml"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,98 +1,60 @@
|
||||
use ere_zkvm_interface::zkVMError;
|
||||
use openvm_sdk::{SdkError, commit::AppExecutionCommit};
|
||||
use std::{io, path::PathBuf};
|
||||
use thiserror::Error;
|
||||
|
||||
impl From<OpenVMError> for zkVMError {
|
||||
fn from(value: OpenVMError) -> Self {
|
||||
zkVMError::Other(Box::new(value))
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error(transparent)]
|
||||
CommonError(#[from] ere_compile_utils::CommonError),
|
||||
|
||||
impl From<CommonError> for zkVMError {
|
||||
fn from(value: CommonError) -> Self {
|
||||
zkVMError::Other(Box::new(value))
|
||||
}
|
||||
#[error("Failed to build guest, code: {0}")]
|
||||
BuildFailed(i32),
|
||||
|
||||
#[error("Guest building skipped (OPENVM_SKIP_BUILD is set)")]
|
||||
BuildSkipped,
|
||||
|
||||
#[error("Missing to find unique elf: {0}")]
|
||||
UniqueElfNotFound(eyre::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum OpenVMError {
|
||||
#[error(transparent)]
|
||||
Compile(#[from] CompileError),
|
||||
CommonError(#[from] ere_zkvm_interface::CommonError),
|
||||
|
||||
#[error(transparent)]
|
||||
Execute(#[from] ExecuteError),
|
||||
|
||||
#[error(transparent)]
|
||||
Prove(#[from] ProveError),
|
||||
|
||||
#[error(transparent)]
|
||||
Verify(#[from] VerifyError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error("Failed to build guest, code: {0}")]
|
||||
BuildFailed(i32),
|
||||
#[error("Guest building skipped (OPENVM_SKIP_BUILD is set)")]
|
||||
BuildSkipped,
|
||||
#[error("Missing to find unique elf: {0}")]
|
||||
UniqueElfNotFound(Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
#[error("Failed to read elf at {path}: {source}")]
|
||||
ReadElfFailed { source: io::Error, path: PathBuf },
|
||||
#[error("Failed to read OpenVM's config file at {path}: {source}")]
|
||||
ReadConfigFailed { source: io::Error, path: PathBuf },
|
||||
#[error("Failed to deserialize OpenVM's config file: {0}")]
|
||||
DeserializeConfigFailed(toml::de::Error),
|
||||
#[error(transparent)]
|
||||
CompileUtilError(#[from] ere_compile_utils::CompileError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExecuteError {
|
||||
#[error("OpenVM execute failed: {0}")]
|
||||
Execute(#[source] SdkError),
|
||||
#[error(transparent)]
|
||||
Common(#[from] CommonError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ProveError {
|
||||
#[error("OpenVM prove failed: {0}")]
|
||||
Prove(#[source] SdkError),
|
||||
#[error("Unexpected app commit: {proved:?}, expected: {preprocessed:?}")]
|
||||
UnexpectedAppCommit {
|
||||
preprocessed: AppExecutionCommit,
|
||||
proved: AppExecutionCommit,
|
||||
},
|
||||
#[error("Serialize proof failed: {0}")]
|
||||
SerializeProof(io::Error),
|
||||
#[error(transparent)]
|
||||
Common(#[from] CommonError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum VerifyError {
|
||||
#[error("OpenVM verification failed: {0}")]
|
||||
Verify(#[source] SdkError),
|
||||
#[error("Deserialize proof failed: {0}")]
|
||||
DeserializeProof(io::Error),
|
||||
#[error(transparent)]
|
||||
Common(#[from] CommonError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CommonError {
|
||||
// Common
|
||||
#[error("Initialize SDK failed: {0}")]
|
||||
SdkInit(SdkError),
|
||||
|
||||
#[error("Decode elf failed: {0}")]
|
||||
ElfDecode(Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
ElfDecode(eyre::Error),
|
||||
|
||||
#[error("Transpile elf failed: {0}")]
|
||||
Transpile(SdkError),
|
||||
|
||||
#[error("Read aggregation key failed: {0}")]
|
||||
ReadAggKeyFailed(Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
ReadAggKeyFailed(eyre::Error),
|
||||
|
||||
#[error("Initialize prover failed: {0}")]
|
||||
ProverInit(SdkError),
|
||||
|
||||
#[error("Invalid public value")]
|
||||
InvalidPublicValue,
|
||||
|
||||
// Execute
|
||||
#[error("OpenVM execution failed: {0}")]
|
||||
Execute(#[source] SdkError),
|
||||
|
||||
// Prove
|
||||
#[error("OpenVM proving failed: {0}")]
|
||||
Prove(#[source] SdkError),
|
||||
|
||||
#[error("Unexpected app commit: {proved:?}, expected: {preprocessed:?}")]
|
||||
UnexpectedAppCommit {
|
||||
preprocessed: Box<AppExecutionCommit>,
|
||||
proved: Box<AppExecutionCommit>,
|
||||
},
|
||||
|
||||
// Verify
|
||||
#[error("OpenVM verification failed: {0}")]
|
||||
Verify(#[source] SdkError),
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
|
||||
use crate::{
|
||||
compiler::OpenVMProgram,
|
||||
error::{CommonError, ExecuteError, OpenVMError, ProveError, VerifyError},
|
||||
};
|
||||
use crate::{compiler::OpenVMProgram, error::OpenVMError};
|
||||
use anyhow::bail;
|
||||
use ere_zkvm_interface::{
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
CommonError, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM,
|
||||
};
|
||||
use openvm_circuit::arch::instructions::exe::VmExe;
|
||||
use openvm_continuations::verifier::internal::types::VmStarkProof;
|
||||
@@ -38,7 +36,7 @@ pub struct EreOpenVM {
|
||||
}
|
||||
|
||||
impl EreOpenVM {
|
||||
pub fn new(program: OpenVMProgram, resource: ProverResourceType) -> Result<Self, zkVMError> {
|
||||
pub fn new(program: OpenVMProgram, resource: ProverResourceType) -> Result<Self, OpenVMError> {
|
||||
match resource {
|
||||
#[cfg(not(feature = "cuda"))]
|
||||
ProverResourceType::Gpu => {
|
||||
@@ -52,24 +50,23 @@ impl EreOpenVM {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let sdk = CpuSdk::new(program.app_config.clone()).map_err(CommonError::SdkInit)?;
|
||||
let sdk = CpuSdk::new(program.app_config.clone()).map_err(OpenVMError::SdkInit)?;
|
||||
|
||||
let elf = Elf::decode(&program.elf, MEM_SIZE as u32)
|
||||
.map_err(|e| CommonError::ElfDecode(e.into()))?;
|
||||
let elf = Elf::decode(&program.elf, MEM_SIZE as u32).map_err(OpenVMError::ElfDecode)?;
|
||||
|
||||
let app_exe = sdk.convert_to_exe(elf).map_err(CommonError::Transpile)?;
|
||||
let app_exe = sdk.convert_to_exe(elf).map_err(OpenVMError::Transpile)?;
|
||||
|
||||
let (app_pk, _) = sdk.app_keygen();
|
||||
|
||||
let agg_pk = read_object_from_file::<AggProvingKey, _>(agg_pk_path())
|
||||
.map_err(|e| CommonError::ReadAggKeyFailed(e.into()))?;
|
||||
.map_err(OpenVMError::ReadAggKeyFailed)?;
|
||||
let agg_vk = agg_pk.get_agg_vk();
|
||||
|
||||
let _ = sdk.set_agg_pk(agg_pk.clone());
|
||||
|
||||
let app_commit = sdk
|
||||
.prover(app_exe.clone())
|
||||
.map_err(CommonError::ProverInit)?
|
||||
.map_err(OpenVMError::ProverInit)?
|
||||
.app_commit();
|
||||
|
||||
Ok(Self {
|
||||
@@ -83,18 +80,18 @@ impl EreOpenVM {
|
||||
})
|
||||
}
|
||||
|
||||
fn cpu_sdk(&self) -> Result<CpuSdk, CommonError> {
|
||||
fn cpu_sdk(&self) -> Result<CpuSdk, OpenVMError> {
|
||||
let sdk = CpuSdk::new_without_transpiler(self.app_config.clone())
|
||||
.map_err(CommonError::SdkInit)?;
|
||||
.map_err(OpenVMError::SdkInit)?;
|
||||
let _ = sdk.set_app_pk(self.app_pk.clone());
|
||||
let _ = sdk.set_agg_pk(self.agg_pk.clone());
|
||||
Ok(sdk)
|
||||
}
|
||||
|
||||
#[cfg(feature = "cuda")]
|
||||
fn gpu_sdk(&self) -> Result<openvm_sdk::GpuSdk, CommonError> {
|
||||
fn gpu_sdk(&self) -> Result<openvm_sdk::GpuSdk, OpenVMError> {
|
||||
let sdk = openvm_sdk::GpuSdk::new_without_transpiler(self.app_config.clone())
|
||||
.map_err(CommonError::SdkInit)?;
|
||||
.map_err(OpenVMError::SdkInit)?;
|
||||
let _ = sdk.set_app_pk(self.app_pk.clone());
|
||||
let _ = sdk.set_agg_pk(self.agg_pk.clone());
|
||||
Ok(sdk)
|
||||
@@ -102,7 +99,7 @@ impl EreOpenVM {
|
||||
}
|
||||
|
||||
impl zkVM for EreOpenVM {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, input: &[u8]) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
|
||||
let mut stdin = StdIn::default();
|
||||
stdin.write_bytes(input);
|
||||
|
||||
@@ -110,7 +107,7 @@ impl zkVM for EreOpenVM {
|
||||
let public_values = self
|
||||
.cpu_sdk()?
|
||||
.execute(self.app_exe.clone(), stdin)
|
||||
.map_err(|e| OpenVMError::from(ExecuteError::Execute(e)))?;
|
||||
.map_err(OpenVMError::Execute)?;
|
||||
|
||||
Ok((
|
||||
public_values,
|
||||
@@ -125,9 +122,12 @@ impl zkVM for EreOpenVM {
|
||||
&self,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
|
||||
if proof_kind != ProofKind::Compressed {
|
||||
panic!("Only Compressed proof kind is supported.");
|
||||
bail!(CommonError::unsupported_proof_kind(
|
||||
proof_kind,
|
||||
[ProofKind::Compressed]
|
||||
))
|
||||
}
|
||||
|
||||
let mut stdin = StdIn::default();
|
||||
@@ -148,20 +148,20 @@ impl zkVM for EreOpenVM {
|
||||
);
|
||||
}
|
||||
}
|
||||
.map_err(|e| OpenVMError::from(ProveError::Prove(e)))?;
|
||||
.map_err(OpenVMError::Prove)?;
|
||||
let elapsed = now.elapsed();
|
||||
|
||||
if app_commit != self.app_commit {
|
||||
return Err(OpenVMError::from(ProveError::UnexpectedAppCommit {
|
||||
preprocessed: self.app_commit,
|
||||
proved: app_commit,
|
||||
}))?;
|
||||
bail!(OpenVMError::UnexpectedAppCommit {
|
||||
preprocessed: self.app_commit.into(),
|
||||
proved: app_commit.into(),
|
||||
});
|
||||
}
|
||||
|
||||
let public_values = extract_public_values(&proof.user_public_values)?;
|
||||
let proof_bytes = proof
|
||||
.encode_to_vec()
|
||||
.map_err(|e| OpenVMError::from(ProveError::SerializeProof(e)))?;
|
||||
.map_err(|err| CommonError::serialize("proof", "openvm_sdk", err))?;
|
||||
|
||||
Ok((
|
||||
public_values,
|
||||
@@ -170,16 +170,18 @@ impl zkVM for EreOpenVM {
|
||||
))
|
||||
}
|
||||
|
||||
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
|
||||
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
|
||||
let Proof::Compressed(proof) = proof else {
|
||||
return Err(zkVMError::other("Only Compressed proof kind is supported."));
|
||||
bail!(CommonError::unsupported_proof_kind(
|
||||
proof.kind(),
|
||||
[ProofKind::Compressed]
|
||||
))
|
||||
};
|
||||
|
||||
let proof = VmStarkProof::<SC>::decode(&mut proof.as_slice())
|
||||
.map_err(|e| OpenVMError::from(VerifyError::DeserializeProof(e)))?;
|
||||
.map_err(|err| CommonError::deserialize("proof", "openvm_sdk", err))?;
|
||||
|
||||
CpuSdk::verify_proof(&self.agg_vk, self.app_commit, &proof)
|
||||
.map_err(|e| OpenVMError::Verify(VerifyError::Verify(e)))?;
|
||||
CpuSdk::verify_proof(&self.agg_vk, self.app_commit, &proof).map_err(OpenVMError::Verify)?;
|
||||
|
||||
let public_values = extract_public_values(&proof.user_public_values)?;
|
||||
|
||||
@@ -199,12 +201,12 @@ impl zkVM for EreOpenVM {
|
||||
///
|
||||
/// The public values revealed in guest program will be flatten into `Vec<u8>`
|
||||
/// then converted to field elements `Vec<F>`, so here we try to downcast it.
|
||||
fn extract_public_values(user_public_values: &[F]) -> Result<Vec<u8>, CommonError> {
|
||||
fn extract_public_values(user_public_values: &[F]) -> Result<Vec<u8>, OpenVMError> {
|
||||
user_public_values
|
||||
.iter()
|
||||
.map(|v| u8::try_from(v.as_canonical_u32()).ok())
|
||||
.collect::<Option<_>>()
|
||||
.ok_or(CommonError::InvalidPublicValue)
|
||||
.ok_or(OpenVMError::InvalidPublicValue)
|
||||
}
|
||||
|
||||
fn agg_pk_path() -> PathBuf {
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
compiler::PicoProgram,
|
||||
error::{CompileError, PicoError},
|
||||
};
|
||||
use crate::{compiler::PicoProgram, error::CompileError};
|
||||
use ere_compile_utils::CargoBuildCmd;
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use std::{env, path::Path};
|
||||
@@ -35,7 +32,7 @@ const CARGO_BUILD_OPTIONS: &[&str] = &[
|
||||
pub struct RustRv32ima;
|
||||
|
||||
impl Compiler for RustRv32ima {
|
||||
type Error = PicoError;
|
||||
type Error = CompileError;
|
||||
|
||||
type Program = PicoProgram;
|
||||
|
||||
@@ -45,8 +42,7 @@ impl Compiler for RustRv32ima {
|
||||
.toolchain(toolchain)
|
||||
.build_options(CARGO_BUILD_OPTIONS)
|
||||
.rustflags(RUSTFLAGS)
|
||||
.exec(guest_directory, TARGET_TRIPLE)
|
||||
.map_err(CompileError::CompileUtilError)?;
|
||||
.exec(guest_directory, TARGET_TRIPLE)?;
|
||||
Ok(elf)
|
||||
}
|
||||
}
|
||||
@@ -68,7 +64,7 @@ mod tests {
|
||||
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);
|
||||
let zkvm = ErePico::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
zkvm.execute(&[]).unwrap();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::error::{CompileError, PicoError};
|
||||
use crate::error::CompileError;
|
||||
use ere_compile_utils::{CommonError, cargo_metadata};
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use std::{fs, path::Path, process::Command};
|
||||
use tempfile::tempdir;
|
||||
@@ -8,43 +9,31 @@ use tempfile::tempdir;
|
||||
pub struct RustRv32imaCustomized;
|
||||
|
||||
impl Compiler for RustRv32imaCustomized {
|
||||
type Error = PicoError;
|
||||
type Error = CompileError;
|
||||
|
||||
type Program = Vec<u8>;
|
||||
|
||||
fn compile(&self, guest_directory: &Path) -> Result<Self::Program, Self::Error> {
|
||||
let tempdir = tempdir().map_err(CompileError::Tempdir)?;
|
||||
let tempdir = tempdir().map_err(CommonError::tempdir)?;
|
||||
|
||||
// 1. Check guest path
|
||||
if !guest_directory.exists() {
|
||||
return Err(CompileError::PathNotFound(guest_directory.to_path_buf()))?;
|
||||
}
|
||||
cargo_metadata(guest_directory)?;
|
||||
|
||||
// 2. Run `cargo pico build`
|
||||
let status = Command::new("cargo")
|
||||
let mut cmd = Command::new("cargo");
|
||||
let status = cmd
|
||||
.current_dir(guest_directory)
|
||||
.env("RUST_LOG", "info")
|
||||
.args(["pico", "build", "--output-directory"])
|
||||
.arg(tempdir.path())
|
||||
.status()
|
||||
.map_err(CompileError::CargoPicoBuild)?;
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(CompileError::CargoPicoBuildFailed { status })?;
|
||||
return Err(CommonError::command_exit_non_zero(&cmd, status, None))?;
|
||||
}
|
||||
|
||||
// 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,
|
||||
})?;
|
||||
let elf_bytes =
|
||||
fs::read(&elf_path).map_err(|err| CommonError::read_file("elf", &elf_path, err))?;
|
||||
|
||||
Ok(elf_bytes)
|
||||
}
|
||||
|
||||
@@ -1,85 +1,40 @@
|
||||
use ere_zkvm_interface::zkVMError;
|
||||
use std::{io, path::PathBuf, process::ExitStatus};
|
||||
use thiserror::Error;
|
||||
|
||||
impl From<PicoError> for zkVMError {
|
||||
fn from(value: PicoError) -> Self {
|
||||
zkVMError::Other(Box::new(value))
|
||||
}
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error(transparent)]
|
||||
CommonError(#[from] ere_compile_utils::CommonError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum PicoError {
|
||||
#[error(transparent)]
|
||||
Compile(#[from] CompileError),
|
||||
CommonError(#[from] ere_zkvm_interface::CommonError),
|
||||
|
||||
#[error(transparent)]
|
||||
Execute(#[from] ExecuteError),
|
||||
|
||||
#[error(transparent)]
|
||||
Prove(#[from] ProveError),
|
||||
|
||||
#[error(transparent)]
|
||||
Verify(#[from] VerifyError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error("Failed to create temporary directory: {0}")]
|
||||
Tempdir(io::Error),
|
||||
/// Guest program directory does not exist.
|
||||
#[error("guest program directory not found: {0}")]
|
||||
PathNotFound(PathBuf),
|
||||
/// Failed to spawn or run `cargo pico build`.
|
||||
#[error("failed to run `cargo pico build`: {0}")]
|
||||
CargoPicoBuild(#[from] io::Error),
|
||||
/// `cargo pico build` exited with a non-zero status.
|
||||
#[error("`cargo pico build` failed with status {status:?}")]
|
||||
CargoPicoBuildFailed { status: ExitStatus },
|
||||
/// Expected ELF file was not produced.
|
||||
#[error("ELF file not found at {0}")]
|
||||
ElfNotFound(PathBuf),
|
||||
/// Reading the ELF file failed.
|
||||
#[error("failed to read ELF file at {path}: {source}")]
|
||||
ReadElf {
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
#[error(transparent)]
|
||||
CompileUtilError(#[from] ere_compile_utils::CompileError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExecuteError {
|
||||
#[error("Pico execution failed: {0}")]
|
||||
Client(anyhow::Error),
|
||||
// Execute
|
||||
#[error("Pico execution panicked: {0}")]
|
||||
Panic(String),
|
||||
}
|
||||
ExecutePanic(String),
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ProveError {
|
||||
// Prove
|
||||
#[error("Pico proving failed: {0}")]
|
||||
Client(anyhow::Error),
|
||||
#[error("Pico proving panicked: {0}")]
|
||||
Panic(String),
|
||||
#[error("Serialising proof with `bincode` failed: {0}")]
|
||||
Bincode(#[from] bincode::error::EncodeError),
|
||||
}
|
||||
Prove(#[source] anyhow::Error),
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum VerifyError {
|
||||
#[error("Pico proving panicked: {0}")]
|
||||
ProvePanic(String),
|
||||
|
||||
// Verify
|
||||
#[error("Pico verifying failed: {0}")]
|
||||
Client(anyhow::Error),
|
||||
#[error("Deserialising proof with `bincode` failed: {0}")]
|
||||
Bincode(#[from] bincode::error::DecodeError),
|
||||
Verify(#[source] anyhow::Error),
|
||||
|
||||
#[error("Invalid base proof length {0}, expected 1")]
|
||||
InvalidBaseProofLength(usize),
|
||||
|
||||
#[error("Invalid public values length {0}, expected at least 32")]
|
||||
InvalidPublicValuesLength(usize),
|
||||
|
||||
#[error("First 32 public values are expected in byte")]
|
||||
InvalidPublicValues,
|
||||
#[error("Public values digest are expected in bytes")]
|
||||
InvalidPublicValuesDigest,
|
||||
|
||||
#[error("Unexpected public value digest - claimed: {claimed:?}, proved: {proved:?}")]
|
||||
UnexpectedPublicValuesDigest { claimed: [u8; 32], proved: [u8; 32] },
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
use crate::{
|
||||
client::{MetaProof, ProverClient},
|
||||
compiler::PicoProgram,
|
||||
error::{ExecuteError, PicoError, ProveError, VerifyError},
|
||||
error::PicoError,
|
||||
};
|
||||
use anyhow::bail;
|
||||
use ere_zkvm_interface::{
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
CommonError, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM,
|
||||
};
|
||||
use pico_p3_field::PrimeField32;
|
||||
use pico_vm::emulator::stdin::EmulatorStdinBuilder;
|
||||
@@ -32,11 +33,11 @@ pub struct ErePico {
|
||||
}
|
||||
|
||||
impl ErePico {
|
||||
pub fn new(program: PicoProgram, resource: ProverResourceType) -> Self {
|
||||
pub fn new(program: PicoProgram, resource: ProverResourceType) -> Result<Self, PicoError> {
|
||||
if !matches!(resource, ProverResourceType::Cpu) {
|
||||
panic!("Network or GPU proving not yet implemented for Pico. Use CPU resource type.");
|
||||
}
|
||||
ErePico { program }
|
||||
Ok(ErePico { program })
|
||||
}
|
||||
|
||||
pub fn client(&self) -> ProverClient {
|
||||
@@ -45,7 +46,7 @@ impl ErePico {
|
||||
}
|
||||
|
||||
impl zkVM for ErePico {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, input: &[u8]) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
|
||||
let mut stdin = EmulatorStdinBuilder::default();
|
||||
stdin.write_slice(input);
|
||||
|
||||
@@ -55,7 +56,7 @@ impl zkVM for ErePico {
|
||||
let result = client.execute(stdin);
|
||||
(result, start.elapsed())
|
||||
})
|
||||
.map_err(|err| PicoError::Execute(ExecuteError::Panic(panic_msg(err))))?;
|
||||
.map_err(|err| PicoError::ExecutePanic(panic_msg(err)))?;
|
||||
|
||||
Ok((
|
||||
public_values,
|
||||
@@ -71,16 +72,12 @@ impl zkVM for ErePico {
|
||||
&self,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<
|
||||
(
|
||||
PublicValues,
|
||||
Proof,
|
||||
ere_zkvm_interface::ProgramProvingReport,
|
||||
),
|
||||
zkVMError,
|
||||
> {
|
||||
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
|
||||
if proof_kind != ProofKind::Compressed {
|
||||
panic!("Only Compressed proof kind is implemented.");
|
||||
bail!(CommonError::unsupported_proof_kind(
|
||||
proof_kind,
|
||||
[ProofKind::Compressed]
|
||||
))
|
||||
}
|
||||
|
||||
let mut stdin = EmulatorStdinBuilder::default();
|
||||
@@ -92,8 +89,8 @@ impl zkVM for ErePico {
|
||||
let result = client.prove(stdin)?;
|
||||
Ok((result, start.elapsed()))
|
||||
})
|
||||
.map_err(|err| PicoError::Prove(ProveError::Panic(panic_msg(err))))?
|
||||
.map_err(|err| PicoError::Prove(ProveError::Client(err)))?;
|
||||
.map_err(|err| PicoError::ProvePanic(panic_msg(err)))?
|
||||
.map_err(PicoError::Prove)?;
|
||||
|
||||
let proof_bytes = bincode::serde::encode_to_vec(
|
||||
&PicoProofWithPublicValues {
|
||||
@@ -102,7 +99,7 @@ impl zkVM for ErePico {
|
||||
},
|
||||
bincode::config::legacy(),
|
||||
)
|
||||
.map_err(|err| PicoError::Prove(ProveError::Bincode(err)))?;
|
||||
.map_err(|err| CommonError::serialize("proof", "bincode", err))?;
|
||||
|
||||
Ok((
|
||||
public_values,
|
||||
@@ -111,27 +108,26 @@ impl zkVM for ErePico {
|
||||
))
|
||||
}
|
||||
|
||||
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
|
||||
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
|
||||
let Proof::Compressed(proof) = proof else {
|
||||
return Err(zkVMError::other(
|
||||
"Only Compressed proof kind is implemented.",
|
||||
));
|
||||
bail!(CommonError::unsupported_proof_kind(
|
||||
proof.kind(),
|
||||
[ProofKind::Compressed]
|
||||
))
|
||||
};
|
||||
|
||||
let client = self.client();
|
||||
|
||||
let (proof, _): (PicoProofWithPublicValues, _) =
|
||||
bincode::serde::decode_from_slice(proof, bincode::config::legacy())
|
||||
.map_err(|err| PicoError::Verify(VerifyError::Bincode(err)))?;
|
||||
.map_err(|err| CommonError::deserialize("proof", "bincode", err))?;
|
||||
|
||||
client
|
||||
.verify(&proof.proof)
|
||||
.map_err(|err| PicoError::Verify(VerifyError::Client(err)))?;
|
||||
client.verify(&proof.proof).map_err(PicoError::Verify)?;
|
||||
|
||||
if extract_public_values_sha256_digest(&proof.proof).map_err(PicoError::Verify)?
|
||||
!= <[u8; 32]>::from(Sha256::digest(&proof.public_values))
|
||||
{
|
||||
return Err(PicoError::Verify(VerifyError::InvalidPublicValuesDigest))?;
|
||||
let claimed = <[u8; 32]>::from(Sha256::digest(&proof.public_values));
|
||||
let proved = extract_public_values_sha256_digest(&proof.proof)?;
|
||||
if claimed != proved {
|
||||
bail!(PicoError::UnexpectedPublicValuesDigest { claimed, proved });
|
||||
}
|
||||
|
||||
Ok(proof.public_values)
|
||||
@@ -149,13 +145,13 @@ impl zkVM for ErePico {
|
||||
/// Extract public values sha256 digest from base proof of compressed proof.
|
||||
/// The sha256 digest will be placed at the first 32 field elements of the
|
||||
/// public values of the only base proof.
|
||||
fn extract_public_values_sha256_digest(proof: &MetaProof) -> Result<[u8; 32], VerifyError> {
|
||||
fn extract_public_values_sha256_digest(proof: &MetaProof) -> Result<[u8; 32], PicoError> {
|
||||
if proof.proofs().len() != 1 {
|
||||
return Err(VerifyError::InvalidBaseProofLength(proof.proofs().len()));
|
||||
return Err(PicoError::InvalidBaseProofLength(proof.proofs().len()));
|
||||
}
|
||||
|
||||
if proof.proofs()[0].public_values.len() < 32 {
|
||||
return Err(VerifyError::InvalidPublicValuesLength(
|
||||
return Err(PicoError::InvalidPublicValuesLength(
|
||||
proof.proofs()[0].public_values.len(),
|
||||
));
|
||||
}
|
||||
@@ -164,7 +160,7 @@ fn extract_public_values_sha256_digest(proof: &MetaProof) -> Result<[u8; 32], Ve
|
||||
.iter()
|
||||
.map(|value| u8::try_from(value.as_canonical_u32()))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|_| VerifyError::InvalidPublicValues)?
|
||||
.map_err(|_| PicoError::InvalidPublicValues)?
|
||||
.try_into()
|
||||
.unwrap())
|
||||
}
|
||||
@@ -203,7 +199,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_execute() {
|
||||
let program = basic_program();
|
||||
let zkvm = ErePico::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = ErePico::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_execute(&zkvm, &test_case);
|
||||
@@ -212,7 +208,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_execute_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = ErePico::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = ErePico::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.execute(&input).unwrap_err();
|
||||
@@ -222,7 +218,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prove() {
|
||||
let program = basic_program();
|
||||
let zkvm = ErePico::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = ErePico::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_prove(&zkvm, &test_case);
|
||||
@@ -231,7 +227,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prove_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = ErePico::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = ErePico::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.prove(&input, ProofKind::default()).unwrap_err();
|
||||
|
||||
@@ -14,6 +14,7 @@ tracing.workspace = true
|
||||
|
||||
# Risc0 dependencies
|
||||
risc0-build = { workspace = true, features = ["unstable"] }
|
||||
risc0-zkp.workspace = true
|
||||
risc0-zkvm = { workspace = true, features = ["client", "unstable"] }
|
||||
risc0-binfmt.workspace = true
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use crate::compiler::Risc0Program;
|
||||
use crate::error::{CompileError, Risc0Error};
|
||||
use crate::{compiler::Risc0Program, error::CompileError};
|
||||
use ere_compile_utils::CargoBuildCmd;
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use risc0_binfmt::ProgramBinary;
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::{env, path::Path};
|
||||
use tracing::info;
|
||||
|
||||
// TODO: Make this with `zkos` package building to avoid binary file storing in repo.
|
||||
@@ -34,7 +32,7 @@ const CARGO_BUILD_OPTIONS: &[&str] = &[
|
||||
pub struct RustRv32ima;
|
||||
|
||||
impl Compiler for RustRv32ima {
|
||||
type Error = Risc0Error;
|
||||
type Error = CompileError;
|
||||
|
||||
type Program = Risc0Program;
|
||||
|
||||
@@ -44,8 +42,7 @@ impl Compiler for RustRv32ima {
|
||||
.toolchain(toolchain)
|
||||
.build_options(CARGO_BUILD_OPTIONS)
|
||||
.rustflags(RUSTFLAGS)
|
||||
.exec(guest_directory, TARGET_TRIPLE)
|
||||
.map_err(CompileError::CompileUtilError)?;
|
||||
.exec(guest_directory, TARGET_TRIPLE)?;
|
||||
|
||||
let program = ProgramBinary::new(elf.as_slice(), V1COMPAT_ELF);
|
||||
let image_id = program
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
compiler::Risc0Program,
|
||||
error::{CompileError, Risc0Error},
|
||||
};
|
||||
use crate::{compiler::Risc0Program, error::CompileError};
|
||||
use ere_compile_utils::cargo_metadata;
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use risc0_build::GuestOptions;
|
||||
@@ -13,14 +10,14 @@ use tracing::info;
|
||||
pub struct RustRv32imaCustomized;
|
||||
|
||||
impl Compiler for RustRv32imaCustomized {
|
||||
type Error = Risc0Error;
|
||||
type Error = CompileError;
|
||||
|
||||
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 metadata = cargo_metadata(guest_directory)?;
|
||||
let package = metadata.root_package().unwrap();
|
||||
|
||||
// Use `risc0_build::build_package` to build package instead of calling
|
||||
@@ -30,9 +27,9 @@ impl Compiler for RustRv32imaCustomized {
|
||||
&metadata.target_directory,
|
||||
GuestOptions::default(),
|
||||
)
|
||||
.map_err(|source| CompileError::BuildFailure {
|
||||
source,
|
||||
crate_path: guest_directory.to_path_buf(),
|
||||
.map_err(|err| CompileError::BuildFailure {
|
||||
err,
|
||||
guest_path: guest_directory.to_path_buf(),
|
||||
})?
|
||||
.into_iter()
|
||||
.next()
|
||||
|
||||
@@ -1,24 +1,47 @@
|
||||
use ere_zkvm_interface::ProofKind;
|
||||
use risc0_zkp::verify::VerificationError;
|
||||
use std::path::PathBuf;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Risc0Error {
|
||||
pub enum CompileError {
|
||||
#[error(transparent)]
|
||||
Compile(#[from] CompileError),
|
||||
CommonError(#[from] ere_compile_utils::CommonError),
|
||||
|
||||
#[error("`risc0_build::build_package` for {guest_path} failed: {err}")]
|
||||
BuildFailure {
|
||||
#[source]
|
||||
err: anyhow::Error,
|
||||
guest_path: PathBuf,
|
||||
},
|
||||
|
||||
#[error("`risc0_build::build_package` succeeded but failed to find guest")]
|
||||
Risc0BuildMissingGuest,
|
||||
|
||||
#[error("ELF binary image calculation failure: {0}")]
|
||||
ImageIDCalculationFailure(anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error("`risc0_build::build_package` for {crate_path} failed: {source}")]
|
||||
BuildFailure {
|
||||
#[source]
|
||||
source: anyhow::Error,
|
||||
crate_path: PathBuf,
|
||||
},
|
||||
#[error("`risc0_build::build_package` succeeded but failed to find guest")]
|
||||
Risc0BuildMissingGuest,
|
||||
#[error("ELF binary image calculation failure : {0}")]
|
||||
ImageIDCalculationFailure(anyhow::Error),
|
||||
#[error(transparent)]
|
||||
CompileUtilError(#[from] ere_compile_utils::CompileError),
|
||||
pub enum Risc0Error {
|
||||
// Execute
|
||||
#[error("Failed to build `ExecutorEnv`: {0}")]
|
||||
BuildExecutorEnv(anyhow::Error),
|
||||
|
||||
#[error("Failed to execute: {0}")]
|
||||
Execute(anyhow::Error),
|
||||
|
||||
// Prove
|
||||
#[error("Failed to initialize cuda prover: {0}")]
|
||||
InitializeCudaProver(anyhow::Error),
|
||||
|
||||
#[error("Failed to prove: {0}")]
|
||||
Prove(anyhow::Error),
|
||||
|
||||
// Verify
|
||||
#[error("Invalid proof kind, expected: {0:?}, got: {1}")]
|
||||
InvalidProofKind(ProofKind, String),
|
||||
|
||||
#[error("Failed to verify: {0}")]
|
||||
Verify(VerificationError),
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
|
||||
use crate::compiler::Risc0Program;
|
||||
use crate::{compiler::Risc0Program, error::Risc0Error};
|
||||
use anyhow::bail;
|
||||
use ere_zkvm_interface::{
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
CommonError, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM,
|
||||
};
|
||||
use risc0_zkvm::{
|
||||
DEFAULT_MAX_PO2, DefaultProver, ExecutorEnv, ExternalProver, InnerReceipt, ProverOpts, Receipt,
|
||||
@@ -50,7 +51,7 @@ pub struct EreRisc0 {
|
||||
}
|
||||
|
||||
impl EreRisc0 {
|
||||
pub fn new(program: Risc0Program, resource: ProverResourceType) -> Result<Self, zkVMError> {
|
||||
pub fn new(program: Risc0Program, resource: ProverResourceType) -> Result<Self, Risc0Error> {
|
||||
if matches!(resource, ProverResourceType::Network(_)) {
|
||||
panic!(
|
||||
"Network proving not yet implemented for RISC Zero. Use CPU or GPU resource type."
|
||||
@@ -82,17 +83,17 @@ impl EreRisc0 {
|
||||
}
|
||||
|
||||
impl zkVM for EreRisc0 {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, input: &[u8]) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
|
||||
let executor = default_executor();
|
||||
let env = ExecutorEnv::builder()
|
||||
.write_slice(input)
|
||||
.build()
|
||||
.map_err(zkVMError::other)?;
|
||||
.map_err(Risc0Error::BuildExecutorEnv)?;
|
||||
|
||||
let start = Instant::now();
|
||||
let session_info = executor
|
||||
.execute(env, &self.program.elf)
|
||||
.map_err(zkVMError::other)?;
|
||||
.map_err(Risc0Error::Execute)?;
|
||||
|
||||
let public_values = session_info.journal.bytes.clone();
|
||||
|
||||
@@ -110,7 +111,7 @@ impl zkVM for EreRisc0 {
|
||||
&self,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
|
||||
let prover = match self.resource {
|
||||
ProverResourceType::Cpu => Rc::new(ExternalProver::new("ipc", "r0vm")),
|
||||
ProverResourceType::Gpu => {
|
||||
@@ -124,7 +125,10 @@ impl zkVM for EreRisc0 {
|
||||
// workers to do multi-gpu proving.
|
||||
// It uses env `RISC0_DEFAULT_PROVER_NUM_GPUS` to determine
|
||||
// how many available GPUs there are.
|
||||
Rc::new(DefaultProver::new("r0vm-cuda").map_err(zkVMError::other)?)
|
||||
Rc::new(
|
||||
DefaultProver::new("r0vm-cuda")
|
||||
.map_err(Risc0Error::InitializeCudaProver)?,
|
||||
)
|
||||
}
|
||||
}
|
||||
ProverResourceType::Network(_) => {
|
||||
@@ -138,25 +142,25 @@ impl zkVM for EreRisc0 {
|
||||
.write_slice(input)
|
||||
.segment_limit_po2(self.segment_po2 as _)
|
||||
.keccak_max_po2(self.keccak_po2 as _)
|
||||
.map_err(zkVMError::other)?
|
||||
.build()
|
||||
.map_err(zkVMError::other)?;
|
||||
.and_then(|builder| builder.build())
|
||||
.map_err(Risc0Error::BuildExecutorEnv)?;
|
||||
|
||||
let opts = match proof_kind {
|
||||
ProofKind::Compressed => ProverOpts::succinct(),
|
||||
ProofKind::Groth16 => ProverOpts::groth16(),
|
||||
};
|
||||
|
||||
let now = std::time::Instant::now();
|
||||
let now = Instant::now();
|
||||
let prove_info = prover
|
||||
.prove_with_opts(env, &self.program.elf, &opts)
|
||||
.map_err(zkVMError::other)?;
|
||||
.map_err(Risc0Error::Prove)?;
|
||||
let proving_time = now.elapsed();
|
||||
|
||||
let public_values = prove_info.receipt.journal.bytes.clone();
|
||||
let proof = Proof::new(
|
||||
proof_kind,
|
||||
borsh::to_vec(&prove_info.receipt).map_err(zkVMError::other)?,
|
||||
borsh::to_vec(&prove_info.receipt)
|
||||
.map_err(|err| CommonError::serialize("proof", "borsh", err))?,
|
||||
);
|
||||
|
||||
Ok((
|
||||
@@ -166,24 +170,30 @@ impl zkVM for EreRisc0 {
|
||||
))
|
||||
}
|
||||
|
||||
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
|
||||
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
|
||||
let proof_kind = proof.kind();
|
||||
|
||||
let receipt: Receipt = borsh::from_slice(proof.as_bytes()).map_err(zkVMError::other)?;
|
||||
let receipt: Receipt = borsh::from_slice(proof.as_bytes())
|
||||
.map_err(|err| CommonError::deserialize("proof", "borsh", err))?;
|
||||
|
||||
if !matches!(
|
||||
(proof_kind, &receipt.inner),
|
||||
(ProofKind::Compressed, InnerReceipt::Succinct(_))
|
||||
| (ProofKind::Groth16, InnerReceipt::Groth16(_))
|
||||
) {
|
||||
return Err(zkVMError::other(format!(
|
||||
"Invalid inner receipt kind, expected {proof_kind:?}",
|
||||
)))?;
|
||||
let got = match &receipt.inner {
|
||||
InnerReceipt::Composite(_) => "Composite",
|
||||
InnerReceipt::Succinct(_) => "Succinct",
|
||||
InnerReceipt::Groth16(_) => "Groth16",
|
||||
InnerReceipt::Fake(_) => "Fake",
|
||||
_ => "Unknown",
|
||||
};
|
||||
bail!(Risc0Error::InvalidProofKind(proof_kind, got.to_string()));
|
||||
}
|
||||
|
||||
receipt
|
||||
.verify(self.program.image_id)
|
||||
.map_err(zkVMError::other)?;
|
||||
.map_err(Risc0Error::Verify)?;
|
||||
|
||||
let public_values = receipt.journal.bytes.clone();
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
bincode = { workspace = true, features = ["alloc", "serde"] }
|
||||
tempfile.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
compiler::SP1Program,
|
||||
error::{CompileError, SP1Error},
|
||||
};
|
||||
use crate::{compiler::SP1Program, error::CompileError};
|
||||
use ere_compile_utils::CargoBuildCmd;
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use std::{env, path::Path};
|
||||
@@ -35,7 +32,7 @@ const CARGO_BUILD_OPTIONS: &[&str] = &[
|
||||
pub struct RustRv32ima;
|
||||
|
||||
impl Compiler for RustRv32ima {
|
||||
type Error = SP1Error;
|
||||
type Error = CompileError;
|
||||
|
||||
type Program = SP1Program;
|
||||
|
||||
@@ -45,8 +42,7 @@ impl Compiler for RustRv32ima {
|
||||
.toolchain(toolchain)
|
||||
.build_options(CARGO_BUILD_OPTIONS)
|
||||
.rustflags(RUSTFLAGS)
|
||||
.exec(guest_directory, TARGET_TRIPLE)
|
||||
.map_err(CompileError::CompileUtilError)?;
|
||||
.exec(guest_directory, TARGET_TRIPLE)?;
|
||||
Ok(elf)
|
||||
}
|
||||
}
|
||||
@@ -68,7 +64,7 @@ mod tests {
|
||||
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);
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
zkvm.execute(&[]).unwrap();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use crate::{
|
||||
compiler::SP1Program,
|
||||
error::{CompileError, SP1Error},
|
||||
};
|
||||
use crate::{compiler::SP1Program, error::CompileError};
|
||||
use ere_compile_utils::{CommonError, cargo_metadata};
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use std::{fs, path::Path, process::Command};
|
||||
use tempfile::tempdir;
|
||||
@@ -12,68 +10,44 @@ use tracing::info;
|
||||
pub struct RustRv32imaCustomized;
|
||||
|
||||
impl Compiler for RustRv32imaCustomized {
|
||||
type Error = SP1Error;
|
||||
type Error = CompileError;
|
||||
|
||||
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(),
|
||||
})?;
|
||||
}
|
||||
cargo_metadata(guest_directory)?;
|
||||
|
||||
// ── build into a temp dir ─────────────────────────────────────────────
|
||||
let temp_output_dir = tempdir().map_err(CompileError::TempDir)?;
|
||||
let temp_output_dir_path = temp_output_dir.path();
|
||||
let output_dir = tempdir().map_err(CommonError::tempdir)?;
|
||||
|
||||
info!(
|
||||
"Running `cargo prove build` → dir: {}",
|
||||
temp_output_dir_path.display(),
|
||||
output_dir.path().display(),
|
||||
);
|
||||
|
||||
let status = Command::new("cargo")
|
||||
let mut cmd = Command::new("cargo");
|
||||
let status = cmd
|
||||
.current_dir(guest_directory)
|
||||
.args([
|
||||
"prove",
|
||||
"build",
|
||||
"--output-directory",
|
||||
temp_output_dir_path.to_str().unwrap(),
|
||||
&output_dir.path().to_string_lossy(),
|
||||
"--elf-name",
|
||||
"guest.elf",
|
||||
])
|
||||
.status()
|
||||
.map_err(|e| CompileError::CargoProveBuild {
|
||||
cwd: guest_directory.to_path_buf(),
|
||||
source: e,
|
||||
})?;
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(CompileError::CargoProveBuildFailed {
|
||||
status,
|
||||
path: guest_directory.to_path_buf(),
|
||||
})?;
|
||||
return Err(CommonError::command_exit_non_zero(&cmd, status, None))?;
|
||||
}
|
||||
|
||||
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,
|
||||
})?;
|
||||
let elf_path = output_dir.path().join("guest.elf");
|
||||
let elf_bytes =
|
||||
fs::read(&elf_path).map_err(|err| CommonError::read_file("elf", &elf_path, err))?;
|
||||
info!("SP1 program compiled OK - {} bytes", elf_bytes.len());
|
||||
|
||||
Ok(elf_bytes)
|
||||
|
||||
@@ -1,92 +1,33 @@
|
||||
use std::{path::PathBuf, process::ExitStatus};
|
||||
|
||||
use ere_zkvm_interface::{ProofKind, zkVMError};
|
||||
use sp1_sdk::SP1ProofMode;
|
||||
use ere_zkvm_interface::ProofKind;
|
||||
use sp1_sdk::{SP1ProofMode, SP1VerificationError};
|
||||
use thiserror::Error;
|
||||
|
||||
impl From<SP1Error> for zkVMError {
|
||||
fn from(value: SP1Error) -> Self {
|
||||
zkVMError::Other(Box::new(value))
|
||||
}
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error(transparent)]
|
||||
CommonError(#[from] ere_compile_utils::CommonError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SP1Error {
|
||||
#[error(transparent)]
|
||||
CompileError(#[from] CompileError),
|
||||
CommonError(#[from] ere_zkvm_interface::CommonError),
|
||||
|
||||
#[error(transparent)]
|
||||
Execute(#[from] ExecuteError),
|
||||
|
||||
#[error(transparent)]
|
||||
Prove(#[from] ProveError),
|
||||
|
||||
#[error(transparent)]
|
||||
Verify(#[from] VerifyError),
|
||||
}
|
||||
|
||||
/// Errors that can be encountered while compiling a SP1 program
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[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("Failed to create temporary output directory: {0}")]
|
||||
TempDir(#[from] std::io::Error),
|
||||
#[error("Could not find `[package].name` in guest Cargo.toml at {path}")]
|
||||
MissingPackageName { path: PathBuf },
|
||||
#[error("Failed to execute `cargo prove build` in {cwd}: {source}")]
|
||||
CargoProveBuild {
|
||||
cwd: PathBuf,
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
},
|
||||
#[error("`cargo prove build` failed with status: {status} for program at {path}")]
|
||||
CargoProveBuildFailed { status: ExitStatus, path: PathBuf },
|
||||
#[error("Compiled ELF not found at expected path: {0}")]
|
||||
ElfNotFound(PathBuf),
|
||||
#[error("Failed to read file at {path}: {source}")]
|
||||
ReadFile {
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
},
|
||||
#[error(transparent)]
|
||||
CompileUtilError(#[from] ere_compile_utils::CompileError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExecuteError {
|
||||
// Execute
|
||||
#[error("SP1 execution failed: {0}")]
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
}
|
||||
Execute(#[source] anyhow::Error),
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ProveError {
|
||||
// Prove
|
||||
#[error("SP1 SDK proving failed: {0}")]
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
Prove(#[source] anyhow::Error),
|
||||
|
||||
#[error("SP1 proving panicked: {0}")]
|
||||
Panic(String),
|
||||
|
||||
#[error("Serialising proof with `bincode` failed: {0}")]
|
||||
Bincode(#[from] bincode::error::EncodeError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum VerifyError {
|
||||
#[error("Deserialising proof failed: {0}")]
|
||||
Bincode(#[from] bincode::error::DecodeError),
|
||||
|
||||
#[error("Invalid proof kind, expected: {}, got: {}", 0.to_string(), 1.to_string() )]
|
||||
// Verify
|
||||
#[error("Invalid proof kind, expected: {0:?}, got: {1:?}")]
|
||||
InvalidProofKind(ProofKind, SP1ProofMode),
|
||||
|
||||
#[error("SP1 SDK verification failed: {0}")]
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
Verify(#[source] SP1VerificationError),
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
|
||||
use crate::{
|
||||
compiler::SP1Program,
|
||||
error::{ExecuteError, ProveError, SP1Error, VerifyError},
|
||||
};
|
||||
use crate::{compiler::SP1Program, error::SP1Error};
|
||||
use anyhow::bail;
|
||||
use ere_zkvm_interface::{
|
||||
NetworkProverConfig, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM, zkVMError,
|
||||
CommonError, NetworkProverConfig, ProgramExecutionReport, ProgramProvingReport, Proof,
|
||||
ProofKind, ProverResourceType, PublicValues, zkVM,
|
||||
};
|
||||
use sp1_sdk::{
|
||||
CpuProver, CudaProver, NetworkProver, Prover, ProverClient, SP1ProofMode,
|
||||
@@ -47,9 +45,7 @@ impl ProverType {
|
||||
ProverType::Network(network_prover) => network_prover.execute(program, input),
|
||||
};
|
||||
|
||||
cpu_executor_builder
|
||||
.run()
|
||||
.map_err(|e| SP1Error::Execute(ExecuteError::Client(e.into())))
|
||||
cpu_executor_builder.run().map_err(SP1Error::Execute)
|
||||
}
|
||||
|
||||
fn prove(
|
||||
@@ -63,7 +59,7 @@ impl ProverType {
|
||||
ProverType::Gpu(cuda_prover) => cuda_prover.prove(pk, input).mode(mode).run(),
|
||||
ProverType::Network(network_prover) => network_prover.prove(pk, input).mode(mode).run(),
|
||||
}
|
||||
.map_err(|e| SP1Error::Prove(ProveError::Client(e.into())))
|
||||
.map_err(SP1Error::Prove)
|
||||
}
|
||||
|
||||
fn verify(
|
||||
@@ -76,7 +72,7 @@ impl ProverType {
|
||||
ProverType::Gpu(cuda_prover) => cuda_prover.verify(proof, vk),
|
||||
ProverType::Network(network_prover) => network_prover.verify(proof, vk),
|
||||
}
|
||||
.map_err(|e| SP1Error::Verify(VerifyError::Client(e.into())))
|
||||
.map_err(SP1Error::Verify)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,20 +127,20 @@ impl EreSP1 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(program: SP1Program, resource: ProverResourceType) -> Self {
|
||||
pub fn new(program: SP1Program, resource: ProverResourceType) -> Result<Self, SP1Error> {
|
||||
let (pk, vk) = Self::create_client(&resource).setup(&program);
|
||||
|
||||
Self {
|
||||
Ok(Self {
|
||||
program,
|
||||
pk,
|
||||
vk,
|
||||
resource,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl zkVM for EreSP1 {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, input: &[u8]) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
|
||||
let mut stdin = SP1Stdin::new();
|
||||
stdin.write_slice(input);
|
||||
|
||||
@@ -166,7 +162,7 @@ impl zkVM for EreSP1 {
|
||||
&self,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
|
||||
info!("Generating proof…");
|
||||
|
||||
let mut stdin = SP1Stdin::new();
|
||||
@@ -179,17 +175,17 @@ impl zkVM for EreSP1 {
|
||||
|
||||
let (proof, proving_time) = panic::catch_unwind(|| {
|
||||
let client = Self::create_client(&self.resource);
|
||||
let start = std::time::Instant::now();
|
||||
let start = Instant::now();
|
||||
let proof = client.prove(&self.pk, &stdin, mode)?;
|
||||
Ok::<_, SP1Error>((proof, start.elapsed()))
|
||||
})
|
||||
.map_err(|err| SP1Error::Prove(ProveError::Panic(panic_msg(err))))??;
|
||||
.map_err(|err| SP1Error::Panic(panic_msg(err)))??;
|
||||
|
||||
let public_values = proof.public_values.to_vec();
|
||||
let proof = Proof::new(
|
||||
proof_kind,
|
||||
bincode::serde::encode_to_vec(&proof, bincode::config::legacy())
|
||||
.map_err(|err| SP1Error::Prove(ProveError::Bincode(err)))?,
|
||||
.map_err(|err| CommonError::serialize("proof", "bincode", err))?,
|
||||
);
|
||||
|
||||
Ok((
|
||||
@@ -199,14 +195,14 @@ impl zkVM for EreSP1 {
|
||||
))
|
||||
}
|
||||
|
||||
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
|
||||
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
|
||||
info!("Verifying proof…");
|
||||
|
||||
let proof_kind = proof.kind();
|
||||
|
||||
let (proof, _): (SP1ProofWithPublicValues, _) =
|
||||
bincode::serde::decode_from_slice(proof.as_bytes(), bincode::config::legacy())
|
||||
.map_err(|err| SP1Error::Verify(VerifyError::Bincode(err)))?;
|
||||
.map_err(|err| CommonError::deserialize("proof", "bincode", err))?;
|
||||
let inner_proof_kind = SP1ProofMode::from(&proof.proof);
|
||||
|
||||
if !matches!(
|
||||
@@ -214,14 +210,11 @@ impl zkVM for EreSP1 {
|
||||
(ProofKind::Compressed, SP1ProofMode::Compressed)
|
||||
| (ProofKind::Groth16, SP1ProofMode::Groth16)
|
||||
) {
|
||||
return Err(SP1Error::Verify(VerifyError::InvalidProofKind(
|
||||
proof_kind,
|
||||
inner_proof_kind,
|
||||
)))?;
|
||||
bail!(SP1Error::InvalidProofKind(proof_kind, inner_proof_kind));
|
||||
}
|
||||
|
||||
let client = Self::create_client(&self.resource);
|
||||
client.verify(&proof, &self.vk).map_err(zkVMError::from)?;
|
||||
client.verify(&proof, &self.vk)?;
|
||||
|
||||
let public_values_bytes = proof.public_values.as_slice().to_vec();
|
||||
|
||||
@@ -267,7 +260,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_execute() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_execute(&zkvm, &test_case);
|
||||
@@ -276,7 +269,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_execute_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.execute(&input).unwrap_err();
|
||||
@@ -286,7 +279,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prove() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_prove(&zkvm, &test_case);
|
||||
@@ -295,7 +288,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prove_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.prove(&input, ProofKind::default()).unwrap_err();
|
||||
@@ -317,7 +310,7 @@ mod tests {
|
||||
api_key: std::env::var("NETWORK_PRIVATE_KEY").ok(),
|
||||
};
|
||||
let program = basic_program();
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Network(network_config));
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Network(network_config)).unwrap();
|
||||
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_prove(&zkvm, &test_case);
|
||||
|
||||
@@ -6,16 +6,17 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
bincode = { workspace = true, features = ["std", "serde"] }
|
||||
thiserror.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
# Ziren dependencies
|
||||
zkm-sdk = { workspace = true }
|
||||
zkm-sdk.workspace = true
|
||||
|
||||
# Local dependencies
|
||||
ere-compile-utils.workspace = true
|
||||
ere-zkvm-interface = { workspace = true }
|
||||
ere-zkvm-interface.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
ere-test-utils = { workspace = true, features = ["host"] }
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
use crate::{
|
||||
compiler::ZirenProgram,
|
||||
error::{CompileError, ZirenError},
|
||||
};
|
||||
use ere_compile_utils::cargo_metadata;
|
||||
use crate::{compiler::ZirenProgram, error::CompileError};
|
||||
use ere_compile_utils::{CommonError, cargo_metadata, rustc_path};
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
use std::{fs, path::Path, process::Command};
|
||||
|
||||
const ZKM_TOOLCHAIN: &str = "zkm";
|
||||
|
||||
@@ -17,68 +10,42 @@ const ZKM_TOOLCHAIN: &str = "zkm";
|
||||
pub struct RustMips32r2Customized;
|
||||
|
||||
impl Compiler for RustMips32r2Customized {
|
||||
type Error = ZirenError;
|
||||
type Error = CompileError;
|
||||
|
||||
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 metadata = cargo_metadata(guest_directory)?;
|
||||
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")
|
||||
let mut cmd = Command::new("cargo");
|
||||
let output = cmd
|
||||
.current_dir(guest_directory)
|
||||
.env("RUSTC", rustc)
|
||||
.env("RUSTC", rustc_path(ZKM_TOOLCHAIN)?)
|
||||
.env("ZIREN_ZKM_CC", "mipsel-zkm-zkvm-elf-gcc")
|
||||
.args(["ziren", "build"])
|
||||
.output()
|
||||
.map_err(CompileError::CargoZirenBuildFailed)?;
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
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(),
|
||||
})?;
|
||||
return Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
))?;
|
||||
}
|
||||
|
||||
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,
|
||||
})?;
|
||||
let elf_path = metadata
|
||||
.target_directory
|
||||
.join("elf-compilation")
|
||||
.join("mipsel-zkm-zkvm-elf")
|
||||
.join("release")
|
||||
.join(&package.name);
|
||||
let elf =
|
||||
fs::read(&elf_path).map_err(|err| CommonError::read_file("elf", &elf_path, err))?;
|
||||
|
||||
Ok(elf)
|
||||
}
|
||||
|
||||
@@ -1,89 +1,30 @@
|
||||
use ere_zkvm_interface::{ProofKind, zkVMError};
|
||||
use std::{io, path::PathBuf, process::ExitStatus};
|
||||
use ere_zkvm_interface::ProofKind;
|
||||
use thiserror::Error;
|
||||
use zkm_sdk::ZKMProofKind;
|
||||
use zkm_sdk::{ZKMProofKind, ZKMVerificationError};
|
||||
|
||||
impl From<ZirenError> for zkVMError {
|
||||
fn from(value: ZirenError) -> Self {
|
||||
zkVMError::Other(Box::new(value))
|
||||
}
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error(transparent)]
|
||||
CommonError(#[from] ere_compile_utils::CommonError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ZirenError {
|
||||
#[error(transparent)]
|
||||
Compile(#[from] CompileError),
|
||||
|
||||
#[error(transparent)]
|
||||
Execute(#[from] ExecuteError),
|
||||
|
||||
#[error(transparent)]
|
||||
Prove(#[from] ProveError),
|
||||
|
||||
#[error(transparent)]
|
||||
Verify(#[from] VerifyError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error("`RUSTUP_TOOLCHAIN=zkm rustc --print sysroot` failed to execute: {0}")]
|
||||
RustcSysrootFailed(#[source] io::Error),
|
||||
#[error(
|
||||
"`RUSTUP_TOOLCHAIN=zkm rustc --print sysroot` exited with non-zero status {status}, stdout: {stdout}, stderr: {stderr}"
|
||||
)]
|
||||
RustcSysrootExitNonZero {
|
||||
status: ExitStatus,
|
||||
stdout: String,
|
||||
stderr: String,
|
||||
},
|
||||
#[error("`cargo ziren build` failed to execute: {0}")]
|
||||
CargoZirenBuildFailed(#[source] io::Error),
|
||||
#[error(
|
||||
"`cargo ziren build` exited with non-zero status {status}, stdout: {stdout}, stderr: {stderr}"
|
||||
)]
|
||||
CargoZirenBuildExitNonZero {
|
||||
status: ExitStatus,
|
||||
stdout: String,
|
||||
stderr: String,
|
||||
},
|
||||
#[error("Failed to find guest in built packages")]
|
||||
GuestNotFound { name: String },
|
||||
#[error("Failed to read file at {path}: {source}")]
|
||||
ReadFile {
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
},
|
||||
#[error(transparent)]
|
||||
CompileUtilError(#[from] ere_compile_utils::CompileError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExecuteError {
|
||||
// Execute
|
||||
#[error("Ziren execution failed: {0}")]
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ProveError {
|
||||
#[error("Serialising proof with `bincode` failed: {0}")]
|
||||
Bincode(#[from] bincode::error::EncodeError),
|
||||
Execute(#[source] anyhow::Error),
|
||||
|
||||
// Prove
|
||||
#[error("Ziren proving failed: {0}")]
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
Prove(#[source] anyhow::Error),
|
||||
|
||||
#[error("Ziren proving panicked: {0}")]
|
||||
Panic(String),
|
||||
}
|
||||
ProvePanic(String),
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum VerifyError {
|
||||
#[error("Deserialising proof with `bincode` failed: {0}")]
|
||||
Bincode(#[from] bincode::error::DecodeError),
|
||||
|
||||
#[error("Invalid proof kind, expected: {}, got: {}", 0.to_string(), 1.to_string() )]
|
||||
// Verify
|
||||
#[error("Invalid proof kind, expected: {0:?}, got: {1:?}")]
|
||||
InvalidProofKind(ProofKind, ZKMProofKind),
|
||||
|
||||
#[error("Ziren verification failed: {0}")]
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
Verify(#[source] ZKMVerificationError),
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
|
||||
use crate::{
|
||||
compiler::ZirenProgram,
|
||||
error::{ExecuteError, ProveError, VerifyError, ZirenError},
|
||||
};
|
||||
use crate::{compiler::ZirenProgram, error::ZirenError};
|
||||
use anyhow::bail;
|
||||
use ere_zkvm_interface::{
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
CommonError, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM,
|
||||
};
|
||||
use std::{panic, time::Instant};
|
||||
use tracing::info;
|
||||
@@ -27,7 +25,7 @@ pub struct EreZiren {
|
||||
}
|
||||
|
||||
impl EreZiren {
|
||||
pub fn new(program: ZirenProgram, resource: ProverResourceType) -> Self {
|
||||
pub fn new(program: ZirenProgram, resource: ProverResourceType) -> Result<Self, ZirenError> {
|
||||
if matches!(
|
||||
resource,
|
||||
ProverResourceType::Gpu | ProverResourceType::Network(_)
|
||||
@@ -35,19 +33,19 @@ impl EreZiren {
|
||||
panic!("Network or Gpu proving not yet implemented for ZKM. Use CPU resource type.");
|
||||
}
|
||||
let (pk, vk) = CpuProver::new().setup(&program);
|
||||
Self { program, pk, vk }
|
||||
Ok(Self { program, pk, vk })
|
||||
}
|
||||
}
|
||||
|
||||
impl zkVM for EreZiren {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, input: &[u8]) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
|
||||
let mut stdin = ZKMStdin::new();
|
||||
stdin.write_slice(input);
|
||||
|
||||
let start = Instant::now();
|
||||
let (public_inputs, exec_report) = CpuProver::new()
|
||||
.execute(&self.program, &stdin)
|
||||
.map_err(|err| ZirenError::Execute(ExecuteError::Client(err.into())))?;
|
||||
.map_err(ZirenError::Execute)?;
|
||||
let execution_duration = start.elapsed();
|
||||
|
||||
Ok((
|
||||
@@ -64,7 +62,7 @@ impl zkVM for EreZiren {
|
||||
&self,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
|
||||
info!("Generating proof…");
|
||||
|
||||
let mut stdin = ZKMStdin::new();
|
||||
@@ -78,15 +76,15 @@ impl zkVM for EreZiren {
|
||||
let start = std::time::Instant::now();
|
||||
let proof =
|
||||
panic::catch_unwind(|| CpuProver::new().prove(&self.pk, stdin, inner_proof_kind))
|
||||
.map_err(|err| ZirenError::Prove(ProveError::Panic(panic_msg(err))))?
|
||||
.map_err(|err| ZirenError::Prove(ProveError::Client(err.into())))?;
|
||||
.map_err(|err| ZirenError::ProvePanic(panic_msg(err)))?
|
||||
.map_err(ZirenError::Prove)?;
|
||||
let proving_time = start.elapsed();
|
||||
|
||||
let public_values = proof.public_values.to_vec();
|
||||
let proof = Proof::new(
|
||||
proof_kind,
|
||||
bincode::serde::encode_to_vec(&proof, bincode::config::legacy())
|
||||
.map_err(|err| ZirenError::Prove(ProveError::Bincode(err)))?,
|
||||
.map_err(|err| CommonError::serialize("proof", "bincode", err))?,
|
||||
);
|
||||
|
||||
Ok((
|
||||
@@ -96,14 +94,14 @@ impl zkVM for EreZiren {
|
||||
))
|
||||
}
|
||||
|
||||
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
|
||||
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
|
||||
info!("Verifying proof…");
|
||||
|
||||
let proof_kind = proof.kind();
|
||||
|
||||
let (proof, _): (ZKMProofWithPublicValues, _) =
|
||||
bincode::serde::decode_from_slice(proof.as_bytes(), bincode::config::legacy())
|
||||
.map_err(|err| ZirenError::Verify(VerifyError::Bincode(err)))?;
|
||||
.map_err(|err| CommonError::deserialize("proof", "bincode", err))?;
|
||||
let inner_proof_kind = ZKMProofKind::from(&proof.proof);
|
||||
|
||||
if !matches!(
|
||||
@@ -111,15 +109,12 @@ impl zkVM for EreZiren {
|
||||
(ProofKind::Compressed, ZKMProofKind::Compressed)
|
||||
| (ProofKind::Groth16, ZKMProofKind::Groth16)
|
||||
) {
|
||||
return Err(ZirenError::Verify(VerifyError::InvalidProofKind(
|
||||
proof_kind,
|
||||
inner_proof_kind,
|
||||
)))?;
|
||||
bail!(ZirenError::InvalidProofKind(proof_kind, inner_proof_kind));
|
||||
}
|
||||
|
||||
CpuProver::new()
|
||||
.verify(&proof, &self.vk)
|
||||
.map_err(|err| ZirenError::Verify(VerifyError::Client(err.into())))?;
|
||||
.map_err(ZirenError::Verify)?;
|
||||
|
||||
Ok(proof.public_values.to_vec())
|
||||
}
|
||||
@@ -163,7 +158,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_execute() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreZiren::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreZiren::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_execute(&zkvm, &test_case);
|
||||
@@ -172,7 +167,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_execute_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreZiren::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreZiren::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.execute(&input).unwrap_err();
|
||||
@@ -182,7 +177,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prove() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreZiren::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreZiren::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_prove(&zkvm, &test_case);
|
||||
@@ -191,7 +186,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_prove_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreZiren::new(program, ProverResourceType::Cpu);
|
||||
let zkvm = EreZiren::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.prove(&input, ProofKind::default()).unwrap_err();
|
||||
|
||||
@@ -6,7 +6,7 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bincode = { workspace = true, features = ["alloc", "serde"] }
|
||||
anyhow.workspace = true
|
||||
blake3.workspace = true
|
||||
bytemuck.workspace = true
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use crate::error::ZiskError;
|
||||
use ere_zkvm_interface::{ProverResourceType, PublicValues};
|
||||
use ere_zkvm_interface::{CommonError, ProverResourceType, PublicValues};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
env, fs,
|
||||
io::BufRead,
|
||||
iter,
|
||||
path::{Path, PathBuf},
|
||||
process::{Child, Command, Stdio},
|
||||
process::{Child, Command},
|
||||
sync::OnceLock,
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
@@ -151,17 +151,15 @@ impl ZiskSdk {
|
||||
// Save ELF to `~/.zisk/cache` along with the ROM binaries, to avoid it
|
||||
// been cleaned up during a long run process.
|
||||
let cache_dir_path = dot_zisk_dir_path().join("cache");
|
||||
fs::create_dir_all(&cache_dir_path)?;
|
||||
fs::create_dir_all(&cache_dir_path)
|
||||
.map_err(|err| CommonError::create_dir("cache", &cache_dir_path, err))?;
|
||||
|
||||
// Use blake3 hash as the ELF file name, since ROM setup uses blake3 as
|
||||
// unique identifier as well.
|
||||
let elf_hash = blake3::hash(&elf);
|
||||
let elf_path = cache_dir_path.join(format!("{elf_hash}.elf"));
|
||||
|
||||
fs::write(&elf_path, elf).map_err(|source| ZiskError::WriteFile {
|
||||
path: elf_path.clone(),
|
||||
source,
|
||||
})?;
|
||||
fs::write(&elf_path, elf).map_err(|err| CommonError::write_file("elf", &elf_path, err))?;
|
||||
|
||||
Ok(Self {
|
||||
elf_path,
|
||||
@@ -173,16 +171,15 @@ impl ZiskSdk {
|
||||
|
||||
/// Execute the ELF with the given `input`.
|
||||
pub fn execute(&self, input: &[u8]) -> Result<(PublicValues, u64), ZiskError> {
|
||||
let tempdir = tempdir().map_err(ZiskError::TempDir)?;
|
||||
let tempdir = tempdir().map_err(CommonError::tempdir)?;
|
||||
let input_path = tempdir.path().join("input");
|
||||
let output_path = tempdir.path().join("output");
|
||||
|
||||
fs::write(&input_path, input).map_err(|source| ZiskError::WriteFile {
|
||||
path: input_path.clone(),
|
||||
source,
|
||||
})?;
|
||||
fs::write(&input_path, input)
|
||||
.map_err(|err| CommonError::write_file("input", &input_path, err))?;
|
||||
|
||||
let output = Command::new("ziskemu")
|
||||
let mut cmd = Command::new("ziskemu");
|
||||
let output = cmd
|
||||
.arg("--elf")
|
||||
.arg(&self.elf_path)
|
||||
.arg("--inputs")
|
||||
@@ -190,14 +187,15 @@ impl ZiskSdk {
|
||||
.arg("--output")
|
||||
.arg(&output_path)
|
||||
.arg("--stats") // Enable stats in order to get total steps.
|
||||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
.map_err(ZiskError::Ziskemu)?;
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(ZiskError::ZiskemuFailed {
|
||||
status: output.status,
|
||||
});
|
||||
return Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
))?;
|
||||
}
|
||||
|
||||
// Extract cycle count from the stdout.
|
||||
@@ -212,10 +210,8 @@ impl ZiskSdk {
|
||||
})
|
||||
.ok_or(ZiskError::TotalStepsNotFound)?;
|
||||
|
||||
let public_values = fs::read(&output_path).map_err(|source| ZiskError::ReadFile {
|
||||
path: output_path,
|
||||
source,
|
||||
})?;
|
||||
let public_values = fs::read(&output_path)
|
||||
.map_err(|err| CommonError::read_file("output", &output_path, err))?;
|
||||
|
||||
Ok((public_values, total_num_cycles))
|
||||
}
|
||||
@@ -269,7 +265,7 @@ impl ZiskSdk {
|
||||
cmd.arg("--witness-lib").arg(witness_lib_path);
|
||||
}
|
||||
|
||||
let child = cmd.spawn().map_err(ZiskError::CargoZiskServer)?;
|
||||
let child = cmd.spawn().map_err(|err| CommonError::command(&cmd, err))?;
|
||||
let server = ZiskServer {
|
||||
options: self.options.clone(),
|
||||
rom_digest,
|
||||
@@ -285,20 +281,19 @@ impl ZiskSdk {
|
||||
pub fn verify(&self, proof: &[u8]) -> Result<PublicValues, ZiskError> {
|
||||
let rom_digest = self.rom_digest()?;
|
||||
|
||||
let tempdir = tempdir().map_err(ZiskError::TempDir)?;
|
||||
let tempdir = tempdir().map_err(CommonError::tempdir)?;
|
||||
let proof_path = tempdir.path().join("proof");
|
||||
|
||||
fs::write(&proof_path, proof).map_err(|source| ZiskError::WriteFile {
|
||||
path: proof_path.clone(),
|
||||
source,
|
||||
})?;
|
||||
fs::write(&proof_path, proof)
|
||||
.map_err(|err| CommonError::write_file("proof", &proof_path, err))?;
|
||||
|
||||
let output = Command::new("cargo-zisk")
|
||||
let mut cmd = Command::new("cargo-zisk");
|
||||
let output = cmd
|
||||
.arg("verify")
|
||||
.arg("--proof")
|
||||
.arg(&proof_path)
|
||||
.output()
|
||||
.map_err(ZiskError::CargoZiskVerify)?;
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !output.status.success() {
|
||||
Err(ZiskError::InvalidProof(
|
||||
@@ -306,10 +301,8 @@ impl ZiskSdk {
|
||||
))?
|
||||
}
|
||||
|
||||
let proof = fs::read(&proof_path).map_err(|source| ZiskError::ReadFile {
|
||||
path: proof_path,
|
||||
source,
|
||||
})?;
|
||||
let proof = fs::read(&proof_path)
|
||||
.map_err(|err| CommonError::read_file("proof", &proof_path, err))?;
|
||||
|
||||
// Deserialize public values.
|
||||
let (proved_rom_digest, public_values) = deserialize_public_values(&proof)?;
|
||||
@@ -356,25 +349,30 @@ impl Drop for ZiskServer {
|
||||
impl ZiskServer {
|
||||
/// Get status of server.
|
||||
pub fn status(&self) -> Result<ZiskServerStatus, ZiskError> {
|
||||
let output = Command::new("cargo-zisk")
|
||||
let mut cmd = Command::new("cargo-zisk");
|
||||
let output = cmd
|
||||
.args(["prove-client", "status"])
|
||||
.args(self.options.prove_client_args())
|
||||
.output()
|
||||
.map_err(ZiskError::CargoZiskStatus)?;
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(ZiskError::CargoZiskStatusFailed {
|
||||
status: output.status,
|
||||
});
|
||||
return Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
))?;
|
||||
}
|
||||
|
||||
let output = String::from_utf8_lossy(&output.stdout);
|
||||
if output.contains("idle") {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
if stdout.contains("idle") {
|
||||
Ok(ZiskServerStatus::Idle)
|
||||
} else if output.contains("working") {
|
||||
} else if stdout.contains("working") {
|
||||
Ok(ZiskServerStatus::Working)
|
||||
} else {
|
||||
Err(ZiskError::UnknownServerStatus)
|
||||
Err(ZiskError::UnknownServerStatus {
|
||||
stdout: stdout.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,19 +383,18 @@ impl ZiskServer {
|
||||
// so there will be no conflict.
|
||||
const PREFIX: &str = "ere";
|
||||
|
||||
let tempdir = tempdir().map_err(ZiskError::TempDir)?;
|
||||
let tempdir = tempdir().map_err(CommonError::tempdir)?;
|
||||
let input_path = tempdir.path().join("input");
|
||||
let output_path = tempdir.path().join("output");
|
||||
let proof_path = output_path.join(format!("{PREFIX}-vadcop_final_proof.bin"));
|
||||
|
||||
fs::write(&input_path, input).map_err(|source| ZiskError::WriteFile {
|
||||
path: input_path.clone(),
|
||||
source,
|
||||
})?;
|
||||
fs::write(&input_path, input)
|
||||
.map_err(|err| CommonError::write_file("input", &input_path, err))?;
|
||||
|
||||
// NOTE: Use snake case for `prove-client` command
|
||||
// Issue for tracking: https://github.com/eth-act/ere/issues/151.
|
||||
let status = Command::new("cargo-zisk")
|
||||
let mut cmd = Command::new("cargo-zisk");
|
||||
let output = cmd
|
||||
.args(["prove-client", "prove"])
|
||||
.arg("--input")
|
||||
.arg(input_path)
|
||||
@@ -406,11 +403,15 @@ impl ZiskServer {
|
||||
.args(["-p", PREFIX])
|
||||
.args(["--aggregation", "--verify_proofs"])
|
||||
.args(self.options.prove_args())
|
||||
.status()
|
||||
.map_err(ZiskError::CargoZiskProve)?;
|
||||
.output()
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(ZiskError::CargoZiskProveFailed { status });
|
||||
if !output.status.success() {
|
||||
return Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
))?;
|
||||
}
|
||||
|
||||
// ZisK server will finish the `prove` requested above then respond the
|
||||
@@ -418,10 +419,8 @@ impl ZiskServer {
|
||||
// should also be ready.
|
||||
self.status()?;
|
||||
|
||||
let proof = fs::read(&proof_path).map_err(|source| ZiskError::ReadFile {
|
||||
path: proof_path,
|
||||
source,
|
||||
})?;
|
||||
let proof = fs::read(&proof_path)
|
||||
.map_err(|err| CommonError::read_file("proof", &proof_path, err))?;
|
||||
|
||||
// Deserialize public values.
|
||||
let (proved_rom_digest, public_values) = deserialize_public_values(&proof)?;
|
||||
@@ -458,13 +457,18 @@ impl ZiskServer {
|
||||
fn check_setup() -> Result<(), ZiskError> {
|
||||
info!("Running command `cargo-zisk check-setup --aggregation`...");
|
||||
|
||||
let status = Command::new("cargo-zisk")
|
||||
let mut cmd = Command::new("cargo-zisk");
|
||||
let output = cmd
|
||||
.args(["check-setup", "--aggregation"])
|
||||
.status()
|
||||
.map_err(ZiskError::CargoZiskCheckSetup)?;
|
||||
.output()
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(ZiskError::CargoZiskCheckSetupFailed { status });
|
||||
if !output.status.success() {
|
||||
Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
))?;
|
||||
}
|
||||
|
||||
info!("Command `cargo-zisk check-setup --aggregation` succeeded");
|
||||
@@ -476,18 +480,20 @@ fn check_setup() -> Result<(), ZiskError> {
|
||||
fn rom_setup(elf_path: &Path) -> Result<RomDigest, ZiskError> {
|
||||
info!("Running command `cargo-zisk rom-setup` ...");
|
||||
|
||||
let output = Command::new("cargo-zisk")
|
||||
let mut cmd = Command::new("cargo-zisk");
|
||||
let output = cmd
|
||||
.arg("rom-setup")
|
||||
.arg("--elf")
|
||||
.arg(elf_path)
|
||||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
.map_err(ZiskError::CargoZiskRomSetup)?;
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(ZiskError::CargoZiskRomSetupFailed {
|
||||
status: output.status,
|
||||
});
|
||||
Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
))?;
|
||||
}
|
||||
|
||||
// Parse the ROM digest from the stdout.
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
use crate::error::ZiskError;
|
||||
use ere_compile_utils::cargo_metadata;
|
||||
use crate::error::CompileError;
|
||||
use ere_compile_utils::{CommonError, cargo_metadata, rustc_path};
|
||||
use ere_zkvm_interface::Compiler;
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
use std::{fs, path::Path, process::Command};
|
||||
use tracing::info;
|
||||
|
||||
const ZISK_TOOLCHAIN: &str = "zisk";
|
||||
@@ -16,7 +12,7 @@ const ZISK_TARGET: &str = "riscv64ima-zisk-zkvm-elf";
|
||||
pub struct RustRv64imaCustomized;
|
||||
|
||||
impl Compiler for RustRv64imaCustomized {
|
||||
type Error = ZiskError;
|
||||
type Error = CompileError;
|
||||
|
||||
type Program = Vec<u8>;
|
||||
|
||||
@@ -24,68 +20,31 @@ impl Compiler for RustRv64imaCustomized {
|
||||
info!("Compiling ZisK program at {}", guest_directory.display());
|
||||
|
||||
let metadata = cargo_metadata(guest_directory)?;
|
||||
let package_name = &metadata.root_package().unwrap().name;
|
||||
let package = metadata.root_package().unwrap();
|
||||
|
||||
info!("Parsed program name: {package_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])
|
||||
let mut cmd = Command::new("cargo");
|
||||
let status = cmd
|
||||
.env("RUSTC", rustc_path(ZISK_TOOLCHAIN)?)
|
||||
.args(["build", "--release"])
|
||||
.args(["--target", ZISK_TARGET])
|
||||
.arg("--manifest-path")
|
||||
.arg(&package.manifest_path)
|
||||
.status()
|
||||
.map_err(|e| ZiskError::CargoBuild {
|
||||
cwd: guest_directory.to_path_buf(),
|
||||
source: e,
|
||||
})?;
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(ZiskError::CargoBuildFailed {
|
||||
status,
|
||||
path: guest_directory.to_path_buf(),
|
||||
});
|
||||
return Err(CommonError::command_exit_non_zero(&cmd, status, None))?;
|
||||
}
|
||||
|
||||
// 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")
|
||||
let elf_path = metadata
|
||||
.target_directory
|
||||
.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,
|
||||
})?;
|
||||
.join(&package.name);
|
||||
let elf_bytes =
|
||||
fs::read(&elf_path).map_err(|err| CommonError::read_file("elf", elf_path, err))?;
|
||||
|
||||
Ok(elf_bytes)
|
||||
}
|
||||
|
||||
@@ -1,112 +1,52 @@
|
||||
use crate::client::RomDigest;
|
||||
use bytemuck::PodCastError;
|
||||
use ere_zkvm_interface::zkVMError;
|
||||
use std::{io, path::PathBuf, process::ExitStatus};
|
||||
use thiserror::Error;
|
||||
|
||||
impl From<ZiskError> for zkVMError {
|
||||
fn from(value: ZiskError) -> Self {
|
||||
zkVMError::Other(Box::new(value))
|
||||
}
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CompileError {
|
||||
#[error(transparent)]
|
||||
CommonError(#[from] ere_compile_utils::CommonError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ZiskError {
|
||||
// IO and file system
|
||||
#[error("IO failure: {0}")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("IO failure in temporary directory: {0}")]
|
||||
TempDir(io::Error),
|
||||
#[error("Failed to read file at {path}: {source}")]
|
||||
ReadFile {
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
#[error("Failed to write file at {path}: {source}")]
|
||||
WriteFile {
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
|
||||
// Compilation
|
||||
#[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`")]
|
||||
CargoLocateProject(#[source] io::Error),
|
||||
#[error("Failed to execute `RUSTC=$ZISK_RUSTC cargo build --release ...` in {cwd}: {source}")]
|
||||
CargoBuild {
|
||||
cwd: PathBuf,
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
#[error(
|
||||
"`RUSTC=$ZISK_RUSTC cargo build --release ...` failed with status: {status} for program at {path}"
|
||||
)]
|
||||
CargoBuildFailed { status: ExitStatus, path: PathBuf },
|
||||
#[error(transparent)]
|
||||
CompileUtilError(#[from] ere_compile_utils::CompileError),
|
||||
|
||||
// Serialization
|
||||
#[error("Bincode encode failed: {0}")]
|
||||
BincodeEncode(#[from] bincode::error::EncodeError),
|
||||
#[error("Bincode decode failed: {0}")]
|
||||
BincodeDecode(#[from] bincode::error::DecodeError),
|
||||
CommonError(#[from] ere_zkvm_interface::CommonError),
|
||||
|
||||
// Execution
|
||||
#[error("Failed to execute `ziskemu`: {0}")]
|
||||
Ziskemu(#[source] io::Error),
|
||||
#[error("`ziskemu` failed with status: {status}")]
|
||||
ZiskemuFailed { status: ExitStatus },
|
||||
#[error("Total steps not found in execution report")]
|
||||
TotalStepsNotFound,
|
||||
|
||||
// Check setup
|
||||
#[error("Failed to execute `cargo-zisk check-setup`: {0}")]
|
||||
CargoZiskCheckSetup(#[source] io::Error),
|
||||
#[error("`cargo-zisk check-setup` failed with status: {status}")]
|
||||
CargoZiskCheckSetupFailed { status: ExitStatus },
|
||||
|
||||
// Rom setup
|
||||
#[error("Failed to execute `cargo-zisk rom-setup`: {0}")]
|
||||
CargoZiskRomSetup(#[source] io::Error),
|
||||
#[error("`cargo-zisk rom-setup` failed with status: {status}")]
|
||||
CargoZiskRomSetupFailed { status: ExitStatus },
|
||||
#[error("Failed to find ROM digest in output")]
|
||||
RomDigestNotFound,
|
||||
|
||||
#[error("`cargo-zisk rom-setup` failed in another thread")]
|
||||
RomSetupFailedBefore,
|
||||
|
||||
// Prove
|
||||
#[error("Mutex of ZiskServer is poisoned")]
|
||||
MutexPoisoned,
|
||||
#[error("Failed to execute `cargo-zisk server`: {0}")]
|
||||
CargoZiskServer(#[source] io::Error),
|
||||
|
||||
#[error("Timeout waiting for server ready")]
|
||||
TimeoutWaitingServerReady,
|
||||
#[error("Failed to execute `cargo-zisk prove-client status`: {0}")]
|
||||
CargoZiskStatus(#[source] io::Error),
|
||||
#[error("`cargo-zisk prove-client status` failed with status: {status}")]
|
||||
CargoZiskStatusFailed { status: ExitStatus },
|
||||
#[error("Uknown server status")]
|
||||
UnknownServerStatus,
|
||||
#[error("Failed to execute `cargo-zisk prove-client prove`: {0}")]
|
||||
CargoZiskProve(#[source] io::Error),
|
||||
#[error("`cargo-zisk prove-client prove` failed with status: {status}")]
|
||||
CargoZiskProveFailed { status: ExitStatus },
|
||||
|
||||
#[error("Uknown server status, stdout: {stdout}")]
|
||||
UnknownServerStatus { stdout: String },
|
||||
|
||||
// Verify
|
||||
#[error("Failed to execute `cargo-zisk verify`: {0}")]
|
||||
CargoZiskVerify(#[source] io::Error),
|
||||
#[error("Invalid proof: {0}")]
|
||||
InvalidProof(String),
|
||||
|
||||
#[error("Cast proof to `u64` slice failed: {0}")]
|
||||
CastProofBytesToU64s(PodCastError),
|
||||
|
||||
#[error("Invalid public value format")]
|
||||
InvalidPublicValue,
|
||||
|
||||
#[error("Public values length {0}, but expected at least 6")]
|
||||
InvalidPublicValuesLength(usize),
|
||||
|
||||
#[error("Unexpected ROM digest - preprocessed: {preprocessed:?}, proved: {proved:?}")]
|
||||
UnexpectedRomDigest {
|
||||
preprocessed: RomDigest,
|
||||
|
||||
@@ -5,9 +5,10 @@ use crate::{
|
||||
compiler::ZiskProgram,
|
||||
error::ZiskError,
|
||||
};
|
||||
use anyhow::bail;
|
||||
use ere_zkvm_interface::{
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
CommonError, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM,
|
||||
};
|
||||
use std::{
|
||||
sync::{Mutex, MutexGuard},
|
||||
@@ -30,7 +31,7 @@ pub struct EreZisk {
|
||||
}
|
||||
|
||||
impl EreZisk {
|
||||
pub fn new(elf: ZiskProgram, resource: ProverResourceType) -> Result<Self, zkVMError> {
|
||||
pub fn new(elf: ZiskProgram, resource: ProverResourceType) -> Result<Self, ZiskError> {
|
||||
if matches!(resource, ProverResourceType::Network(_)) {
|
||||
panic!("Network proving not yet implemented for ZisK. Use CPU or GPU resource type.");
|
||||
}
|
||||
@@ -63,7 +64,7 @@ impl EreZisk {
|
||||
}
|
||||
|
||||
impl zkVM for EreZisk {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, input: &[u8]) -> anyhow::Result<(PublicValues, ProgramExecutionReport)> {
|
||||
let start = Instant::now();
|
||||
let (public_values, total_num_cycles) = self.sdk.execute(input)?;
|
||||
let execution_duration = start.elapsed();
|
||||
@@ -82,9 +83,12 @@ impl zkVM for EreZisk {
|
||||
&self,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
) -> anyhow::Result<(PublicValues, Proof, ProgramProvingReport)> {
|
||||
if proof_kind != ProofKind::Compressed {
|
||||
panic!("Only Compressed proof kind is supported.");
|
||||
bail!(CommonError::unsupported_proof_kind(
|
||||
proof_kind,
|
||||
[ProofKind::Compressed]
|
||||
))
|
||||
}
|
||||
|
||||
let mut server = self.server()?;
|
||||
@@ -101,9 +105,12 @@ impl zkVM for EreZisk {
|
||||
))
|
||||
}
|
||||
|
||||
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
|
||||
fn verify(&self, proof: &Proof) -> anyhow::Result<PublicValues> {
|
||||
let Proof::Compressed(proof) = proof else {
|
||||
return Err(zkVMError::other("Only Compressed proof kind is supported."));
|
||||
bail!(CommonError::unsupported_proof_kind(
|
||||
proof.kind(),
|
||||
[ProofKind::Compressed]
|
||||
))
|
||||
};
|
||||
|
||||
Ok(self.sdk.verify(proof)?)
|
||||
|
||||
Reference in New Issue
Block a user