mirror of
https://github.com/eth-act/ere.git
synced 2026-02-19 11:54:42 -05:00
feat: Add ere-zisk with only compile and execute utility. (#27)
* feat: add `ere-zisk` and `compile` functionality * fix: use `FixintEncoding` for `Input::as_bytes` to make it deterministic * feat: implement `zkVM::execute` * fix: make `install_zisk_sdk.sh` work with docker * chore: add comment why use `#[should_panic]` * feat: use command `cargo-zisk ...` for `compile`, `execute`, `prove` and `verify` * ci * fix: invalid proof
This commit is contained in:
58
.github/workflows/check_zisk_image.yml
vendored
58
.github/workflows/check_zisk_image.yml
vendored
@@ -1,36 +1,36 @@
|
||||
name: Check ZisK Docker Image
|
||||
|
||||
# on:
|
||||
# push:
|
||||
# branches:
|
||||
# - master
|
||||
# pull_request:
|
||||
# branches:
|
||||
# - master
|
||||
# workflow_dispatch:
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
# jobs:
|
||||
# build_zisk_image:
|
||||
# name: Build ZisK Docker Image
|
||||
# runs-on: ubuntu-latest
|
||||
jobs:
|
||||
build_zisk_image:
|
||||
name: Build ZisK Docker Image
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# steps:
|
||||
# - name: Checkout repository
|
||||
# uses: actions/checkout@v4
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# - name: Set up Docker Buildx
|
||||
# uses: docker/setup-buildx-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# - name: Build dependent Docker base image
|
||||
# run: |
|
||||
# docker build \
|
||||
# --file docker/base/Dockerfile.base \
|
||||
# --tag ere-base:latest \
|
||||
# .
|
||||
- name: Build dependent Docker base image
|
||||
run: |
|
||||
docker build \
|
||||
--file docker/base/Dockerfile.base \
|
||||
--tag ere-base:latest \
|
||||
.
|
||||
|
||||
# - name: Build ZisK Docker image
|
||||
# run: |
|
||||
# docker build \
|
||||
# --file docker/zisk/Dockerfile \
|
||||
# --tag ere-builder-zisk-check:latest \
|
||||
# .
|
||||
- name: Build ZisK Docker image
|
||||
run: |
|
||||
docker build \
|
||||
--file docker/zisk/Dockerfile \
|
||||
--tag ere-builder-zisk-check:latest \
|
||||
.
|
||||
21
Cargo.lock
generated
21
Cargo.lock
generated
@@ -1193,7 +1193,7 @@ dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools 0.10.5",
|
||||
"itertools 0.11.0",
|
||||
"log",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
@@ -2630,6 +2630,19 @@ dependencies = [
|
||||
"zkvm-interface",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ere-zisk"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"serde",
|
||||
"tempfile",
|
||||
"thiserror 2.0.12",
|
||||
"toml",
|
||||
"tracing",
|
||||
"zkvm-interface",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.11"
|
||||
@@ -2637,7 +2650,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7142,7 +7155,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.10.5",
|
||||
"itertools 0.11.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
@@ -7235,7 +7248,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"socket2",
|
||||
"tracing",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -8,6 +8,7 @@ members = [
|
||||
"crates/ere-openvm",
|
||||
"crates/ere-pico",
|
||||
"crates/ere-jolt",
|
||||
"crates/ere-zisk",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
|
||||
19
crates/ere-zisk/Cargo.toml
Normal file
19
crates/ere-zisk/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "ere-zisk"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
zkvm-interface = { workspace = true }
|
||||
thiserror = "2"
|
||||
toml = "0.8"
|
||||
tracing = "0.1"
|
||||
tempfile = "3.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
bincode = "1.3"
|
||||
|
||||
[lib]
|
||||
name = "ere_zisk"
|
||||
path = "src/lib.rs"
|
||||
158
crates/ere-zisk/src/compile.rs
Normal file
158
crates/ere-zisk/src/compile.rs
Normal file
@@ -0,0 +1,158 @@
|
||||
use crate::error::CompileError;
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
use toml::Value as TomlValue;
|
||||
use tracing::info;
|
||||
|
||||
/// Compile the guest crate and return raw ELF bytes.
|
||||
pub fn compile_zisk_program(program_crate_path: &Path) -> Result<PathBuf, CompileError> {
|
||||
info!("Compiling ZisK program at {}", program_crate_path.display());
|
||||
|
||||
if !program_crate_path.exists() || !program_crate_path.is_dir() {
|
||||
return Err(CompileError::InvalidProgramPath(
|
||||
program_crate_path.to_path_buf(),
|
||||
));
|
||||
}
|
||||
|
||||
let guest_manifest_path = program_crate_path.join("Cargo.toml");
|
||||
if !guest_manifest_path.exists() {
|
||||
return Err(CompileError::CargoTomlMissing {
|
||||
program_dir: program_crate_path.to_path_buf(),
|
||||
manifest_path: guest_manifest_path.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
// ── read + parse Cargo.toml ───────────────────────────────────────────
|
||||
let manifest_content =
|
||||
fs::read_to_string(&guest_manifest_path).map_err(|e| CompileError::ReadFile {
|
||||
path: guest_manifest_path.clone(),
|
||||
source: e,
|
||||
})?;
|
||||
|
||||
let manifest_toml: TomlValue =
|
||||
manifest_content
|
||||
.parse::<TomlValue>()
|
||||
.map_err(|e| CompileError::ParseCargoToml {
|
||||
path: guest_manifest_path.clone(),
|
||||
source: e,
|
||||
})?;
|
||||
|
||||
let program_name = manifest_toml
|
||||
.get("package")
|
||||
.and_then(|p| p.get("name"))
|
||||
.and_then(|n| n.as_str())
|
||||
.ok_or_else(|| CompileError::MissingPackageName {
|
||||
path: guest_manifest_path.clone(),
|
||||
})?;
|
||||
|
||||
info!("Parsed program name: {program_name}");
|
||||
|
||||
// ── build ─────────────────────────────────────────────────────────────
|
||||
let status = Command::new("cargo-zisk")
|
||||
.current_dir(program_crate_path)
|
||||
.args(["build", "--release"])
|
||||
.status()
|
||||
.map_err(|e| CompileError::CargoZiskBuild {
|
||||
cwd: program_crate_path.to_path_buf(),
|
||||
source: e,
|
||||
})?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(CompileError::CargoZiskBuildFailed {
|
||||
status,
|
||||
path: program_crate_path.to_path_buf(),
|
||||
});
|
||||
}
|
||||
|
||||
let elf_path = program_crate_path
|
||||
.join("target")
|
||||
.join("riscv64ima-zisk-zkvm-elf")
|
||||
.join("release")
|
||||
.join(program_name);
|
||||
let elf_path = elf_path
|
||||
.canonicalize()
|
||||
.map_err(|e| CompileError::ElfNotFound {
|
||||
path: elf_path,
|
||||
source: e,
|
||||
})?;
|
||||
|
||||
// FIXME: This currently uses global build directory `${HOME}/.zisk/zisk/emulator-asm`
|
||||
// which causes `compile_zisk_program` to panic if ran in parallel.
|
||||
// We should create a temporary directory and copy only necessary
|
||||
// data to setup each ELF.
|
||||
let status = Command::new("cargo-zisk")
|
||||
.current_dir(program_crate_path)
|
||||
.arg("rom-setup")
|
||||
.arg("-e")
|
||||
.arg(&elf_path)
|
||||
.status()
|
||||
.map_err(|e| CompileError::CargoZiskRomSetup { source: e })?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(CompileError::CargoZiskRomSetupFailed {
|
||||
status,
|
||||
path: program_crate_path.to_path_buf(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(elf_path)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use zkvm_interface::Compiler;
|
||||
|
||||
use crate::RV64_IMA_ZISK_ZKVM_ELF;
|
||||
|
||||
use super::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
// TODO: for now, we just get one test file
|
||||
// TODO: but this should get the whole directory and compile each test
|
||||
fn get_compile_test_guest_program_path() -> PathBuf {
|
||||
let workspace_dir = env!("CARGO_WORKSPACE_DIR");
|
||||
PathBuf::from(workspace_dir)
|
||||
.join("tests")
|
||||
.join("zisk")
|
||||
.join("compile")
|
||||
.join("basic")
|
||||
.canonicalize()
|
||||
.expect("Failed to find or canonicalize test guest program at <CARGO_WORKSPACE_DIR>/tests/compile/zisk")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compile_zisk_program() {
|
||||
let test_guest_path = get_compile_test_guest_program_path();
|
||||
|
||||
match compile_zisk_program(&test_guest_path) {
|
||||
Ok(elf_path) => {
|
||||
assert!(
|
||||
fs::metadata(elf_path).unwrap().len() != 0,
|
||||
"ELF bytes should not be empty."
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("compile failed for dedicated guest: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compile_trait() {
|
||||
let test_guest_path = get_compile_test_guest_program_path();
|
||||
match RV64_IMA_ZISK_ZKVM_ELF::compile(&test_guest_path) {
|
||||
Ok(elf_path) => {
|
||||
assert!(
|
||||
fs::metadata(elf_path).unwrap().len() != 0,
|
||||
"ELF bytes should not be empty."
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("compile_zisk_program direct call failed for dedicated guest: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
121
crates/ere-zisk/src/error.rs
Normal file
121
crates/ere-zisk/src/error.rs
Normal file
@@ -0,0 +1,121 @@
|
||||
use std::{io, path::PathBuf, process::ExitStatus};
|
||||
use thiserror::Error;
|
||||
use zkvm_interface::zkVMError;
|
||||
|
||||
impl From<ZiskError> for zkVMError {
|
||||
fn from(value: ZiskError) -> Self {
|
||||
zkVMError::Other(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ZiskError {
|
||||
#[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("Program path does not exist or is not a directory: {0}")]
|
||||
InvalidProgramPath(PathBuf),
|
||||
#[error(
|
||||
"Cargo.toml not found in program directory: {program_dir}. Expected at: {manifest_path}"
|
||||
)]
|
||||
CargoTomlMissing {
|
||||
program_dir: PathBuf,
|
||||
manifest_path: PathBuf,
|
||||
},
|
||||
#[error("Could not find `[package].name` in guest Cargo.toml at {path}")]
|
||||
MissingPackageName { path: PathBuf },
|
||||
#[error("Compiled ELF not found at expected path: {path}")]
|
||||
ElfNotFound {
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
#[error("Failed to read file at {path}: {source}")]
|
||||
ReadFile {
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
#[error("Failed to parse guest Cargo.toml at {path}: {source}")]
|
||||
ParseCargoToml {
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: toml::de::Error,
|
||||
},
|
||||
#[error("Failed to execute `cargo-zisk build --release` in {cwd}: {source}")]
|
||||
CargoZiskBuild {
|
||||
cwd: PathBuf,
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
#[error("`cargo-zisk build --release` failed with status: {status} for program at {path}")]
|
||||
CargoZiskBuildFailed { status: ExitStatus, path: PathBuf },
|
||||
#[error("Failed to execute `cargo-zisk rom-setup`: {source}")]
|
||||
CargoZiskRomSetup {
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
#[error("`cargo-zisk rom-setup` failed with status: {status} for program at {path}")]
|
||||
CargoZiskRomSetupFailed { status: ExitStatus, path: PathBuf },
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExecuteError {
|
||||
#[error("IO failure: {0}")]
|
||||
Io(io::Error),
|
||||
#[error("Failed to serialize input: {0}")]
|
||||
SerializeInput(Box<dyn std::error::Error + Send + Sync>),
|
||||
#[error("Failed to execute `ziskemu`: {source}")]
|
||||
Ziskemu {
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
#[error("`ziskemu` failed with status: {status}")]
|
||||
ZiskemuFailed { status: ExitStatus },
|
||||
#[error("Total steps not found in report")]
|
||||
TotalStepsNotFound,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ProveError {
|
||||
#[error("IO failure: {0}")]
|
||||
Io(io::Error),
|
||||
#[error("Failed to serialize input: {0}")]
|
||||
SerializeInput(Box<dyn std::error::Error + Send + Sync>),
|
||||
#[error("Failed to execute `cargo prove`: {source}")]
|
||||
CargoZiskProve {
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
#[error("`cargo prove` failed with status: {status}")]
|
||||
CargoZiskProveFailed { status: ExitStatus },
|
||||
#[error("Serialising proof with `bincode` failed: {0}")]
|
||||
Bincode(#[from] bincode::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum VerifyError {
|
||||
#[error("IO failure: {0}")]
|
||||
Io(io::Error),
|
||||
#[error("Deserialising proof with `bincode` failed: {0}")]
|
||||
Bincode(#[from] bincode::Error),
|
||||
#[error("Failed to execute `cargo-zisk verify`: {source}")]
|
||||
CargoZiskVerify {
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
#[error("Invalid proof: {0}")]
|
||||
InvalidProof(String),
|
||||
}
|
||||
339
crates/ere-zisk/src/lib.rs
Normal file
339
crates/ere-zisk/src/lib.rs
Normal file
@@ -0,0 +1,339 @@
|
||||
use crate::{
|
||||
compile::compile_zisk_program,
|
||||
error::{ExecuteError, ProveError, VerifyError, ZiskError},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fs,
|
||||
io::Write,
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, Stdio},
|
||||
time,
|
||||
};
|
||||
use tempfile::tempdir;
|
||||
use zkvm_interface::{
|
||||
Compiler, Input, ProgramExecutionReport, ProgramProvingReport, ProverResourceType, zkVM,
|
||||
zkVMError,
|
||||
};
|
||||
|
||||
mod compile;
|
||||
mod error;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct RV64_IMA_ZISK_ZKVM_ELF;
|
||||
|
||||
impl Compiler for RV64_IMA_ZISK_ZKVM_ELF {
|
||||
type Error = ZiskError;
|
||||
|
||||
/// Path to compiled ELF.
|
||||
type Program = PathBuf;
|
||||
|
||||
fn compile(path_to_program: &Path) -> Result<Self::Program, Self::Error> {
|
||||
compile_zisk_program(path_to_program).map_err(ZiskError::Compile)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ZiskProofWithPublicValues {
|
||||
/// The raw aggregated proof generated by the ZisK zkVM.
|
||||
pub proof: Vec<u8>,
|
||||
/// The public values generated by the ZisK zkVM.
|
||||
pub public_values: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct EreZisk {
|
||||
elf_path: PathBuf,
|
||||
resource: ProverResourceType,
|
||||
}
|
||||
|
||||
impl EreZisk {
|
||||
pub fn new(elf_path: PathBuf, resource: ProverResourceType) -> Self {
|
||||
Self { elf_path, resource }
|
||||
}
|
||||
}
|
||||
|
||||
impl EreZisk {}
|
||||
|
||||
impl zkVM for EreZisk {
|
||||
fn execute(&self, input: &Input) -> Result<ProgramExecutionReport, zkVMError> {
|
||||
let input_bytes = input
|
||||
.iter()
|
||||
.try_fold(Vec::new(), |mut acc, item| {
|
||||
acc.extend(item.as_bytes().map_err(ExecuteError::SerializeInput)?);
|
||||
Ok(acc)
|
||||
})
|
||||
.map_err(ZiskError::Execute)?;
|
||||
|
||||
let output_dir = tempdir().map_err(|e| ZiskError::Execute(ExecuteError::Io(e)))?;
|
||||
let input_path = input_path(output_dir.path());
|
||||
|
||||
fs::File::create(&input_path)
|
||||
.and_then(|mut file| file.write_all(&input_bytes))
|
||||
.map_err(|e| ZiskError::Execute(ExecuteError::Io(e)))?;
|
||||
|
||||
let output = Command::new("ziskemu")
|
||||
.arg("-e")
|
||||
.arg(&self.elf_path)
|
||||
.arg("-i")
|
||||
.arg(&input_path)
|
||||
.arg("-x")
|
||||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
.map_err(|e| ZiskError::Execute(ExecuteError::Ziskemu { source: e }))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(ZiskError::Execute(ExecuteError::ZiskemuFailed {
|
||||
status: output.status,
|
||||
})
|
||||
.into());
|
||||
}
|
||||
|
||||
let total_num_cycles = String::from_utf8_lossy(&output.stdout)
|
||||
.split_once("total steps = ")
|
||||
.and_then(|(_, stats)| {
|
||||
stats
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.and_then(|steps| steps.parse::<u64>().ok())
|
||||
})
|
||||
.ok_or(ZiskError::Execute(ExecuteError::TotalStepsNotFound))?;
|
||||
|
||||
Ok(ProgramExecutionReport::new(total_num_cycles))
|
||||
}
|
||||
|
||||
fn prove(&self, input: &Input) -> Result<(Vec<u8>, ProgramProvingReport), zkVMError> {
|
||||
let input_bytes = input
|
||||
.iter()
|
||||
.try_fold(Vec::new(), |mut acc, item| {
|
||||
acc.extend(item.as_bytes().map_err(ProveError::SerializeInput)?);
|
||||
Ok(acc)
|
||||
})
|
||||
.map_err(ZiskError::Prove)?;
|
||||
|
||||
let output_dir = tempdir().map_err(|e| ZiskError::Prove(ProveError::Io(e)))?;
|
||||
let input_path = input_path(output_dir.path());
|
||||
let proof_path = proof_path(output_dir.path());
|
||||
let public_values_path = public_values_path(output_dir.path());
|
||||
|
||||
fs::File::create(&input_path)
|
||||
.and_then(|mut file| file.write_all(&input_bytes))
|
||||
.map_err(|e| ZiskError::Prove(ProveError::Io(e)))?;
|
||||
|
||||
let start = time::Instant::now();
|
||||
match self.resource {
|
||||
ProverResourceType::Cpu => {
|
||||
let status = Command::new("cargo-zisk")
|
||||
.arg("prove")
|
||||
.arg("-e")
|
||||
.arg(&self.elf_path)
|
||||
.arg("-i")
|
||||
.arg(&input_path)
|
||||
.arg("-o")
|
||||
.arg(output_dir.path())
|
||||
// FIXME: Not sure why if we don't set the flag `aggregation`
|
||||
// there will be no proof, but ideally we should
|
||||
// only generate the core proof as other zkVMs.
|
||||
.arg("-a")
|
||||
.status()
|
||||
.map_err(|e| ZiskError::Prove(ProveError::CargoZiskProve { source: e }))?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(
|
||||
ZiskError::Prove(ProveError::CargoZiskProveFailed { status }).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
ProverResourceType::Gpu => {
|
||||
// TODO: Need to install another version of `cargo-zisk` with
|
||||
// `features = gpu` and call it here.
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
let proving_time = start.elapsed();
|
||||
|
||||
let proof_with_public_values = ZiskProofWithPublicValues {
|
||||
proof: fs::read(proof_path).map_err(|e| ZiskError::Prove(ProveError::Io(e)))?,
|
||||
public_values: fs::read(public_values_path)
|
||||
.map_err(|e| ZiskError::Prove(ProveError::Io(e)))?,
|
||||
};
|
||||
let bytes = bincode::serialize(&proof_with_public_values)
|
||||
.map_err(|err| ZiskError::Prove(ProveError::Bincode(err)))?;
|
||||
|
||||
Ok((bytes, ProgramProvingReport::new(proving_time)))
|
||||
}
|
||||
|
||||
fn verify(&self, bytes: &[u8]) -> Result<(), zkVMError> {
|
||||
let proof_with_public_values: ZiskProofWithPublicValues = bincode::deserialize(bytes)
|
||||
.map_err(|err| ZiskError::Verify(VerifyError::Bincode(err)))?;
|
||||
|
||||
let output_dir = tempdir().map_err(|e| ZiskError::Verify(VerifyError::Io(e)))?;
|
||||
let proof_path = proof_path(output_dir.path());
|
||||
let public_values_path = public_values_path(output_dir.path());
|
||||
fs::create_dir_all(proof_dir(output_dir.path()))
|
||||
.map_err(|e| ZiskError::Verify(VerifyError::Io(e)))?;
|
||||
|
||||
fs::File::create(&proof_path)
|
||||
.and_then(|mut file| file.write_all(&proof_with_public_values.proof))
|
||||
.map_err(|e| ZiskError::Verify(VerifyError::Io(e)))?;
|
||||
fs::File::create(&public_values_path)
|
||||
.and_then(|mut file| file.write_all(&proof_with_public_values.public_values))
|
||||
.map_err(|e| ZiskError::Verify(VerifyError::Io(e)))?;
|
||||
|
||||
let output = Command::new("cargo-zisk")
|
||||
.arg("verify")
|
||||
.arg("-p")
|
||||
.arg(&proof_path)
|
||||
.arg("-u")
|
||||
.arg(&public_values_path)
|
||||
.output()
|
||||
.map_err(|e| ZiskError::Verify(VerifyError::CargoZiskVerify { source: e }))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(ZiskError::Verify(VerifyError::InvalidProof(
|
||||
String::from_utf8_lossy(&output.stderr).to_string(),
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn input_path(dir: impl AsRef<Path>) -> PathBuf {
|
||||
dir.as_ref().join("input.bin")
|
||||
}
|
||||
|
||||
fn proof_dir(dir: impl AsRef<Path>) -> PathBuf {
|
||||
dir.as_ref().join("proofs")
|
||||
}
|
||||
|
||||
fn proof_path(dir: impl AsRef<Path>) -> PathBuf {
|
||||
proof_dir(dir).join("vadcop_final_proof.json")
|
||||
}
|
||||
|
||||
fn public_values_path(dir: impl AsRef<Path>) -> PathBuf {
|
||||
dir.as_ref().join("publics.json")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod execute_tests {
|
||||
use super::*;
|
||||
|
||||
fn get_compiled_test_zisk_elf() -> Result<PathBuf, ZiskError> {
|
||||
let test_guest_path = get_execute_test_guest_program_path();
|
||||
RV64_IMA_ZISK_ZKVM_ELF::compile(&test_guest_path)
|
||||
}
|
||||
|
||||
fn get_execute_test_guest_program_path() -> PathBuf {
|
||||
let workspace_dir = env!("CARGO_WORKSPACE_DIR");
|
||||
PathBuf::from(workspace_dir)
|
||||
.join("tests")
|
||||
.join("zisk")
|
||||
.join("execute")
|
||||
.join("basic")
|
||||
.canonicalize()
|
||||
.expect("Failed to find or canonicalize test guest program at <CARGO_WORKSPACE_DIR>/tests/execute/zisk")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_execute_zisk_dummy_input() {
|
||||
let elf_path = get_compiled_test_zisk_elf()
|
||||
.expect("Failed to compile test ZisK guest for execution test");
|
||||
|
||||
let mut input_builder = Input::new();
|
||||
let n: u32 = 42;
|
||||
let a: u16 = 42;
|
||||
input_builder.write(n);
|
||||
input_builder.write(a);
|
||||
|
||||
let zkvm = EreZisk::new(elf_path, ProverResourceType::Cpu);
|
||||
|
||||
let result = zkvm.execute(&input_builder);
|
||||
|
||||
if let Err(e) = &result {
|
||||
panic!("Execution error: {e:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_execute_zisk_no_input_for_guest_expecting_input() {
|
||||
let elf_path = get_compiled_test_zisk_elf()
|
||||
.expect("Failed to compile test ZisK guest for execution test");
|
||||
|
||||
let empty_input = Input::new();
|
||||
|
||||
let zkvm = EreZisk::new(elf_path, ProverResourceType::Cpu);
|
||||
assert!(zkvm.execute(&empty_input).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod prove_tests {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::*;
|
||||
use zkvm_interface::Input;
|
||||
|
||||
fn get_prove_test_guest_program_path() -> PathBuf {
|
||||
let workspace_dir = env!("CARGO_WORKSPACE_DIR");
|
||||
PathBuf::from(workspace_dir)
|
||||
.join("tests")
|
||||
.join("zisk")
|
||||
.join("prove")
|
||||
.join("basic")
|
||||
.canonicalize()
|
||||
.expect("Failed to find or canonicalize test guest program at <CARGO_WORKSPACE_DIR>/tests/execute/zisk")
|
||||
}
|
||||
|
||||
fn get_compiled_test_zisk_elf_for_prove() -> Result<PathBuf, ZiskError> {
|
||||
let test_guest_path = get_prove_test_guest_program_path();
|
||||
RV64_IMA_ZISK_ZKVM_ELF::compile(&test_guest_path)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prove_zisk_dummy_input() {
|
||||
let elf_path = get_compiled_test_zisk_elf_for_prove()
|
||||
.expect("Failed to compile test ZisK guest for proving test");
|
||||
|
||||
let mut input_builder = Input::new();
|
||||
let n: u32 = 42;
|
||||
let a: u16 = 42;
|
||||
input_builder.write(n);
|
||||
input_builder.write(a);
|
||||
|
||||
let zkvm = EreZisk::new(elf_path, ProverResourceType::Cpu);
|
||||
|
||||
let proof_bytes = match zkvm.prove(&input_builder) {
|
||||
Ok((prove_result, _)) => prove_result,
|
||||
Err(err) => {
|
||||
panic!("Proving error in test: {err:?}");
|
||||
}
|
||||
};
|
||||
|
||||
assert!(!proof_bytes.is_empty(), "Proof bytes should not be empty.");
|
||||
|
||||
assert!(zkvm.verify(&proof_bytes).is_ok());
|
||||
|
||||
let invalid_proof_bytes = {
|
||||
let mut invalid_proof: ZiskProofWithPublicValues =
|
||||
bincode::deserialize(&proof_bytes).unwrap();
|
||||
// alter the first digit of `evals[0][0]`
|
||||
invalid_proof.proof[40] = invalid_proof.proof[40].overflowing_add(1).0;
|
||||
bincode::serialize(&invalid_proof).unwrap()
|
||||
};
|
||||
assert!(zkvm.verify(&invalid_proof_bytes).is_err());
|
||||
|
||||
// TODO: Check public inputs
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prove_zisk_fails_on_bad_input_causing_execution_failure() {
|
||||
let elf_path = get_compiled_test_zisk_elf_for_prove()
|
||||
.expect("Failed to compile test ZisK guest for proving test");
|
||||
|
||||
let empty_input = Input::new();
|
||||
|
||||
let zkvm = EreZisk::new(elf_path, ProverResourceType::Cpu);
|
||||
assert!(zkvm.prove(&empty_input).is_err());
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
use bincode::Options;
|
||||
use erased_serde::Serialize as ErasedSerialize;
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -69,12 +70,14 @@ impl InputItem {
|
||||
}
|
||||
|
||||
/// Get the item as bytes (serialize objects, return bytes directly)
|
||||
pub fn as_bytes(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
||||
pub fn as_bytes(&self) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
match self {
|
||||
InputItem::Object(obj) => {
|
||||
let mut buf = Vec::new();
|
||||
let mut serializer =
|
||||
bincode::Serializer::new(&mut buf, bincode::DefaultOptions::new());
|
||||
let mut serializer = bincode::Serializer::new(
|
||||
&mut buf,
|
||||
bincode::DefaultOptions::new().with_fixint_encoding(),
|
||||
);
|
||||
erased_serde::serialize(obj.as_ref(), &mut serializer)?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
@@ -40,13 +40,13 @@ RUN chmod +x /tmp/install_zisk_sdk.sh
|
||||
RUN /tmp/install_zisk_sdk.sh && rm /tmp/install_zisk_sdk.sh # Clean up the script
|
||||
|
||||
# The 'zisk' Rust toolchain is now installed.
|
||||
# cargo-zisk is installed in $HOME/.zisk/bin.
|
||||
# The install_zisk_sdk.sh script adds $HOME/.zisk/bin to PATH for its session.
|
||||
# For the image environment, we need to ensure $HOME/.zisk/bin is persistently in PATH.
|
||||
ENV ZISK_TOOLS_BIN_DIR="${HOME}/.zisk/bin"
|
||||
ENV PATH="${ZISK_TOOLS_BIN_DIR}:${PATH}"
|
||||
# cargo-zisk is installed in /root/.zisk/bin.
|
||||
# The ziskup script adds /root/.zisk/bin to PATH for its session.
|
||||
# For the image environment, we need to ensure /root/.zisk/bin is persistently in PATH.
|
||||
ENV ZISK_BIN_DIR="/root/.zisk/bin"
|
||||
ENV PATH="${PATH}:${ZISK_BIN_DIR}"
|
||||
|
||||
# Verify cargo-zisk is accessible
|
||||
RUN echo "Verifying Zisk installation in Dockerfile ..." && cargo +zisk zisk --version
|
||||
RUN echo "Verifying Zisk installation in Dockerfile ..." && cargo-zisk --version
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
@@ -31,32 +31,14 @@ ensure_tool_installed "rustup" "for managing Rust toolchains (ZisK installs its
|
||||
ensure_tool_installed "cargo" "as cargo-zisk is a cargo subcommand"
|
||||
|
||||
# Step 1: Download and run the script that installs the ziskup binary itself.
|
||||
ZISKUP_INSTALLER_DOWNLOAD_PATH="/tmp/ziskup_install.sh"
|
||||
echo "Downloading the script that installs the 'ziskup' binary to ${ZISKUP_INSTALLER_DOWNLOAD_PATH}..."
|
||||
curl -L https://raw.githubusercontent.com/0xPolygonHermez/zisk/main/ziskup/install.sh -o "${ZISKUP_INSTALLER_DOWNLOAD_PATH}"
|
||||
chmod +x "${ZISKUP_INSTALLER_DOWNLOAD_PATH}"
|
||||
|
||||
echo "Running the 'ziskup' binary installer script..."
|
||||
"${ZISKUP_INSTALLER_DOWNLOAD_PATH}" # This script installs $HOME/.zisk/bin/ziskup
|
||||
rm "${ZISKUP_INSTALLER_DOWNLOAD_PATH}" # Clean up the downloaded installer script
|
||||
|
||||
# Step 2: Ensure the installed ziskup binary is in PATH for this script session.
|
||||
ZISK_BIN_DIR="${HOME}/.zisk/bin"
|
||||
if [ -d "${ZISK_BIN_DIR}" ] && [[ ":$PATH:" != *":${ZISK_BIN_DIR}:"* ]]; then
|
||||
echo "Adding ${ZISK_BIN_DIR} to PATH for current script session."
|
||||
export PATH="${ZISK_BIN_DIR}:$PATH"
|
||||
fi
|
||||
|
||||
# Ensure the ziskup binary itself is now installed and executable
|
||||
ensure_tool_installed "ziskup" "ZisK SDK manager tool"
|
||||
|
||||
# Step 3: Now run the installed ziskup binary with non-interactive flags.
|
||||
echo "Running 'ziskup --provingkey' to install ZisK components..."
|
||||
# Export GH_RUNNER=true to ensure ziskup uses default non-interactive options.
|
||||
export GH_RUNNER=true
|
||||
ziskup --provingkey # This is the actual ziskup tool that processes --provingkey
|
||||
curl https://raw.githubusercontent.com/0xPolygonHermez/zisk/main/ziskup/install.sh | bash
|
||||
unset GH_RUNNER
|
||||
|
||||
# Step 2: Ensure the installed cargo-zisk binary is in PATH for this script session.
|
||||
export PATH="${PATH}:${HOME}/.zisk/bin"
|
||||
|
||||
# Verify ZisK installation
|
||||
echo "Verifying ZisK installation..."
|
||||
|
||||
@@ -76,16 +58,16 @@ else
|
||||
fi
|
||||
|
||||
echo "Checking for cargo-zisk CLI tool (using +zisk toolchain)..."
|
||||
# cargo-zisk should be callable via `cargo +zisk zisk ...`
|
||||
if cargo +zisk zisk --version; then
|
||||
# cargo-zisk should be callable via `cargo-zisk ...`
|
||||
if cargo-zisk --version; then
|
||||
echo "cargo-zisk CLI tool verified successfully."
|
||||
else
|
||||
echo "Error: 'cargo +zisk zisk --version' failed." >&2
|
||||
echo "Error: 'cargo-zisk --version' failed." >&2
|
||||
echo " Attempting verification with cargo-zisk directly (if in PATH from ${ZISK_BIN_DIR})..."
|
||||
if command -v cargo-zisk &> /dev/null && cargo-zisk --version; then
|
||||
echo "cargo-zisk found directly in PATH and verified."
|
||||
else
|
||||
echo "Error: cargo-zisk also not found directly or 'cargo +zisk zisk --version' failed." >&2
|
||||
echo "Error: cargo-zisk also not found directly or 'cargo-zisk --version' failed." >&2
|
||||
echo " Ensure ${ZISK_BIN_DIR} is effectively in PATH for new shells and check ziskup output." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
9
tests/zisk/compile/basic/Cargo.toml
Normal file
9
tests/zisk/compile/basic/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "ere-test-zisk-guest"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
ziskos = { git = "https://github.com/0xPolygonHermez/zisk.git", rev = "f9a3655" }
|
||||
14
tests/zisk/compile/basic/src/main.rs
Normal file
14
tests/zisk/compile/basic/src/main.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
#![no_main]
|
||||
|
||||
ziskos::entrypoint!(main);
|
||||
|
||||
fn main() {
|
||||
// Read an input
|
||||
let n = u32::from_le_bytes(
|
||||
ziskos::read_input()
|
||||
.try_into()
|
||||
.expect("input to be 4 bytes"),
|
||||
);
|
||||
// Write n*2 to output
|
||||
ziskos::set_output(0, n * 2);
|
||||
}
|
||||
9
tests/zisk/execute/basic/Cargo.toml
Normal file
9
tests/zisk/execute/basic/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "ere-test-zisk-guest"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
ziskos = { git = "https://github.com/0xPolygonHermez/zisk.git", rev = "f9a3655" }
|
||||
16
tests/zisk/execute/basic/src/main.rs
Normal file
16
tests/zisk/execute/basic/src/main.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
#![no_main]
|
||||
|
||||
ziskos::entrypoint!(main);
|
||||
|
||||
fn main() {
|
||||
let input = ziskos::read_input();
|
||||
if input.len() != 6 {
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
// Read an input
|
||||
let n = u32::from_le_bytes(input[..4].try_into().unwrap());
|
||||
let a = u16::from_le_bytes(input[4..6].try_into().unwrap()) as u32;
|
||||
|
||||
ziskos::set_output(0, (n + a) * 2);
|
||||
}
|
||||
9
tests/zisk/prove/basic/Cargo.toml
Normal file
9
tests/zisk/prove/basic/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "ere-test-zisk-guest"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
ziskos = { git = "https://github.com/0xPolygonHermez/zisk.git", rev = "f9a3655" }
|
||||
16
tests/zisk/prove/basic/src/main.rs
Normal file
16
tests/zisk/prove/basic/src/main.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
#![no_main]
|
||||
|
||||
ziskos::entrypoint!(main);
|
||||
|
||||
fn main() {
|
||||
let input = ziskos::read_input();
|
||||
if input.len() != 6 {
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
// Read an input
|
||||
let n = u32::from_le_bytes(input[..4].try_into().unwrap());
|
||||
let a = u16::from_le_bytes(input[4..6].try_into().unwrap()) as u32;
|
||||
|
||||
ziskos::set_output(0, (n + a) * 2);
|
||||
}
|
||||
Reference in New Issue
Block a user