Support multiple proof kind (#161)

This commit is contained in:
Han
2025-10-14 21:30:07 +08:00
committed by GitHub
parent ad4e211af7
commit 1d9c16dc8d
25 changed files with 365 additions and 360 deletions

1
Cargo.lock generated
View File

@@ -15552,6 +15552,7 @@ dependencies = [
"indexmap 2.10.0",
"serde",
"serde_json",
"strum 0.27.2",
"thiserror 2.0.12",
]

View File

@@ -82,7 +82,7 @@ ere-sp1 = { git = "https://github.com/eth-act/ere.git", tag = "v0.0.12" }
```rust
// main.rs
use ere_sp1::{EreSP1, RV32_IM_SUCCINCT_ZKVM_ELF};
use zkvm_interface::{Compiler, Input, ProverResourceType, zkVM};
use zkvm_interface::{Compiler, Input, ProofKind, ProverResourceType, zkVM};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let guest_directory = std::path::Path::new("workspace/guest");
@@ -99,13 +99,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
io.write(42u32);
// Execute
let _report = zkvm.execute(&io)?;
let (public_values, report) = zkvm.execute(&io)?;
// Prove
let (proof, _report) = zkvm.prove(&io)?;
let (public_values, proof, report) = zkvm.prove(&io, ProofKind::Compressed)?;
// Verify
zkvm.verify(&proof)?;
let public_values = zkvm.verify(&proof)?;
Ok(())
}
@@ -129,7 +129,7 @@ ere-dockerized = { git = "https://github.com/eth-act/ere.git", tag = "v0.0.12" }
```rust
// main.rs
use ere_dockerized::{EreDockerizedCompiler, EreDockerizedzkVM, ErezkVM};
use zkvm_interface::{Compiler, Input, ProverResourceType, zkVM};
use zkvm_interface::{Compiler, Input, ProofKind, ProverResourceType, zkVM};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let guest_directory = std::path::Path::new("workspace/guest");
@@ -146,13 +146,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
io.write(42u32);
// Execute
let _report = zkvm.execute(&io)?;
let (public_values, report) = zkvm.execute(&io)?;
// Prove
let (proof, _report) = zkvm.prove(&io)?;
let (public_values, proof, report) = zkvm.prove(&io, ProofKind::Compressed)?;
// Verify
zkvm.verify(&proof)?;
let public_values = zkvm.verify(&proof)?;
Ok(())
}

View File

@@ -131,6 +131,10 @@ impl DockerRunCmd {
)
}
pub fn env(self, key: impl AsRef<str>, value: impl AsRef<str>) -> Self {
self.option("env", format!("{}={}", key.as_ref(), value.as_ref()))
}
/// Mounts `/var/run/docker.sock` to allow Docker-out-of-Docker (DooD).
pub fn mount_docker_socket(self) -> Self {
self.volume(DOCKER_SOCKET, DOCKER_SOCKET)
@@ -152,7 +156,7 @@ impl DockerRunCmd {
pub fn inherit_env(self, key: impl AsRef<str>) -> Self {
let key = key.as_ref();
match env::var(key) {
Ok(val) => self.option("env", format!("{key}={val}")),
Ok(val) => self.env(key, val),
Err(_) => self,
}
}

View File

@@ -27,7 +27,7 @@
//! ```rust,no_run
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use ere_dockerized::{EreDockerizedCompiler, EreDockerizedzkVM, ErezkVM};
//! use zkvm_interface::{Compiler, Input, ProverResourceType, zkVM};
//! use zkvm_interface::{Compiler, Input, ProofKind, ProverResourceType, zkVM};
//! use std::path::Path;
//!
//! // The zkVM we plan to use
@@ -52,7 +52,7 @@
//! println!("Execution cycles: {}", execution_report.total_num_cycles);
//!
//! // Generate proof
//! let (public_values, proof, proving_report) = zkvm.prove(&inputs)?;
//! let (public_values, proof, proving_report) = zkvm.prove(&inputs, ProofKind::Compressed)?;
//! println!("Proof generated in: {:?}", proving_report.proving_time);
//!
//! // Verify proof
@@ -83,8 +83,8 @@ use std::{
use tempfile::TempDir;
use tracing::error;
use zkvm_interface::{
Compiler, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType,
PublicValues, zkVM, zkVMError,
Compiler, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM, zkVMError,
};
include!(concat!(env!("OUT_DIR"), "/crate_version.rs"));
@@ -258,7 +258,7 @@ impl ErezkVM {
&self,
program: &SerializedProgram,
resource: &ProverResourceType,
) -> Result<String, CommonError> {
) -> Result<ServerContainer, CommonError> {
let port = self.server_port().to_string();
let name = format!("ere-server-{self}-{port}");
let gpu = matches!(resource, ProverResourceType::Gpu);
@@ -295,11 +295,9 @@ impl ErezkVM {
if gpu {
cmd = match self {
ErezkVM::OpenVM => cmd.gpus("all"),
// SP1's and Risc0's GPU proving requires Docker to start GPU prover
// service, to give the client access to the prover service, we need
// to use the host networking driver.
// The `--cuda` flags will be set when the GPU prover service is
// spin up, so we don't need to set here.
// SP1 runs docker command to spin up the server to do GPU
// proving, to give the client access to the prover service, we
// need to use the host networking driver.
ErezkVM::SP1 => cmd.mount_docker_socket().network("host"),
ErezkVM::Risc0 => cmd.gpus("all").inherit_env("RISC0_DEFAULT_PROVER_NUM_GPUS"),
ErezkVM::Zisk => cmd.gpus("all"),
@@ -307,13 +305,41 @@ impl ErezkVM {
}
}
let tempdir = TempDir::new()
.map_err(|err| CommonError::io(err, "Failed to create temporary directory"))?;
// zkVM specific options needed for proving Groth16 proof.
cmd = match self {
// Risc0 and SP1 runs docker command to prove Groth16 proof, and
// they pass the input by mounting temporary directory. Here we
// create a temporary directory and mount it on the top level, so
// the volume could be shared, and override `TMPDIR` so we don't
// need to mount the whole `/tmp`.
ErezkVM::Risc0 => cmd
.mount_docker_socket()
.env("TMPDIR", tempdir.path().to_string_lossy())
.volume(tempdir.path(), tempdir.path()),
ErezkVM::SP1 => {
let groth16_circuit_path = home_dir().join(".sp1").join("circuits").join("groth16");
cmd.mount_docker_socket()
.env(
"SP1_GROTH16_CIRCUIT_PATH",
groth16_circuit_path.to_string_lossy(),
)
.env("TMPDIR", tempdir.path().to_string_lossy())
.volume(tempdir.path(), tempdir.path())
.volume(&groth16_circuit_path, &groth16_circuit_path)
}
_ => cmd,
};
let args = iter::empty()
.chain(["--port", &port])
.chain(resource.to_args());
cmd.spawn(args, &program.0)
.map_err(CommonError::DockerRunCmd)?;
Ok(name)
Ok(ServerContainer { name, tempdir })
}
}
@@ -417,22 +443,29 @@ impl Compiler for EreDockerizedCompiler {
}
}
struct ServerContainer {
name: String,
#[allow(dead_code)]
tempdir: TempDir,
}
impl Drop for ServerContainer {
fn drop(&mut self) {
if let Err(err) = stop_docker_container(&self.name) {
error!("{err}");
}
}
}
pub struct EreDockerizedzkVM {
zkvm: ErezkVM,
program: SerializedProgram,
resource: ProverResourceType,
server_container_name: String,
#[allow(dead_code)]
server_container: ServerContainer,
client: zkVMClient,
}
impl Drop for EreDockerizedzkVM {
fn drop(&mut self) {
if let Err(err) = stop_docker_container(&self.server_container_name) {
error!("{err}");
}
}
}
impl EreDockerizedzkVM {
pub fn new(
zkvm: ErezkVM,
@@ -441,7 +474,7 @@ impl EreDockerizedzkVM {
) -> Result<Self, zkVMError> {
zkvm.build_docker_image(matches!(resource, ProverResourceType::Gpu))?;
let server_container_name = zkvm.spawn_server(&program, &resource)?;
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)?;
@@ -450,7 +483,7 @@ impl EreDockerizedzkVM {
zkvm,
program,
resource,
server_container_name,
server_container,
client,
})
}
@@ -484,19 +517,21 @@ impl zkVM for EreDockerizedzkVM {
fn prove(
&self,
inputs: &Input,
proof_kind: ProofKind,
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
let serialized_input = self
.zkvm
.serialize_inputs(inputs)
.map_err(|err| DockerizedError::Prove(ProveError::Common(err)))?;
let (public_values, proof, report) = block_on(self.client.prove(serialized_input))
.map_err(|err| DockerizedError::Prove(ProveError::Client(err)))?;
let (public_values, proof, report) =
block_on(self.client.prove(serialized_input, proof_kind))
.map_err(|err| DockerizedError::Prove(ProveError::Client(err)))?;
Ok((public_values, proof, report))
}
fn verify(&self, proof: &[u8]) -> Result<PublicValues, zkVMError> {
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
let public_values = block_on(self.client.verify(proof))
.map_err(|err| DockerizedError::Verify(VerifyError::Client(err)))?;
@@ -530,6 +565,10 @@ fn workspace_dir() -> PathBuf {
dir.canonicalize().unwrap()
}
fn home_dir() -> PathBuf {
PathBuf::from(std::env::var("HOME").expect("env `$HOME` should be set"))
}
#[cfg(test)]
mod test {
macro_rules! test_compile {

View File

@@ -14,8 +14,8 @@ use std::{
};
use tempfile::TempDir;
use zkvm_interface::{
Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType, PublicValues,
zkVM, zkVMError,
Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
PublicValues, zkVM, zkVMError,
};
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
@@ -72,7 +72,12 @@ impl zkVM for EreJolt {
fn prove(
&self,
inputs: &Input,
proof_kind: ProofKind,
) -> Result<(PublicValues, Proof, zkvm_interface::ProgramProvingReport), zkVMError> {
if proof_kind != ProofKind::Compressed {
panic!("Only Compressed proof kind is supported.");
}
let (_tempdir, program) = program(&self.elf)?;
let now = std::time::Instant::now();
@@ -89,13 +94,17 @@ impl zkVM for EreJolt {
Ok((
public_values,
proof_bytes,
Proof::Compressed(proof_bytes),
ProgramProvingReport::new(elapsed),
))
}
fn verify(&self, proof_bytes: &[u8]) -> Result<PublicValues, zkVMError> {
let proof = EreJoltProof::deserialize_compressed(&mut Cursor::new(proof_bytes))
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
let Proof::Compressed(proof) = proof else {
return Err(zkVMError::other("Only Compressed proof kind is supported."));
};
let proof = EreJoltProof::deserialize_compressed(&mut Cursor::new(proof))
.map_err(|err| JoltError::Verify(VerifyError::Serialization(err)))?;
verify_generic(proof, self.verifier_preprocessing.clone()).map_err(JoltError::Verify)?;

View File

@@ -16,8 +16,8 @@ use miden_verifier::verify as miden_verify;
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use std::{env, io::Read, time::Instant};
use zkvm_interface::{
Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType, PublicValues,
zkVM, zkVMError,
Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
PublicValues, zkVM, zkVMError,
};
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
@@ -83,7 +83,12 @@ impl zkVM for EreMiden {
fn prove(
&self,
inputs: &Input,
proof_kind: ProofKind,
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
if proof_kind != ProofKind::Compressed {
panic!("Only Compressed proof kind is supported.");
}
let (stack_inputs, advice_inputs) = generate_miden_inputs(inputs)?;
let mut host = Self::setup_host()?;
@@ -112,12 +117,16 @@ impl zkVM for EreMiden {
Ok((
public_values,
proof_bytes,
Proof::Compressed(proof_bytes),
ProgramProvingReport::new(start.elapsed()),
))
}
fn verify(&self, proof: &[u8]) -> Result<PublicValues, zkVMError> {
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
let Proof::Compressed(proof) = proof else {
return Err(zkVMError::other("Only Compressed proof kind is supported."));
};
let bundle: MidenProofBundle = bincode::deserialize(proof)
.map_err(|e| MidenError::Verify(VerifyError::BundleDeserialization(e)))?;
@@ -162,7 +171,7 @@ mod tests {
compiler::{MidenAsm, MidenProgram},
};
use test_utils::host::testing_guest_directory;
use zkvm_interface::{Compiler, Input, ProverResourceType, zkVM};
use zkvm_interface::{Compiler, Input, ProofKind, ProverResourceType, zkVM};
fn load_miden_program(guest_name: &str) -> MidenProgram {
MidenAsm
@@ -184,7 +193,7 @@ mod tests {
inputs.write(const_b);
// Prove
let (prover_public_values, proof, _) = zkvm.prove(&inputs).unwrap();
let (prover_public_values, proof, _) = zkvm.prove(&inputs, ProofKind::default()).unwrap();
// Verify
let verifier_public_values = zkvm.verify(&proof).unwrap();
@@ -211,7 +220,7 @@ mod tests {
inputs.write(n_iterations);
// Prove
let (prover_public_values, proof, _) = zkvm.prove(&inputs).unwrap();
let (prover_public_values, proof, _) = zkvm.prove(&inputs, ProofKind::default()).unwrap();
// Verify
let verifier_public_values = zkvm.verify(&proof).unwrap();

View File

@@ -9,8 +9,8 @@ use serde::de::DeserializeOwned;
use std::{io::Read, time::Instant};
use tracing::info;
use zkvm_interface::{
Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType, PublicValues,
zkVM, zkVMError,
Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
PublicValues, zkVM, zkVMError,
};
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
@@ -47,7 +47,12 @@ impl zkVM for EreNexus {
fn prove(
&self,
_inputs: &Input,
proof_kind: ProofKind,
) -> Result<(PublicValues, Proof, zkvm_interface::ProgramProvingReport), zkVMError> {
if proof_kind != ProofKind::Compressed {
panic!("Only Compressed proof kind is supported.");
}
let prover: Stwo<Local> = Stwo::new_from_bytes(&self.program)
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))
.map_err(zkVMError::from)?;
@@ -68,10 +73,18 @@ impl zkVM for EreNexus {
// TODO: Public values
let public_values = Vec::new();
Ok((public_values, bytes, ProgramProvingReport::new(elapsed)))
Ok((
public_values,
Proof::Compressed(bytes),
ProgramProvingReport::new(elapsed),
))
}
fn verify(&self, proof: &[u8]) -> Result<PublicValues, zkVMError> {
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
let Proof::Compressed(proof) = proof else {
return Err(zkVMError::other("Only Compressed proof kind is supported."));
};
info!("Verifying proof...");
let proof: nexus_sdk::stwo::seq::Proof = bincode::deserialize(proof)

View File

@@ -19,8 +19,8 @@ use openvm_transpiler::{elf::Elf, openvm_platform::memory::MEM_SIZE};
use serde::de::DeserializeOwned;
use std::{env, io::Read, path::PathBuf, sync::Arc, time::Instant};
use zkvm_interface::{
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType,
PublicValues, zkVM, zkVMError,
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM, zkVMError,
};
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
@@ -128,7 +128,12 @@ impl zkVM for EreOpenVM {
fn prove(
&self,
inputs: &Input,
proof_kind: ProofKind,
) -> Result<(PublicValues, Proof, zkvm_interface::ProgramProvingReport), zkVMError> {
if proof_kind != ProofKind::Compressed {
panic!("Only Compressed proof kind is supported.");
}
let mut stdin = StdIn::default();
serialize_inputs(&mut stdin, inputs);
@@ -164,13 +169,17 @@ impl zkVM for EreOpenVM {
Ok((
public_values,
proof_bytes,
Proof::Compressed(proof_bytes),
ProgramProvingReport::new(elapsed),
))
}
fn verify(&self, mut proof: &[u8]) -> Result<PublicValues, zkVMError> {
let proof = VmStarkProof::<SC>::decode(&mut proof)
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
let Proof::Compressed(proof) = proof else {
return Err(zkVMError::other("Only Compressed proof kind is supported."));
};
let proof = VmStarkProof::<SC>::decode(&mut proof.as_slice())
.map_err(|e| OpenVMError::from(VerifyError::DeserializeProof(e)))?;
CpuSdk::verify_proof(&self.agg_vk, self.app_commit, &proof)
@@ -232,7 +241,7 @@ mod tests {
use test_utils::host::{
BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory,
};
use zkvm_interface::{Compiler, ProverResourceType, zkVM};
use zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
fn basic_program() -> OpenVMProgram {
static PROGRAM: OnceLock<OpenVMProgram> = OnceLock::new();
@@ -287,7 +296,7 @@ mod tests {
BasicProgramIo::invalid_type(),
BasicProgramIo::invalid_data(),
] {
zkvm.prove(&inputs).unwrap_err();
zkvm.prove(&inputs, ProofKind::default()).unwrap_err();
}
}
}

View File

@@ -15,8 +15,8 @@ use std::{
time::{self, Instant},
};
use zkvm_interface::{
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType,
PublicValues, zkVM, zkVMError,
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM, zkVMError,
};
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
@@ -71,7 +71,12 @@ impl zkVM for ErePico {
fn prove(
&self,
inputs: &Input,
proof_kind: ProofKind,
) -> Result<(PublicValues, Proof, zkvm_interface::ProgramProvingReport), zkVMError> {
if proof_kind != ProofKind::Compressed {
panic!("Only Compressed proof kind is implemented.");
}
let client = self.client();
let mut stdin = client.new_stdin_builder();
@@ -91,12 +96,18 @@ impl zkVM for ErePico {
Ok((
public_values,
proof_bytes,
Proof::Compressed(proof_bytes),
ProgramProvingReport::new(elapsed),
))
}
fn verify(&self, proof: &[u8]) -> Result<PublicValues, zkVMError> {
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
let Proof::Compressed(proof) = proof else {
return Err(zkVMError::other(
"Only Compressed proof kind is implemented.",
));
};
let client = self.client();
let proof: PicoProofWithPublicValues = bincode::deserialize(proof)
@@ -172,7 +183,7 @@ mod tests {
use test_utils::host::{
BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory,
};
use zkvm_interface::{Compiler, ProverResourceType, zkVM};
use zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
static BASIC_PROGRAM: OnceLock<PicoProgram> = OnceLock::new();
@@ -228,7 +239,7 @@ mod tests {
BasicProgramIo::invalid_type,
BasicProgramIo::invalid_data,
] {
panic::catch_unwind(|| zkvm.prove(&inputs_gen())).unwrap_err();
panic::catch_unwind(|| zkvm.prove(&inputs_gen(), ProofKind::default())).unwrap_err();
}
}
}

View File

@@ -1,16 +1,15 @@
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
use crate::{compiler::Risc0Program, output::deserialize_from};
use borsh::{BorshDeserialize, BorshSerialize};
use risc0_zkvm::{
DEFAULT_MAX_PO2, DefaultProver, ExecutorEnv, ExecutorEnvBuilder, ExternalProver, InnerReceipt,
Journal, ProverOpts, Receipt, ReceiptClaim, SuccinctReceipt, default_executor, default_prover,
ProverOpts, Receipt, default_executor, default_prover,
};
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use serde::de::DeserializeOwned;
use std::{env, io::Read, ops::RangeInclusive, rc::Rc, time::Instant};
use zkvm_interface::{
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType,
PublicValues, zkVM, zkVMError,
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM, zkVMError,
};
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
@@ -45,33 +44,6 @@ const DEFAULT_KECCAK_PO2: usize = 17;
/// [`KECCAK_PO2_RANGE`]: https://github.com/risc0/risc0/blob/v3.0.3/risc0/circuit/keccak/src/lib.rs#L29.
const KECCAK_PO2_RANGE: RangeInclusive<usize> = 14..=18;
#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub struct Risc0ProofWithPublicValues {
/// The aggregated proof generated by the Risc0 zkVM.
pub receipt: SuccinctReceipt<ReceiptClaim>,
/// The public values generated by the Risc0 zkVM.
pub journal: Journal,
}
impl From<Receipt> for Risc0ProofWithPublicValues {
fn from(receipt: Receipt) -> Self {
match receipt.inner {
// We always use `ProverOpts::succinct()`.
InnerReceipt::Succinct(inner) => Self {
receipt: inner,
journal: receipt.journal,
},
_ => unreachable!(),
}
}
}
impl From<Risc0ProofWithPublicValues> for Receipt {
fn from(Risc0ProofWithPublicValues { receipt, journal }: Risc0ProofWithPublicValues) -> Self {
Receipt::new(InnerReceipt::Succinct(receipt), journal.bytes)
}
}
pub struct EreRisc0 {
program: Risc0Program,
resource: ProverResourceType,
@@ -138,6 +110,7 @@ impl zkVM for EreRisc0 {
fn prove(
&self,
inputs: &Input,
proof_kind: ProofKind,
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
let prover = match self.resource {
ProverResourceType::Cpu => Rc::new(ExternalProver::new("ipc", "r0vm")),
@@ -171,15 +144,22 @@ impl zkVM for EreRisc0 {
.build()
.map_err(zkVMError::other)?;
let opts = match proof_kind {
ProofKind::Compressed => ProverOpts::succinct(),
ProofKind::Groth16 => ProverOpts::groth16(),
};
let now = std::time::Instant::now();
let prove_info = prover
.prove_with_opts(env, &self.program.elf, &ProverOpts::succinct())
.prove_with_opts(env, &self.program.elf, &opts)
.map_err(zkVMError::other)?;
let proving_time = now.elapsed();
let public_values = prove_info.receipt.journal.bytes.clone();
let proof = borsh::to_vec(&Risc0ProofWithPublicValues::from(prove_info.receipt))
.map_err(zkVMError::other)?;
let proof = Proof::new(
proof_kind,
borsh::to_vec(&prove_info.receipt).map_err(zkVMError::other)?,
);
Ok((
public_values,
@@ -188,10 +168,20 @@ impl zkVM for EreRisc0 {
))
}
fn verify(&self, proof: &[u8]) -> Result<PublicValues, zkVMError> {
let receipt: Receipt = borsh::from_slice::<Risc0ProofWithPublicValues>(proof)
.map_err(zkVMError::other)?
.into();
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
let proof_kind = proof.kind();
let receipt: Receipt = borsh::from_slice(proof.as_bytes()).map_err(zkVMError::other)?;
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:?}",
)))?;
}
receipt
.verify(self.program.image_id)
@@ -245,7 +235,7 @@ mod tests {
use test_utils::host::{
BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory,
};
use zkvm_interface::{Compiler, Input, ProverResourceType, zkVM};
use zkvm_interface::{Compiler, Input, ProofKind, ProverResourceType, zkVM};
static BASIC_PROGRAM: OnceLock<Risc0Program> = OnceLock::new();
@@ -301,7 +291,7 @@ mod tests {
BasicProgramIo::invalid_type(),
BasicProgramIo::invalid_data(),
] {
zkvm.prove(&inputs).unwrap_err();
zkvm.prove(&inputs, ProofKind::default()).unwrap_err();
}
}

View File

@@ -2,6 +2,11 @@ syntax = "proto3";
package api;
enum ProofKind {
Compressed = 0;
Groth16 = 1;
}
service ZkvmService {
rpc Execute(ExecuteRequest) returns (ExecuteResponse) {}
rpc Prove(ProveRequest) returns (ProveResponse) {}
@@ -19,6 +24,7 @@ message ExecuteResponse {
message ProveRequest {
bytes input = 1;
ProofKind proof_kind = 2;
}
message ProveResponse {
@@ -29,6 +35,7 @@ message ProveResponse {
message VerifyRequest {
bytes proof = 1;
ProofKind proof_kind = 2;
}
message VerifyResponse {

View File

@@ -1,196 +0,0 @@
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(dead_code)]
// This file is @generated by prost-build.
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ExecuteRequest {
#[prost(bytes = "vec", tag = "1")]
pub input: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ExecuteResponse {
#[prost(bytes = "vec", tag = "1")]
pub public_values: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", tag = "2")]
pub report: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ProveRequest {
#[prost(bytes = "vec", tag = "1")]
pub input: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ProveResponse {
#[prost(bytes = "vec", tag = "1")]
pub public_values: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", tag = "2")]
pub proof: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", tag = "3")]
pub report: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct VerifyRequest {
#[prost(bytes = "vec", tag = "1")]
pub proof: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct VerifyResponse {
#[prost(bytes = "vec", tag = "1")]
pub public_values: ::prost::alloc::vec::Vec<u8>,
}
pub use twirp;
#[twirp::async_trait::async_trait]
pub trait ZkvmService: Send + Sync {
async fn execute(
&self,
req: twirp::Request<ExecuteRequest>,
) -> twirp::Result<twirp::Response<ExecuteResponse>>;
async fn prove(
&self,
req: twirp::Request<ProveRequest>,
) -> twirp::Result<twirp::Response<ProveResponse>>;
async fn verify(
&self,
req: twirp::Request<VerifyRequest>,
) -> twirp::Result<twirp::Response<VerifyResponse>>;
}
#[twirp::async_trait::async_trait]
impl<T> ZkvmService for std::sync::Arc<T>
where
T: ZkvmService + Sync + Send,
{
async fn execute(
&self,
req: twirp::Request<ExecuteRequest>,
) -> twirp::Result<twirp::Response<ExecuteResponse>> {
T::execute(&*self, req).await
}
async fn prove(
&self,
req: twirp::Request<ProveRequest>,
) -> twirp::Result<twirp::Response<ProveResponse>> {
T::prove(&*self, req).await
}
async fn verify(
&self,
req: twirp::Request<VerifyRequest>,
) -> twirp::Result<twirp::Response<VerifyResponse>> {
T::verify(&*self, req).await
}
}
pub fn router<T>(api: T) -> twirp::Router
where
T: ZkvmService + Clone + Send + Sync + 'static,
{
twirp::details::TwirpRouterBuilder::new("/api.ZkvmService", api)
.route(
"/Execute",
|api: T, req: twirp::Request<ExecuteRequest>| async move {
api.execute(req).await
},
)
.route(
"/Prove",
|api: T, req: twirp::Request<ProveRequest>| async move {
api.prove(req).await
},
)
.route(
"/Verify",
|api: T, req: twirp::Request<VerifyRequest>| async move {
api.verify(req).await
},
)
.build()
}
#[twirp::async_trait::async_trait]
impl ZkvmService for twirp::client::Client {
async fn execute(
&self,
req: twirp::Request<ExecuteRequest>,
) -> twirp::Result<twirp::Response<ExecuteResponse>> {
self.request("api.ZkvmService/Execute", req).await
}
async fn prove(
&self,
req: twirp::Request<ProveRequest>,
) -> twirp::Result<twirp::Response<ProveResponse>> {
self.request("api.ZkvmService/Prove", req).await
}
async fn verify(
&self,
req: twirp::Request<VerifyRequest>,
) -> twirp::Result<twirp::Response<VerifyResponse>> {
self.request("api.ZkvmService/Verify", req).await
}
}
#[allow(dead_code)]
pub mod handler {
use super::*;
pub struct ZkvmServiceHandler {
inner: std::sync::Arc<dyn ZkvmService>,
}
impl ZkvmServiceHandler {
#[allow(clippy::new_ret_no_self)]
pub fn new<M: ZkvmService + 'static>(inner: M) -> Self {
Self {
inner: std::sync::Arc::new(inner),
}
}
}
#[twirp::async_trait::async_trait]
impl twirp::client::DirectHandler for ZkvmServiceHandler {
fn service(&self) -> &str {
"api.ZkvmService"
}
async fn handle(
&self,
method: &str,
req: twirp::reqwest::Request,
) -> twirp::Result<twirp::reqwest::Response> {
match method {
"Execute" => {
twirp::details::encode_response(
self
.inner
.execute(twirp::details::decode_request(req).await?)
.await?,
)
}
"Prove" => {
twirp::details::encode_response(
self
.inner
.prove(twirp::details::decode_request(req).await?)
.await?,
)
}
"Verify" => {
twirp::details::encode_response(
self
.inner
.verify(twirp::details::decode_request(req).await?)
.await?,
)
}
_ => {
Err(
twirp::bad_route(
format!(
"unknown rpc `{method}` for service `{}`, url: {:?}",
"api.ZkvmService",
req.url(),
),
),
)
}
}
}
}
}

View File

@@ -9,7 +9,9 @@ use anyhow::{Context, Error, bail};
use std::time::{Duration, Instant};
use tokio::time::sleep;
use twirp::{Client, Request, reqwest};
use zkvm_interface::{ProgramExecutionReport, ProgramProvingReport, Proof, PublicValues};
use zkvm_interface::{
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, PublicValues,
};
pub use twirp::url::Url;
@@ -71,10 +73,14 @@ impl zkVMClient {
pub async fn prove(
&self,
input: SerializedInput,
proof_kind: ProofKind,
) -> Result<(PublicValues, Proof, ProgramProvingReport), Error> {
let input = bincode::serialize(&input).with_context(|| "Failed to serialize input")?;
let request = Request::new(ProveRequest { input });
let request = Request::new(ProveRequest {
input,
proof_kind: proof_kind as i32,
});
let response = self
.client
@@ -91,12 +97,13 @@ impl zkVMClient {
let report: ProgramProvingReport = bincode::deserialize(&report)
.with_context(|| "Failed to deserialize proving report")?;
Ok((public_values, proof, report))
Ok((public_values, Proof::new(proof_kind, proof), report))
}
pub async fn verify(&self, proof: &[u8]) -> Result<PublicValues, Error> {
pub async fn verify(&self, proof: &Proof) -> Result<PublicValues, Error> {
let request = Request::new(VerifyRequest {
proof: proof.to_vec(),
proof: proof.as_bytes().to_vec(),
proof_kind: proof.kind() as i32,
});
let response = self

View File

@@ -1,6 +1,10 @@
pub(crate) mod api;
pub mod client;
pub mod input;
#[allow(dead_code)]
pub(crate) mod api {
include!(concat!(env!("OUT_DIR"), "/api.rs"));
}
#[cfg(feature = "server")]
pub mod server;

View File

@@ -6,7 +6,7 @@ use crate::{
input::SerializedInput,
};
use twirp::{Request, Response, async_trait::async_trait, invalid_argument};
use zkvm_interface::zkVM;
use zkvm_interface::{Proof, ProofKind, zkVM};
pub use api::router;
@@ -55,15 +55,18 @@ impl<T: 'static + zkVM + Send + Sync> ZkvmService for zkVMServer<T> {
let input = bincode::deserialize::<SerializedInput>(&request.input)
.map_err(|_| invalid_argument("failed to deserialize input"))?
.into();
let proof_kind = ProofKind::from_repr(request.proof_kind as usize).ok_or_else(|| {
invalid_argument(format!("invalid proof kind: {}", request.proof_kind))
})?;
let (public_values, proof, report) = self
.zkvm
.prove(&input)
.prove(&input, proof_kind)
.map_err(|err| invalid_argument(format!("failed to prove: {err:?}")))?;
Ok(Response::new(ProveResponse {
public_values,
proof,
proof: proof.as_bytes().to_vec(),
report: bincode::serialize(&report).unwrap(),
}))
}
@@ -74,9 +77,13 @@ impl<T: 'static + zkVM + Send + Sync> ZkvmService for zkVMServer<T> {
) -> twirp::Result<Response<VerifyResponse>> {
let request = request.into_body();
let proof_kind = ProofKind::from_repr(request.proof_kind as usize).ok_or_else(|| {
invalid_argument(format!("invalid proof kind: {}", request.proof_kind))
})?;
let public_values = self
.zkvm
.verify(&request.proof)
.verify(&Proof::new(proof_kind, request.proof))
.map_err(|err| invalid_argument(format!("failed to verify: {err:?}")))?;
Ok(Response::new(VerifyResponse { public_values }))

View File

@@ -1,7 +1,8 @@
use std::{path::PathBuf, process::ExitStatus};
use sp1_sdk::SP1ProofMode;
use thiserror::Error;
use zkvm_interface::zkVMError;
use zkvm_interface::{ProofKind, zkVMError};
impl From<SP1Error> for zkVMError {
fn from(value: SP1Error) -> Self {
@@ -80,6 +81,9 @@ pub enum VerifyError {
#[error("Deserialising proof failed: {0}")]
Bincode(#[from] bincode::Error),
#[error("Invalid proof kind, expected: {}, got: {}", 0.to_string(), 1.to_string() )]
InvalidProofKind(ProofKind, SP1ProofMode),
#[error("SP1 SDK verification failed: {0}")]
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
}

View File

@@ -6,14 +6,14 @@ use crate::{
};
use serde::de::DeserializeOwned;
use sp1_sdk::{
CpuProver, CudaProver, NetworkProver, Prover, ProverClient, SP1ProofWithPublicValues,
SP1ProvingKey, SP1Stdin, SP1VerifyingKey,
CpuProver, CudaProver, NetworkProver, Prover, ProverClient, SP1ProofMode,
SP1ProofWithPublicValues, SP1ProvingKey, SP1Stdin, SP1VerifyingKey,
};
use std::{io::Read, time::Instant};
use tracing::info;
use zkvm_interface::{
Input, InputItem, NetworkProverConfig, ProgramExecutionReport, ProgramProvingReport, Proof,
ProverResourceType, PublicValues, zkVM, zkVMError,
ProofKind, ProverResourceType, PublicValues, zkVM, zkVMError,
};
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
@@ -52,17 +52,17 @@ impl ProverType {
.run()
.map_err(|e| SP1Error::Execute(ExecuteError::Client(e.into())))
}
fn prove(
&self,
pk: &SP1ProvingKey,
input: &SP1Stdin,
mode: SP1ProofMode,
) -> Result<SP1ProofWithPublicValues, SP1Error> {
match self {
ProverType::Cpu(cpu_prover) => cpu_prover.prove(pk, input).compressed().run(),
ProverType::Gpu(cuda_prover) => cuda_prover.prove(pk, input).compressed().run(),
ProverType::Network(network_prover) => {
network_prover.prove(pk, input).compressed().run()
}
ProverType::Cpu(cpu_prover) => cpu_prover.prove(pk, input).mode(mode).run(),
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())))
}
@@ -168,33 +168,56 @@ impl zkVM for EreSP1 {
fn prove(
&self,
inputs: &zkvm_interface::Input,
inputs: &Input,
proof_kind: ProofKind,
) -> Result<(PublicValues, Proof, zkvm_interface::ProgramProvingReport), zkVMError> {
info!("Generating proof…");
let mut stdin = SP1Stdin::new();
serialize_inputs(&mut stdin, inputs);
let mode = match proof_kind {
ProofKind::Compressed => SP1ProofMode::Compressed,
ProofKind::Groth16 => SP1ProofMode::Groth16,
};
let client = Self::create_client(&self.resource);
let start = std::time::Instant::now();
let proof_with_inputs = client.prove(&self.pk, &stdin)?;
let proof = client.prove(&self.pk, &stdin, mode)?;
let proving_time = start.elapsed();
let bytes = bincode::serialize(&proof_with_inputs)
.map_err(|err| SP1Error::Prove(ProveError::Bincode(err)))?;
let public_values = proof.public_values.to_vec();
let proof = Proof::new(
proof_kind,
bincode::serialize(&proof).map_err(|err| SP1Error::Prove(ProveError::Bincode(err)))?,
);
Ok((
proof_with_inputs.public_values.to_vec(),
bytes,
public_values,
proof,
ProgramProvingReport::new(proving_time),
))
}
fn verify(&self, proof: &[u8]) -> Result<PublicValues, zkVMError> {
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
info!("Verifying proof…");
let proof: SP1ProofWithPublicValues = bincode::deserialize(proof)
let proof_kind = proof.kind();
let proof: SP1ProofWithPublicValues = bincode::deserialize(proof.as_bytes())
.map_err(|err| SP1Error::Verify(VerifyError::Bincode(err)))?;
let inner_proof_kind = SP1ProofMode::from(&proof.proof);
if !matches!(
(proof_kind, inner_proof_kind),
(ProofKind::Compressed, SP1ProofMode::Compressed)
| (ProofKind::Groth16, SP1ProofMode::Groth16)
) {
return Err(SP1Error::Verify(VerifyError::InvalidProofKind(
proof_kind,
inner_proof_kind,
)))?;
}
let client = Self::create_client(&self.resource);
client.verify(&proof, &self.vk).map_err(zkVMError::from)?;
@@ -235,7 +258,7 @@ mod tests {
use test_utils::host::{
BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory,
};
use zkvm_interface::{Compiler, NetworkProverConfig, ProverResourceType, zkVM};
use zkvm_interface::{Compiler, NetworkProverConfig, ProofKind, ProverResourceType, zkVM};
static BASIC_PROGRAM: OnceLock<Vec<u8>> = OnceLock::new();
@@ -296,7 +319,7 @@ mod tests {
BasicProgramIo::invalid_type,
BasicProgramIo::invalid_data,
] {
panic::catch_unwind(|| zkvm.prove(&inputs_gen())).unwrap_err();
panic::catch_unwind(|| zkvm.prove(&inputs_gen(), ProofKind::default())).unwrap_err();
}
}

View File

@@ -1,7 +1,7 @@
use std::{io, path::PathBuf, process::ExitStatus};
use thiserror::Error;
use zkm_sdk::ZKMProofKind;
use zkvm_interface::zkVMError;
use zkvm_interface::{ProofKind, zkVMError};
impl From<ZirenError> for zkVMError {
fn from(value: ZirenError) -> Self {
@@ -78,8 +78,8 @@ pub enum VerifyError {
#[error("Deserialising proof with `bincode` failed: {0}")]
Bincode(#[from] bincode::Error),
#[error("Expect to get Compressed proof, but got: {}", 0.to_string())]
InvalidProofKind(ZKMProofKind),
#[error("Invalid proof kind, expected: {}, got: {}", 0.to_string(), 1.to_string() )]
InvalidProofKind(ProofKind, ZKMProofKind),
#[error("Ziren verification failed: {0}")]
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),

View File

@@ -12,8 +12,8 @@ use zkm_sdk::{
ZKMVerifyingKey,
};
use zkvm_interface::{
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType,
PublicValues, zkVM, zkVMError,
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM, zkVMError,
};
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
@@ -64,38 +64,55 @@ impl zkVM for EreZiren {
fn prove(
&self,
inputs: &Input,
proof_kind: ProofKind,
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
info!("Generating proof…");
let mut stdin = ZKMStdin::new();
serialize_inputs(&mut stdin, inputs);
let inner_proof_kind = match proof_kind {
ProofKind::Compressed => ZKMProofKind::Compressed,
ProofKind::Groth16 => ZKMProofKind::Groth16,
};
let start = std::time::Instant::now();
let proof = CpuProver::new()
.prove(&self.pk, stdin, ZKMProofKind::Compressed)
.prove(&self.pk, stdin, inner_proof_kind)
.map_err(|err| ZirenError::Prove(ProveError::Client(err.into())))?;
let proving_time = start.elapsed();
let bytes = bincode::serialize(&proof)
.map_err(|err| ZirenError::Prove(ProveError::Bincode(err)))?;
let public_values = proof.public_values.to_vec();
let proof = Proof::new(
proof_kind,
bincode::serialize(&proof)
.map_err(|err| ZirenError::Prove(ProveError::Bincode(err)))?,
);
Ok((
proof.public_values.to_vec(),
bytes,
public_values,
proof,
ProgramProvingReport::new(proving_time),
))
}
fn verify(&self, proof: &[u8]) -> Result<PublicValues, zkVMError> {
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
info!("Verifying proof…");
let proof: ZKMProofWithPublicValues = bincode::deserialize(proof)
.map_err(|err| ZirenError::Verify(VerifyError::Bincode(err)))?;
let proof_kind = proof.kind();
let proof_kind = ZKMProofKind::from(&proof.proof);
if !matches!(proof_kind, ZKMProofKind::Compressed) {
let proof: ZKMProofWithPublicValues = bincode::deserialize(proof.as_bytes())
.map_err(|err| ZirenError::Verify(VerifyError::Bincode(err)))?;
let inner_proof_kind = ZKMProofKind::from(&proof.proof);
if !matches!(
(proof_kind, inner_proof_kind),
(ProofKind::Compressed, ZKMProofKind::Compressed)
| (ProofKind::Groth16, ZKMProofKind::Groth16)
) {
return Err(ZirenError::Verify(VerifyError::InvalidProofKind(
proof_kind,
inner_proof_kind,
)))?;
}
@@ -137,7 +154,7 @@ mod tests {
use test_utils::host::{
BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory,
};
use zkvm_interface::{Compiler, ProverResourceType, zkVM};
use zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
static BASIC_PROGRAM: OnceLock<Vec<u8>> = OnceLock::new();
@@ -202,7 +219,7 @@ mod tests {
BasicProgramIo::invalid_type,
BasicProgramIo::invalid_data,
] {
panic::catch_unwind(|| zkvm.prove(&inputs_gen())).unwrap_err();
panic::catch_unwind(|| zkvm.prove(&inputs_gen(), ProofKind::default())).unwrap_err();
}
}
}

View File

@@ -13,7 +13,7 @@ use std::{
use strum::{EnumIter, IntoEnumIterator};
use tempfile::tempdir;
use tracing::{error, info};
use zkvm_interface::{Proof, ProverResourceType, PublicValues};
use zkvm_interface::{ProverResourceType, PublicValues};
/// Merkle root of ROM trace generated by `cargo-zisk rom-setup`.
pub type RomDigest = [u64; 4];
@@ -379,7 +379,7 @@ impl ZiskServer {
}
/// Send prove request to server and wait for proof to be created.
pub fn prove(&mut self, input: &[u8]) -> Result<(PublicValues, Proof), ZiskError> {
pub fn prove(&mut self, input: &[u8]) -> Result<(PublicValues, Vec<u8>), ZiskError> {
// Prefix that ZisK server will add to the file name of the proof.
// We use constant because the file will be save to a temporary dir,
// so there will be no conflict.

View File

@@ -12,8 +12,8 @@ use std::{
time::Instant,
};
use zkvm_interface::{
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType,
PublicValues, zkVM, zkVMError,
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
ProverResourceType, PublicValues, zkVM, zkVMError,
};
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
@@ -85,7 +85,12 @@ impl zkVM for EreZisk {
fn prove(
&self,
inputs: &Input,
proof_kind: ProofKind,
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
if proof_kind != ProofKind::Compressed {
panic!("Only Compressed proof kind is supported.");
}
let mut server = self.server()?;
let server = server.as_mut().expect("server initialized");
@@ -97,12 +102,16 @@ impl zkVM for EreZisk {
Ok((
public_values,
proof,
Proof::Compressed(proof),
ProgramProvingReport::new(proving_time),
))
}
fn verify(&self, proof: &[u8]) -> Result<PublicValues, zkVMError> {
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError> {
let Proof::Compressed(proof) = proof else {
return Err(zkVMError::other("Only Compressed proof kind is supported."));
};
Ok(self.sdk.verify(proof)?)
}
@@ -142,7 +151,7 @@ mod tests {
use test_utils::host::{
BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory,
};
use zkvm_interface::{Compiler, ProverResourceType, zkVM};
use zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
/// It fails if multiple servers created concurrently using the same port,
/// so we have a lock to avoid that.
@@ -206,7 +215,7 @@ mod tests {
BasicProgramIo::invalid_type(),
BasicProgramIo::invalid_data(),
] {
zkvm.prove(&inputs).unwrap_err();
zkvm.prove(&inputs, ProofKind::default()).unwrap_err();
}
}
}

View File

@@ -1,7 +1,7 @@
use crate::guest::{BasicProgramCore, BasicStruct};
use rand::{Rng, rng};
use std::{fmt::Debug, io::Read, marker::PhantomData, path::PathBuf};
use zkvm_interface::{Input, PublicValues, zkVM};
use zkvm_interface::{Input, ProofKind, PublicValues, zkVM};
fn workspace() -> PathBuf {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
@@ -36,7 +36,7 @@ pub fn run_zkvm_execute(zkvm: &impl zkVM, io: &impl Io) -> PublicValues {
pub fn run_zkvm_prove(zkvm: &impl zkVM, io: &impl Io) -> PublicValues {
let (prover_public_values, proof, _report) = zkvm
.prove(&io.inputs())
.prove(&io.inputs(), ProofKind::default())
.expect("prove should not fail with valid input");
let verifier_public_values = zkvm

View File

@@ -10,6 +10,7 @@ auto_impl.workspace = true
erased-serde.workspace = true
indexmap = { workspace = true, features = ["serde"] }
serde = { workspace = true, features = ["derive"] }
strum = { workspace = true, features = ["derive"] }
thiserror.workspace = true
# Optional dependencies

View File

@@ -1,7 +1,9 @@
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![allow(clippy::double_parens)]
use serde::{Serialize, de::DeserializeOwned};
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use std::{io::Read, path::Path};
use strum::{EnumDiscriminants, EnumIs, EnumTryAs, FromRepr};
use thiserror::Error;
mod input;
@@ -13,7 +15,6 @@ pub use reports::{ProgramExecutionReport, ProgramProvingReport};
mod network;
pub use network::NetworkProverConfig;
#[allow(non_camel_case_types)]
/// Compiler trait for compiling programs into an opaque sequence of bytes.
pub trait Compiler {
type Error: std::error::Error + Send + Sync + 'static;
@@ -97,7 +98,39 @@ pub type PublicValues = Vec<u8>;
/// Proof generated by [`zkVM::prove`], that also includes the [`PublicValues`]
/// for [`zkVM::verify`] to work.
pub type Proof = Vec<u8>;
#[derive(Clone, Debug, Serialize, Deserialize, EnumDiscriminants, EnumIs, EnumTryAs)]
#[strum_discriminants(derive(Default, FromRepr))]
#[strum_discriminants(name(ProofKind))]
pub enum Proof {
/// Compressed proof in contant size regardless of the cycle count.
#[strum_discriminants(default)]
Compressed(Vec<u8>),
/// Groth16 proof that internally verifies a Compressed proof.
Groth16(Vec<u8>),
}
impl Proof {
/// Construct [`Proof`] from [`ProofKind`] and bytes.
pub fn new(proof_kind: ProofKind, bytes: Vec<u8>) -> Self {
match proof_kind {
ProofKind::Compressed => Self::Compressed(bytes),
ProofKind::Groth16 => Self::Groth16(bytes),
}
}
/// Returns [`ProofKind`].
pub fn kind(&self) -> ProofKind {
ProofKind::from(self)
}
/// Returns inner proof as bytes.
pub fn as_bytes(&self) -> &[u8] {
match self {
Self::Compressed(bytes) => bytes,
Self::Groth16(bytes) => bytes,
}
}
}
#[allow(non_camel_case_types)]
#[auto_impl::auto_impl(&, Arc, Box)]
@@ -118,12 +151,13 @@ pub trait zkVM {
fn prove(
&self,
inputs: &Input,
proof_kind: ProofKind,
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError>;
/// 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: &[u8]) -> Result<PublicValues, zkVMError>;
fn verify(&self, proof: &Proof) -> Result<PublicValues, zkVMError>;
/// Returns the name of the zkVM
fn name(&self) -> &'static str;

View File

@@ -34,6 +34,9 @@ ENV PATH=/usr/local/cargo/bin:$PATH
# Copy Risc0 SDK
COPY --from=build_stage /root/.risc0/extensions /root/.risc0/extensions
# Add Docker CLI (only needed for Groth16 proof kind)
COPY --from=docker:cli /usr/local/bin/docker /usr/local/bin/docker
# Copy ere-server
COPY --from=build_stage /ere/bin/ere-server /ere/bin/ere-server