diff --git a/Cargo.lock b/Cargo.lock index 85e4a32..d82de4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,7 +99,7 @@ dependencies = [ "itoa", "k256", "paste", - "rand 0.9.1", + "rand 0.9.2", "ruint", "serde", "tiny-keccak", @@ -1062,19 +1062,6 @@ dependencies = [ "syn 2.0.101", ] -[[package]] -name = "bonsai-sdk" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21055e2f49cbbdbfe9f8f96d597c5527b0c6ab7933341fdc2f147180e48a988e" -dependencies = [ - "duplicate", - "maybe-async", - "reqwest", - "serde", - "thiserror 2.0.12", -] - [[package]] name = "borsh" version = "1.5.7" @@ -2046,17 +2033,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" -[[package]] -name = "duplicate" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97af9b5f014e228b33e77d75ee0e6e87960124f0f4b16337b586a6bec91867b1" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "proc-macro2-diagnostics", -] - [[package]] name = "ecdsa" version = "0.16.9" @@ -2299,6 +2275,7 @@ dependencies = [ "jolt", "jolt-core", "jolt-sdk", + "serde", "tempfile", "test-utils", "thiserror 2.0.12", @@ -2313,6 +2290,7 @@ dependencies = [ "bincode", "build-utils", "nexus-sdk", + "serde", "test-utils", "thiserror 2.0.12", "toml", @@ -2346,6 +2324,7 @@ dependencies = [ "build-utils", "pico-sdk", "pico-vm", + "serde", "test-utils", "thiserror 2.0.12", "zkvm-interface", @@ -2374,6 +2353,7 @@ version = "0.0.11" dependencies = [ "bincode", "build-utils", + "serde", "sp1-sdk", "tempfile", "test-utils", @@ -3973,17 +3953,6 @@ dependencies = [ "rawpointer", ] -[[package]] -name = "maybe-async" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "memchr" version = "2.7.4" @@ -6989,19 +6958,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "proc-macro2-diagnostics" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", - "version_check", - "yansi", -] - [[package]] name = "proptest" version = "1.6.0" @@ -7162,7 +7118,7 @@ dependencies = [ "bytes", "getrandom 0.3.3", "lru-slab", - "rand 0.9.1", + "rand 0.9.2", "ring", "rustc-hash 2.1.1", "rustls", @@ -7245,9 +7201,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -7590,7 +7546,7 @@ dependencies = [ "elf", "lazy_static", "postcard", - "rand 0.9.1", + "rand 0.9.2", "risc0-zkp", "risc0-zkvm-platform", "ruint", @@ -7686,7 +7642,7 @@ dependencies = [ "hex", "lazy-regex", "metal", - "rand 0.9.1", + "rand 0.9.2", "rayon", "risc0-circuit-recursion-sys", "risc0-core", @@ -7730,7 +7686,7 @@ dependencies = [ "num-traits", "paste", "postcard", - "rand 0.9.1", + "rand 0.9.2", "rayon", "ringbuffer", "risc0-binfmt", @@ -7836,7 +7792,7 @@ dependencies = [ "ndarray", "parking_lot", "paste", - "rand 0.9.1", + "rand 0.9.2", "rand_core 0.9.3", "rayon", "risc0-core", @@ -7857,7 +7813,6 @@ dependencies = [ "addr2line", "anyhow", "bincode", - "bonsai-sdk", "borsh", "bytemuck", "bytes", @@ -7874,7 +7829,7 @@ dependencies = [ "num-traits", "object", "prost 0.13.5", - "rand 0.9.1", + "rand 0.9.2", "rayon", "risc0-binfmt", "risc0-build", @@ -8015,7 +7970,7 @@ dependencies = [ "primitive-types", "proptest", "rand 0.8.5", - "rand 0.9.1", + "rand 0.9.2", "rlp", "ruint-macro", "serde", @@ -9401,6 +9356,7 @@ dependencies = [ name = "test-utils" version = "0.0.11" dependencies = [ + "rand 0.9.2", "serde", "zkvm-interface", ] @@ -10072,7 +10028,7 @@ checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ "getrandom 0.3.3", "js-sys", - "rand 0.9.1", + "rand 0.9.2", "wasm-bindgen", ] @@ -10670,12 +10626,6 @@ dependencies = [ "hashlink", ] -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" - [[package]] name = "yoke" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index 05bd479..1935cf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ clap = "4.5.42" dashmap = "6.1.0" erased-serde = "0.4.6" indexmap = "2.10.0" +rand = "0.9.2" serde = "1.0.219" serde_json = "1.0.142" serde_yaml = "0.9.34" diff --git a/crates/ere-cli/src/main.rs b/crates/ere-cli/src/main.rs index 76ef12b..eba9769 100644 --- a/crates/ere-cli/src/main.rs +++ b/crates/ere-cli/src/main.rs @@ -48,6 +48,9 @@ enum Commands { /// Path to the serialized input bytes file #[arg(long)] input_path: PathBuf, + /// Path where the public values will be written + #[arg(long)] + public_values_path: PathBuf, /// Path where the execution report will be written #[arg(long)] report_path: PathBuf, @@ -60,6 +63,9 @@ enum Commands { /// Path to the serialized input bytes file #[arg(long)] input_path: PathBuf, + /// Path where the public values will be written + #[arg(long)] + public_values_path: PathBuf, /// Path where the proof will be written #[arg(long)] proof_path: PathBuf, @@ -78,6 +84,9 @@ enum Commands { /// Path to the proof #[arg(long)] proof_path: PathBuf, + /// Path where the public values will be written + #[arg(long)] + public_values_path: PathBuf, }, } @@ -96,19 +105,29 @@ fn main() -> Result<(), Error> { Commands::Execute { program_path, input_path, + public_values_path, report_path, - } => execute(program_path, input_path, report_path), + } => execute(program_path, input_path, public_values_path, report_path), Commands::Prove { program_path, input_path, + public_values_path, proof_path, report_path, resource, - } => prove(program_path, resource, input_path, proof_path, report_path), + } => prove( + program_path, + resource, + input_path, + public_values_path, + proof_path, + report_path, + ), Commands::Verify { program_path, proof_path, - } => verify(program_path, proof_path), + public_values_path, + } => verify(program_path, proof_path, public_values_path), } } @@ -145,32 +164,61 @@ fn prove( program_path: PathBuf, resource: ProverResourceType, input_path: PathBuf, + public_values_path: PathBuf, proof_path: PathBuf, report_path: PathBuf, ) -> Result<(), Error> { let zkvm = construct_zkvm(program_path, resource)?; let input = serde::read_input(&input_path)?; - let (proof, report) = zkvm.prove(&input).with_context(|| "Failed to prove")?; + let (public_values, proof, report) = zkvm.prove(&input).with_context(|| "Failed to prove")?; + fs::write(&public_values_path, &public_values).with_context(|| { + format!( + "Failed to write public values to {}", + public_values_path.display() + ) + })?; fs::write(&proof_path, proof) .with_context(|| format!("Failed to write proof to {}", proof_path.display()))?; serde::write(&report_path, &report, "report")?; Ok(()) } -fn execute(program_path: PathBuf, input_path: PathBuf, report_path: PathBuf) -> Result<(), Error> { +fn execute( + program_path: PathBuf, + input_path: PathBuf, + public_values_path: PathBuf, + report_path: PathBuf, +) -> Result<(), Error> { let zkvm = construct_zkvm(program_path, ProverResourceType::Cpu)?; let input = serde::read_input(&input_path)?; - let report = zkvm.execute(&input).with_context(|| "Failed to execute")?; + let (public_values, report) = zkvm.execute(&input).with_context(|| "Failed to execute")?; + fs::write(&public_values_path, &public_values).with_context(|| { + format!( + "Failed to write public values to {}", + public_values_path.display() + ) + })?; serde::write(&report_path, &report, "report")?; Ok(()) } -fn verify(program_path: PathBuf, proof_path: PathBuf) -> Result<(), Error> { +fn verify( + program_path: PathBuf, + proof_path: PathBuf, + public_values_path: PathBuf, +) -> Result<(), Error> { let zkvm = construct_zkvm(program_path, ProverResourceType::Cpu)?; let proof = fs::read(&proof_path) .with_context(|| format!("Failed to read proof from {}", proof_path.display()))?; - zkvm.verify(&proof) + let public_values = zkvm + .verify(&proof) .with_context(|| "Failed to verify proof")?; + fs::write(&public_values_path, &public_values).with_context(|| { + format!( + "Failed to write public values to {}", + public_values_path.display() + ) + })?; Ok(()) } diff --git a/crates/ere-dockerized/src/lib.rs b/crates/ere-dockerized/src/lib.rs index 3937005..27d6a26 100644 --- a/crates/ere-dockerized/src/lib.rs +++ b/crates/ere-dockerized/src/lib.rs @@ -62,18 +62,20 @@ use crate::{ docker::{DockerBuildCmd, DockerRunCmd, docker_image_exists}, error::{CommonError, CompileError, DockerizedError, ExecuteError, ProveError, VerifyError}, }; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; use std::{ env, fmt::{self, Display, Formatter}, - fs, iter, + fs, + io::Read, + iter, path::{Path, PathBuf}, str::FromStr, }; use tempfile::TempDir; use zkvm_interface::{ - Compiler, Input, ProgramExecutionReport, ProgramProvingReport, ProverResourceType, zkVM, - zkVMError, + Compiler, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType, + PublicValues, zkVM, zkVMError, }; include!(concat!(env!("OUT_DIR"), "/crate_version.rs")); @@ -288,7 +290,7 @@ impl EreDockerizedzkVM { } impl zkVM for EreDockerizedzkVM { - fn execute(&self, inputs: &Input) -> Result { + fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> { let tempdir = TempDir::new() .map_err(|err| CommonError::io(err, "Failed to create temporary directory")) .map_err(|err| DockerizedError::Execute(ExecuteError::Common(err)))?; @@ -317,12 +319,17 @@ impl zkVM for EreDockerizedzkVM { "/workspace/program", "--input-path", "/workspace/input", + "--public-values-path", + "/workspace/public_values", "--report-path", "/workspace/report", ]) .map_err(CommonError::DockerRunCmd) .map_err(|err| DockerizedError::Execute(ExecuteError::Common(err)))?; + let public_values = fs::read(tempdir.path().join("public_values")) + .map_err(|err| CommonError::io(err, "Failed to read public_values")) + .map_err(|err| DockerizedError::Execute(ExecuteError::Common(err)))?; let report_bytes = fs::read(tempdir.path().join("report")) .map_err(|err| CommonError::io(err, "Failed to read report")) .map_err(|err| DockerizedError::Execute(ExecuteError::Common(err)))?; @@ -330,10 +337,13 @@ impl zkVM for EreDockerizedzkVM { let report = bincode::deserialize(&report_bytes) .map_err(|err| CommonError::serilization(err, "Failed to deserialize report")) .map_err(|err| DockerizedError::Prove(ProveError::Common(err)))?; - Ok(report) + Ok((public_values, report)) } - fn prove(&self, inputs: &Input) -> Result<(Vec, ProgramProvingReport), zkVMError> { + fn prove( + &self, + inputs: &Input, + ) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> { let tempdir = TempDir::new() .map_err(|err| CommonError::io(err, "Failed to create temporary directory")) .map_err(|err| DockerizedError::Prove(ProveError::Common(err)))?; @@ -391,6 +401,8 @@ impl zkVM for EreDockerizedzkVM { "/workspace/program", "--input-path", "/workspace/input", + "--public-values-path", + "/workspace/public_values", "--proof-path", "/workspace/proof", "--report-path", @@ -401,6 +413,9 @@ impl zkVM for EreDockerizedzkVM { .map_err(CommonError::DockerRunCmd) .map_err(|err| DockerizedError::Prove(ProveError::Common(err)))?; + let public_values = fs::read(tempdir.path().join("public_values")) + .map_err(|err| CommonError::io(err, "Failed to read public_values")) + .map_err(|err| DockerizedError::Prove(ProveError::Common(err)))?; let proof = fs::read(tempdir.path().join("proof")) .map_err(|err| CommonError::io(err, "Failed to read proof")) .map_err(|err| DockerizedError::Prove(ProveError::Common(err)))?; @@ -410,10 +425,10 @@ impl zkVM for EreDockerizedzkVM { let report = bincode::deserialize(&report_bytes) .map_err(|err| CommonError::serilization(err, "Failed to deserialize report")) .map_err(|err| DockerizedError::Prove(ProveError::Common(err)))?; - Ok((proof, report)) + Ok((public_values, proof, report)) } - fn verify(&self, proof: &[u8]) -> Result<(), zkVMError> { + fn verify(&self, proof: &[u8]) -> Result { let tempdir = TempDir::new() .map_err(|err| CommonError::io(err, "Failed to create temporary directory")) .map_err(|err| DockerizedError::Verify(VerifyError::Common(err)))?; @@ -436,11 +451,17 @@ impl zkVM for EreDockerizedzkVM { "/workspace/program", "--proof-path", "/workspace/proof", + "--public-values-path", + "/workspace/public_values", ]) .map_err(CommonError::DockerRunCmd) .map_err(|err| DockerizedError::Verify(VerifyError::Common(err)))?; - Ok(()) + let public_values = fs::read(tempdir.path().join("public_values")) + .map_err(|err| CommonError::io(err, "Failed to read public_values")) + .map_err(|err| DockerizedError::Verify(VerifyError::Common(err)))?; + + Ok(public_values) } fn name(&self) -> &'static str { @@ -450,6 +471,10 @@ impl zkVM for EreDockerizedzkVM { fn sdk_version(&self) -> &'static str { self.zkvm.sdk_version() } + + fn deserialize_from(&self, _reader: R) -> Result { + todo!() + } } fn workspace_dir() -> PathBuf { @@ -463,7 +488,7 @@ fn workspace_dir() -> PathBuf { mod test { use crate::{EreDockerizedCompiler, EreDockerizedzkVM, ErezkVM, workspace_dir}; use test_utils::host::{ - BasicProgramInputGen, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, + BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, }; use zkvm_interface::{Compiler, ProverResourceType}; @@ -483,9 +508,9 @@ mod test { let zkvm = EreDockerizedzkVM::new(zkvm, program, ProverResourceType::Cpu).unwrap(); - let inputs = BasicProgramInputGen::valid(); - run_zkvm_execute(&zkvm, &inputs); - run_zkvm_prove(&zkvm, &inputs); + let io = BasicProgramIo::valid(); + run_zkvm_execute(&zkvm, &io); + run_zkvm_prove(&zkvm, &io); } #[test] @@ -499,9 +524,9 @@ mod test { let zkvm = EreDockerizedzkVM::new(zkvm, program, ProverResourceType::Cpu).unwrap(); - let inputs = BasicProgramInputGen::valid(); - run_zkvm_execute(&zkvm, &inputs); - run_zkvm_prove(&zkvm, &inputs); + let io = BasicProgramIo::valid(); + run_zkvm_execute(&zkvm, &io); + run_zkvm_prove(&zkvm, &io); } #[test] @@ -515,9 +540,9 @@ mod test { let zkvm = EreDockerizedzkVM::new(zkvm, program, ProverResourceType::Cpu).unwrap(); - let inputs = BasicProgramInputGen::valid(); - run_zkvm_execute(&zkvm, &inputs); - run_zkvm_prove(&zkvm, &inputs); + let io = BasicProgramIo::valid(); + run_zkvm_execute(&zkvm, &io); + run_zkvm_prove(&zkvm, &io); } #[test] @@ -531,8 +556,8 @@ mod test { let zkvm = EreDockerizedzkVM::new(zkvm, program, ProverResourceType::Cpu).unwrap(); - let inputs = BasicProgramInputGen::valid(); - run_zkvm_execute(&zkvm, &inputs); - run_zkvm_prove(&zkvm, &inputs); + let io = BasicProgramIo::valid(); + run_zkvm_execute(&zkvm, &io); + run_zkvm_prove(&zkvm, &io); } } diff --git a/crates/ere-jolt/Cargo.toml b/crates/ere-jolt/Cargo.toml index 7d2ddc4..4f44b43 100644 --- a/crates/ere-jolt/Cargo.toml +++ b/crates/ere-jolt/Cargo.toml @@ -6,6 +6,7 @@ rust-version.workspace = true license.workspace = true [dependencies] +serde.workspace = true tempfile.workspace = true thiserror.workspace = true toml.workspace = true diff --git a/crates/ere-jolt/src/lib.rs b/crates/ere-jolt/src/lib.rs index e7128ce..82bc40b 100644 --- a/crates/ere-jolt/src/lib.rs +++ b/crates/ere-jolt/src/lib.rs @@ -9,11 +9,17 @@ use jolt::{JoltHyperKZGProof, JoltProverPreprocessing, JoltVerifierPreprocessing use jolt_core::host::Program; use jolt_methods::{preprocess_prover, preprocess_verifier, prove_generic, verify_generic}; use jolt_sdk::host::DEFAULT_TARGET_DIR; -use std::{env::set_current_dir, fs, io::Cursor, path::Path}; +use serde::de::DeserializeOwned; +use std::{ + env::set_current_dir, + fs, + io::{Cursor, Read}, + path::Path, +}; use tempfile::TempDir; use zkvm_interface::{ - Compiler, Input, ProgramExecutionReport, ProgramProvingReport, ProverResourceType, zkVM, - zkVMError, + Compiler, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType, + PublicValues, zkVM, zkVMError, }; include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); @@ -87,7 +93,7 @@ impl zkVM for EreJolt { fn execute( &self, _inputs: &Input, - ) -> Result { + ) -> Result<(PublicValues, zkvm_interface::ProgramExecutionReport), zkVMError> { let (_tempdir, program) = program(&self.elf)?; // TODO: Check how to pass private input to jolt, issue for tracking: @@ -95,13 +101,16 @@ impl zkVM for EreJolt { let summary = program.clone().trace_analyze::(&[]); let trace_len = summary.trace_len(); - Ok(ProgramExecutionReport::new(trace_len as u64)) + // TODO: Public values + let public_values = Vec::new(); + + Ok((public_values, ProgramExecutionReport::new(trace_len as u64))) } fn prove( &self, inputs: &Input, - ) -> Result<(Vec, zkvm_interface::ProgramProvingReport), zkVMError> { + ) -> Result<(PublicValues, Proof, zkvm_interface::ProgramProvingReport), zkVMError> { let (_tempdir, program) = program(&self.elf)?; let now = std::time::Instant::now(); @@ -113,16 +122,26 @@ impl zkVM for EreJolt { .serialize_compressed(&mut proof_bytes) .map_err(|err| JoltError::Prove(ProveError::Serialization(err)))?; - Ok((proof_bytes, ProgramProvingReport::new(elapsed))) + // TODO: Public values + let public_values = Vec::new(); + + Ok(( + public_values, + proof_bytes, + ProgramProvingReport::new(elapsed), + )) } - fn verify(&self, proof_bytes: &[u8]) -> Result<(), zkVMError> { + fn verify(&self, proof_bytes: &[u8]) -> Result { let proof = EreJoltProof::deserialize_compressed(&mut Cursor::new(proof_bytes)) .map_err(|err| JoltError::Verify(VerifyError::Serialization(err)))?; verify_generic(proof, self.verifier_preprocessing.clone()).map_err(JoltError::Verify)?; - Ok(()) + // TODO: Public values + let public_values = Vec::new(); + + Ok(public_values) } fn name(&self) -> &'static str { @@ -132,6 +151,10 @@ impl zkVM for EreJolt { fn sdk_version(&self) -> &'static str { SDK_VERSION } + + fn deserialize_from(&self, _reader: R) -> Result { + todo!() + } } /// Create `jolt::host::Program` by storing the compiled `elf` to a temporary diff --git a/crates/ere-nexus/Cargo.toml b/crates/ere-nexus/Cargo.toml index 46fe998..d47f532 100644 --- a/crates/ere-nexus/Cargo.toml +++ b/crates/ere-nexus/Cargo.toml @@ -7,6 +7,7 @@ license.workspace = true [dependencies] bincode.workspace = true +serde.workspace = true thiserror.workspace = true toml.workspace = true tracing.workspace = true diff --git a/crates/ere-nexus/src/lib.rs b/crates/ere-nexus/src/lib.rs index 6390feb..bec5e4a 100644 --- a/crates/ere-nexus/src/lib.rs +++ b/crates/ere-nexus/src/lib.rs @@ -1,17 +1,19 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![allow(clippy::uninlined_format_args)] +use std::io::Read; use std::path::{Path, PathBuf}; use std::time::Instant; use nexus_sdk::compile::cargo::CargoPackager; use nexus_sdk::compile::{Compile, Compiler as NexusCompiler}; -use nexus_sdk::stwo::seq::{Proof, Stwo}; +use nexus_sdk::stwo::seq::Stwo; use nexus_sdk::{Local, Prover, Verifiable}; +use serde::de::DeserializeOwned; use tracing::info; use zkvm_interface::{ - Compiler, Input, ProgramExecutionReport, ProgramProvingReport, ProverResourceType, zkVM, - zkVMError, + Compiler, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProverResourceType, + PublicValues, zkVM, zkVMError, }; include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); @@ -69,19 +71,22 @@ impl zkVM for EreNexus { fn execute( &self, _inputs: &Input, - ) -> Result { + ) -> Result<(PublicValues, zkvm_interface::ProgramExecutionReport), zkVMError> { // TODO: Serialize inputs by `postcard` and make sure there is no double serailization. // Issue for tracking: https://github.com/eth-act/ere/issues/63. // TODO: Execute and get cycle count - Ok(ProgramExecutionReport::default()) + // TODO: Public values + let public_values = Vec::new(); + + Ok((public_values, ProgramExecutionReport::default())) } fn prove( &self, _inputs: &Input, - ) -> Result<(Vec, zkvm_interface::ProgramProvingReport), zkVMError> { + ) -> Result<(PublicValues, Proof, zkvm_interface::ProgramProvingReport), zkVMError> { let prover: Stwo = Stwo::new_from_file(&self.program.to_string_lossy().to_string()) .map_err(|e| NexusError::Prove(ProveError::Client(e.into()))) .map_err(zkVMError::from)?; @@ -99,13 +104,16 @@ impl zkVM for EreNexus { let bytes = bincode::serialize(&proof) .map_err(|err| NexusError::Prove(ProveError::Bincode(err)))?; - Ok((bytes, ProgramProvingReport::new(elapsed))) + // TODO: Public values + let public_values = Vec::new(); + + Ok((public_values, bytes, ProgramProvingReport::new(elapsed))) } - fn verify(&self, proof: &[u8]) -> Result<(), zkVMError> { + fn verify(&self, proof: &[u8]) -> Result { info!("Verifying proof..."); - let proof: Proof = bincode::deserialize(proof) + let proof: nexus_sdk::stwo::seq::Proof = bincode::deserialize(proof) .map_err(|err| NexusError::Verify(VerifyError::Bincode(err)))?; let prover: Stwo = Stwo::new_from_file(&self.program.to_string_lossy().to_string()) @@ -125,7 +133,11 @@ impl zkVM for EreNexus { .map_err(zkVMError::from)?; info!("Verify Succeeded!"); - Ok(()) + + // TODO: Public values + let public_values = Vec::new(); + + Ok(public_values) } fn name(&self) -> &'static str { @@ -135,6 +147,10 @@ impl zkVM for EreNexus { fn sdk_version(&self) -> &'static str { SDK_VERSION } + + fn deserialize_from(&self, _reader: R) -> Result { + todo!() + } } #[cfg(test)] diff --git a/crates/ere-openvm/src/lib.rs b/crates/ere-openvm/src/lib.rs index 75d9191..a972567 100644 --- a/crates/ere-openvm/src/lib.rs +++ b/crates/ere-openvm/src/lib.rs @@ -13,11 +13,11 @@ use openvm_sdk::{ }; use openvm_stark_sdk::config::FriParameters; use openvm_transpiler::{elf::Elf, openvm_platform::memory::MEM_SIZE}; -use serde::{Deserialize, Serialize}; -use std::{fs, path::Path, sync::Arc, time::Instant}; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; +use std::{fs, io::Read, path::Path, sync::Arc, time::Instant}; use zkvm_interface::{ - Compiler, Input, InputItem, ProgramExecutionReport, ProgramProvingReport, ProverResourceType, - zkVM, zkVMError, + Compiler, Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, + ProverResourceType, PublicValues, zkVM, zkVMError, }; include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); @@ -128,7 +128,10 @@ impl EreOpenVM { } impl zkVM for EreOpenVM { - fn execute(&self, inputs: &Input) -> Result { + fn execute( + &self, + inputs: &Input, + ) -> Result<(PublicValues, zkvm_interface::ProgramExecutionReport), zkVMError> { let sdk = self .sdk() .map_err(|e| OpenVMError::from(ExecuteError::from(e)))?; @@ -141,16 +144,22 @@ impl zkVM for EreOpenVM { .execute(self.app_exe.clone(), stdin) .map_err(|e| OpenVMError::from(ExecuteError::Execute(e)))?; - Ok(ProgramExecutionReport { - execution_duration: start.elapsed(), - ..Default::default() - }) + // TODO: Public values + let public_values = Vec::new(); + + Ok(( + public_values, + ProgramExecutionReport { + execution_duration: start.elapsed(), + ..Default::default() + }, + )) } fn prove( &self, inputs: &Input, - ) -> Result<(Vec, zkvm_interface::ProgramProvingReport), zkVMError> { + ) -> Result<(PublicValues, Proof, zkvm_interface::ProgramProvingReport), zkVMError> { let sdk = self .sdk() .map_err(|e| OpenVMError::from(ProveError::from(e)))?; @@ -175,17 +184,27 @@ impl zkVM for EreOpenVM { .encode_to_vec() .map_err(|e| OpenVMError::from(ProveError::SerializeProof(e)))?; - Ok((proof_bytes, ProgramProvingReport::new(elapsed))) + // TODO: Public values + let public_values = Vec::new(); + + Ok(( + public_values, + proof_bytes, + ProgramProvingReport::new(elapsed), + )) } - fn verify(&self, mut proof: &[u8]) -> Result<(), zkVMError> { + fn verify(&self, mut proof: &[u8]) -> Result { let proof = VmStarkProof::::decode(&mut proof) .map_err(|e| OpenVMError::from(VerifyError::DeserializeProof(e)))?; Sdk::verify_proof(&self.agg_vk, self.app_commit, &proof) .map_err(|e| OpenVMError::Verify(VerifyError::Verify(e)))?; - Ok(()) + // TODO: Public values + let public_values = Vec::new(); + + Ok(public_values) } fn name(&self) -> &'static str { @@ -195,6 +214,10 @@ impl zkVM for EreOpenVM { fn sdk_version(&self) -> &'static str { SDK_VERSION } + + fn deserialize_from(&self, _reader: R) -> Result { + todo!() + } } fn serialize_inputs(stdin: &mut StdIn, inputs: &Input) { @@ -213,7 +236,7 @@ mod tests { use super::*; use std::sync::OnceLock; use test_utils::host::{ - BasicProgramInputGen, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, + BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, }; fn basic_program() -> OpenVMProgram { @@ -242,8 +265,8 @@ mod tests { fn test_execute() { let zkvm = basic_program_ere_openvm(); - let inputs = BasicProgramInputGen::valid(); - run_zkvm_execute(&zkvm, &inputs); + let io = BasicProgramIo::valid(); + run_zkvm_execute(&zkvm, &io); } #[test] @@ -251,9 +274,9 @@ mod tests { let zkvm = basic_program_ere_openvm(); for inputs in [ - BasicProgramInputGen::empty(), - BasicProgramInputGen::invalid_string(), - BasicProgramInputGen::invalid_type(), + BasicProgramIo::empty(), + BasicProgramIo::invalid_type(), + BasicProgramIo::invalid_data(), ] { zkvm.execute(&inputs).unwrap_err(); } @@ -263,8 +286,8 @@ mod tests { fn test_prove() { let zkvm = basic_program_ere_openvm(); - let inputs = BasicProgramInputGen::valid(); - run_zkvm_prove(&zkvm, &inputs); + let io = BasicProgramIo::valid(); + run_zkvm_prove(&zkvm, &io); } #[test] @@ -272,9 +295,9 @@ mod tests { let zkvm = basic_program_ere_openvm(); for inputs in [ - BasicProgramInputGen::empty(), - BasicProgramInputGen::invalid_string(), - BasicProgramInputGen::invalid_type(), + BasicProgramIo::empty(), + BasicProgramIo::invalid_type(), + BasicProgramIo::invalid_data(), ] { zkvm.prove(&inputs).unwrap_err(); } diff --git a/crates/ere-pico/Cargo.toml b/crates/ere-pico/Cargo.toml index 4630042..5b1bceb 100644 --- a/crates/ere-pico/Cargo.toml +++ b/crates/ere-pico/Cargo.toml @@ -7,6 +7,7 @@ license.workspace = true [dependencies] bincode.workspace = true +serde.workspace = true thiserror.workspace = true # Pico dependencies diff --git a/crates/ere-pico/src/lib.rs b/crates/ere-pico/src/lib.rs index 0b550ac..926b652 100644 --- a/crates/ere-pico/src/lib.rs +++ b/crates/ere-pico/src/lib.rs @@ -2,10 +2,11 @@ use pico_sdk::client::DefaultProverClient; use pico_vm::emulator::stdin::EmulatorStdinBuilder; -use std::{path::Path, process::Command, time::Instant}; +use serde::de::DeserializeOwned; +use std::{io::Read, path::Path, process::Command, time::Instant}; use zkvm_interface::{ - Compiler, Input, InputItem, ProgramExecutionReport, ProgramProvingReport, ProverResourceType, - zkVM, zkVMError, + Compiler, Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, + ProverResourceType, PublicValues, zkVM, zkVMError, }; include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); @@ -69,7 +70,7 @@ impl ErePico { } } impl zkVM for ErePico { - fn execute(&self, inputs: &Input) -> Result { + fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> { let client = DefaultProverClient::new(&self.program); let mut stdin = client.new_stdin_builder(); @@ -78,17 +79,23 @@ impl zkVM for ErePico { let start = Instant::now(); let emulation_result = client.emulate(stdin); - Ok(ProgramExecutionReport { - total_num_cycles: emulation_result.0, - execution_duration: start.elapsed(), - ..Default::default() - }) + // TODO: Public values + let public_values = Vec::new(); + + Ok(( + public_values, + ProgramExecutionReport { + total_num_cycles: emulation_result.0, + execution_duration: start.elapsed(), + ..Default::default() + }, + )) } fn prove( &self, inputs: &Input, - ) -> Result<(Vec, zkvm_interface::ProgramProvingReport), zkVMError> { + ) -> Result<(PublicValues, Proof, zkvm_interface::ProgramProvingReport), zkVMError> { let client = DefaultProverClient::new(&self.program); let mut stdin = client.new_stdin_builder(); @@ -113,13 +120,23 @@ impl zkVM for ErePico { bincode::serialize_into(&mut proof_serialized, p).unwrap(); } - Ok((proof_serialized, ProgramProvingReport::new(elapsed))) + // TODO: Public values + let public_values = Vec::new(); + + Ok(( + public_values, + proof_serialized, + ProgramProvingReport::new(elapsed), + )) } - fn verify(&self, _proof: &[u8]) -> Result<(), zkVMError> { + fn verify(&self, _proof: &[u8]) -> Result { let client = DefaultProverClient::new(&self.program); let _vk = client.riscv_vk(); - todo!("Verification method missing from sdk") + // TODO: Verification method missing from sdk + // TODO: Public values + let public_values = Vec::new(); + Ok(public_values) } fn name(&self) -> &'static str { @@ -129,6 +146,10 @@ impl zkVM for ErePico { fn sdk_version(&self) -> &'static str { SDK_VERSION } + + fn deserialize_from(&self, _reader: R) -> Result { + todo!() + } } fn serialize_inputs(stdin: &mut EmulatorStdinBuilder>, inputs: &Input) { @@ -146,7 +167,7 @@ fn serialize_inputs(stdin: &mut EmulatorStdinBuilder>, inputs: &Input) { mod tests { use super::*; use std::{panic, sync::OnceLock}; - use test_utils::host::{BasicProgramInputGen, run_zkvm_execute, testing_guest_directory}; + use test_utils::host::{BasicProgramIo, run_zkvm_execute, testing_guest_directory}; static BASIC_PRORGAM: OnceLock> = OnceLock::new(); @@ -171,8 +192,8 @@ mod tests { let program = basic_program(); let zkvm = ErePico::new(program, ProverResourceType::Cpu); - let inputs = BasicProgramInputGen::valid(); - run_zkvm_execute(&zkvm, &inputs); + let io = BasicProgramIo::valid(); + run_zkvm_execute(&zkvm, &io); } #[test] @@ -181,9 +202,9 @@ mod tests { let zkvm = ErePico::new(program, ProverResourceType::Cpu); for inputs_gen in [ - BasicProgramInputGen::empty, - BasicProgramInputGen::invalid_string, - BasicProgramInputGen::invalid_type, + BasicProgramIo::empty, + BasicProgramIo::invalid_type, + BasicProgramIo::invalid_data, ] { panic::catch_unwind(|| zkvm.execute(&inputs_gen()).unwrap_err()).unwrap_err(); } diff --git a/crates/ere-risc0/Cargo.toml b/crates/ere-risc0/Cargo.toml index d38338b..7bdf33a 100644 --- a/crates/ere-risc0/Cargo.toml +++ b/crates/ere-risc0/Cargo.toml @@ -15,7 +15,7 @@ tracing.workspace = true # Risc0 dependencies risc0-build = { workspace = true, features = ["unstable"] } -risc0-zkvm = { workspace = true, default-features = true, features = ["unstable"] } +risc0-zkvm = { workspace = true, features = ["client", "unstable"] } # Local dependencies zkvm-interface.workspace = true diff --git a/crates/ere-risc0/src/lib.rs b/crates/ere-risc0/src/lib.rs index db93808..e74a787 100644 --- a/crates/ere-risc0/src/lib.rs +++ b/crates/ere-risc0/src/lib.rs @@ -9,11 +9,11 @@ use risc0_zkvm::{ DEFAULT_MAX_PO2, DefaultProver, ExecutorEnv, ExecutorEnvBuilder, ExternalProver, InnerReceipt, Journal, ProverOpts, Receipt, ReceiptClaim, SuccinctReceipt, default_executor, default_prover, }; -use serde::{Deserialize, Serialize}; -use std::{env, ops::RangeInclusive, path::Path, rc::Rc, time::Instant}; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; +use std::{env, io::Read, ops::RangeInclusive, path::Path, rc::Rc, time::Instant}; use zkvm_interface::{ - Compiler, Input, InputItem, ProgramExecutionReport, ProgramProvingReport, ProverResourceType, - zkVM, zkVMError, + Compiler, Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, + ProverResourceType, PublicValues, zkVM, zkVMError, }; include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); @@ -130,7 +130,7 @@ impl EreRisc0 { } impl zkVM for EreRisc0 { - fn execute(&self, inputs: &Input) -> Result { + fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> { let executor = default_executor(); let mut env = ExecutorEnv::builder(); serialize_inputs(&mut env, inputs).map_err(zkVMError::other)?; @@ -140,14 +140,24 @@ impl zkVM for EreRisc0 { let session_info = executor .execute(env, &self.program.elf) .map_err(zkVMError::other)?; - Ok(ProgramExecutionReport { - total_num_cycles: session_info.cycles() as u64, - execution_duration: start.elapsed(), - ..Default::default() - }) + + // TODO: Public values + let public_values = Vec::new(); + + Ok(( + public_values, + ProgramExecutionReport { + total_num_cycles: session_info.cycles() as u64, + execution_duration: start.elapsed(), + ..Default::default() + }, + )) } - fn prove(&self, inputs: &Input) -> Result<(Vec, ProgramProvingReport), zkVMError> { + fn prove( + &self, + inputs: &Input, + ) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> { let prover = match self.resource { ProverResourceType::Cpu => Rc::new(ExternalProver::new("ipc", "r0vm")), ProverResourceType::Gpu => { @@ -188,17 +198,30 @@ impl zkVM for EreRisc0 { let proof = borsh::to_vec(&Risc0ProofWithPublicValues::from(prove_info.receipt)) .map_err(zkVMError::other)?; - Ok((proof, ProgramProvingReport::new(proving_time))) + + // TODO: Public values + let public_values = Vec::new(); + + Ok(( + public_values, + proof, + ProgramProvingReport::new(proving_time), + )) } - fn verify(&self, proof: &[u8]) -> Result<(), zkVMError> { + fn verify(&self, proof: &[u8]) -> Result { let receipt: Receipt = borsh::from_slice::(proof) .map_err(zkVMError::other)? .into(); receipt .verify(self.program.image_id) - .map_err(zkVMError::other) + .map_err(zkVMError::other)?; + + // TODO: Public values + let public_values = Vec::new(); + + Ok(public_values) } fn name(&self) -> &'static str { @@ -208,6 +231,10 @@ impl zkVM for EreRisc0 { fn sdk_version(&self) -> &'static str { SDK_VERSION } + + fn deserialize_from(&self, _reader: R) -> Result { + todo!() + } } fn serialize_inputs(env: &mut ExecutorEnvBuilder, inputs: &Input) -> Result<(), anyhow::Error> { @@ -235,7 +262,7 @@ mod tests { use super::*; use std::sync::OnceLock; use test_utils::host::{ - BasicProgramInputGen, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, + BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, }; static BASIC_PRORGAM: OnceLock = OnceLock::new(); @@ -255,8 +282,8 @@ mod tests { let program = basic_program(); let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap(); - let inputs = BasicProgramInputGen::valid(); - run_zkvm_execute(&zkvm, &inputs); + let io = BasicProgramIo::valid(); + run_zkvm_execute(&zkvm, &io); } #[test] @@ -265,9 +292,9 @@ mod tests { let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap(); for inputs in [ - BasicProgramInputGen::empty(), - BasicProgramInputGen::invalid_string(), - BasicProgramInputGen::invalid_type(), + BasicProgramIo::empty(), + BasicProgramIo::invalid_type(), + BasicProgramIo::invalid_data(), ] { zkvm.execute(&inputs).unwrap_err(); } @@ -278,8 +305,8 @@ mod tests { let program = basic_program(); let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap(); - let inputs = BasicProgramInputGen::valid(); - run_zkvm_prove(&zkvm, &inputs); + let io = BasicProgramIo::valid(); + run_zkvm_prove(&zkvm, &io); } #[test] @@ -288,9 +315,9 @@ mod tests { let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap(); for inputs in [ - BasicProgramInputGen::empty(), - BasicProgramInputGen::invalid_string(), - BasicProgramInputGen::invalid_type(), + BasicProgramIo::empty(), + BasicProgramIo::invalid_type(), + BasicProgramIo::invalid_data(), ] { zkvm.prove(&inputs).unwrap_err(); } diff --git a/crates/ere-sp1/Cargo.toml b/crates/ere-sp1/Cargo.toml index 827edb7..05b366f 100644 --- a/crates/ere-sp1/Cargo.toml +++ b/crates/ere-sp1/Cargo.toml @@ -7,6 +7,7 @@ version.workspace = true [dependencies] bincode.workspace = true +serde.workspace = true tempfile.workspace = true thiserror.workspace = true toml.workspace = true diff --git a/crates/ere-sp1/src/compile_stock_rust.rs b/crates/ere-sp1/src/compile_stock_rust.rs index ab6ece2..1b789d1 100644 --- a/crates/ere-sp1/src/compile_stock_rust.rs +++ b/crates/ere-sp1/src/compile_stock_rust.rs @@ -18,7 +18,7 @@ pub fn stock_rust_compile( ); let target_name = "riscv32ima-unknown-none-elf"; - let plus_toolchain = format!("+{}", toolchain); + let plus_toolchain = format!("+{toolchain}"); let args = [ plus_toolchain.as_str(), diff --git a/crates/ere-sp1/src/lib.rs b/crates/ere-sp1/src/lib.rs index 7ef53dd..e067dc9 100644 --- a/crates/ere-sp1/src/lib.rs +++ b/crates/ere-sp1/src/lib.rs @@ -1,7 +1,8 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] -use std::{path::Path, time::Instant}; +use std::{io::Read, path::Path, time::Instant}; +use serde::de::DeserializeOwned; use sp1_sdk::{ CpuProver, CudaProver, NetworkProver, Prover, ProverClient, SP1ProofWithPublicValues, SP1ProvingKey, SP1Stdin, SP1VerifyingKey, @@ -9,7 +10,7 @@ use sp1_sdk::{ use tracing::info; use zkvm_interface::{ Compiler, Input, InputItem, NetworkProverConfig, ProgramExecutionReport, ProgramProvingReport, - ProverResourceType, zkVM, zkVMError, + Proof, ProverResourceType, PublicValues, zkVM, zkVMError, }; include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); @@ -163,24 +164,31 @@ impl EreSP1 { } impl zkVM for EreSP1 { - fn execute(&self, inputs: &Input) -> Result { + fn execute( + &self, + inputs: &Input, + ) -> Result<(PublicValues, zkvm_interface::ProgramExecutionReport), zkVMError> { let mut stdin = SP1Stdin::new(); serialize_inputs(&mut stdin, inputs); let client = Self::create_client(&self.resource); let start = Instant::now(); - let (_, exec_report) = client.execute(&self.program, &stdin)?; - Ok(ProgramExecutionReport { - total_num_cycles: exec_report.total_instruction_count(), - region_cycles: exec_report.cycle_tracker.into_iter().collect(), - execution_duration: start.elapsed(), - }) + let (public_values, exec_report) = client.execute(&self.program, &stdin)?; + + Ok(( + public_values.to_vec(), + ProgramExecutionReport { + total_num_cycles: exec_report.total_instruction_count(), + region_cycles: exec_report.cycle_tracker.into_iter().collect(), + execution_duration: start.elapsed(), + }, + )) } fn prove( &self, inputs: &zkvm_interface::Input, - ) -> Result<(Vec, zkvm_interface::ProgramProvingReport), zkVMError> { + ) -> Result<(PublicValues, Proof, zkvm_interface::ProgramProvingReport), zkVMError> { info!("Generating proof…"); let mut stdin = SP1Stdin::new(); @@ -194,17 +202,25 @@ impl zkVM for EreSP1 { let bytes = bincode::serialize(&proof_with_inputs) .map_err(|err| SP1Error::Prove(ProveError::Bincode(err)))?; - Ok((bytes, ProgramProvingReport::new(proving_time))) + Ok(( + proof_with_inputs.public_values.to_vec(), + bytes, + ProgramProvingReport::new(proving_time), + )) } - fn verify(&self, proof: &[u8]) -> Result<(), zkVMError> { + fn verify(&self, proof: &[u8]) -> Result { info!("Verifying proof…"); let proof: SP1ProofWithPublicValues = bincode::deserialize(proof) .map_err(|err| SP1Error::Verify(VerifyError::Bincode(err)))?; let client = Self::create_client(&self.resource); - client.verify(&proof, &self.vk).map_err(zkVMError::from) + client.verify(&proof, &self.vk).map_err(zkVMError::from)?; + + let public_values_bytes = proof.public_values.as_slice().to_vec(); + + Ok(public_values_bytes) } fn name(&self) -> &'static str { @@ -214,6 +230,10 @@ impl zkVM for EreSP1 { fn sdk_version(&self) -> &'static str { SDK_VERSION } + + fn deserialize_from(&self, reader: R) -> Result { + bincode::deserialize_from(reader).map_err(zkVMError::other) + } } fn serialize_inputs(stdin: &mut SP1Stdin, inputs: &Input) { @@ -233,7 +253,7 @@ mod tests { use crate::compile::compile; use std::{panic, sync::OnceLock}; use test_utils::host::{ - BasicProgramInputGen, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, + BasicProgramIo, Io, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, }; static BASIC_PRORGAM: OnceLock> = OnceLock::new(); @@ -253,8 +273,9 @@ mod tests { let program = basic_program(); let zkvm = EreSP1::new(program, ProverResourceType::Cpu); - let inputs = BasicProgramInputGen::valid(); - run_zkvm_execute(&zkvm, &inputs); + let io = BasicProgramIo::valid(); + let public_values = run_zkvm_execute(&zkvm, &io); + assert_eq!(io.deserialize_outputs(&zkvm, &public_values), io.outputs()); } #[test] @@ -263,7 +284,7 @@ mod tests { let program = compile(&guest_directory, &"nightly".to_string()).unwrap(); let zkvm = EreSP1::new(program, ProverResourceType::Cpu); - run_zkvm_execute(&zkvm, &Input::new()); + zkvm.execute(&Input::new()).unwrap(); } #[test] @@ -272,9 +293,9 @@ mod tests { let zkvm = EreSP1::new(program, ProverResourceType::Cpu); for inputs in [ - BasicProgramInputGen::empty(), - BasicProgramInputGen::invalid_string(), - BasicProgramInputGen::invalid_type(), + BasicProgramIo::empty(), + BasicProgramIo::invalid_type(), + BasicProgramIo::invalid_data(), ] { zkvm.execute(&inputs).unwrap_err(); } @@ -285,8 +306,9 @@ mod tests { let program = basic_program(); let zkvm = EreSP1::new(program, ProverResourceType::Cpu); - let inputs = BasicProgramInputGen::valid(); - run_zkvm_prove(&zkvm, &inputs); + let io = BasicProgramIo::valid(); + let public_values = run_zkvm_prove(&zkvm, &io); + assert_eq!(io.deserialize_outputs(&zkvm, &public_values), io.outputs()); } #[test] @@ -300,9 +322,9 @@ mod tests { // Note that we iterate on methods because `InputItem::Object` doesn't // implement `RefUnwindSafe`. for inputs_gen in [ - BasicProgramInputGen::empty, - BasicProgramInputGen::invalid_string, - BasicProgramInputGen::invalid_type, + BasicProgramIo::empty, + BasicProgramIo::invalid_type, + BasicProgramIo::invalid_data, ] { panic::catch_unwind(|| zkvm.prove(&inputs_gen())).unwrap_err(); } @@ -325,7 +347,8 @@ mod tests { let program = basic_program(); let zkvm = EreSP1::new(program, ProverResourceType::Network(network_config)); - let inputs = BasicProgramInputGen::valid(); - run_zkvm_prove(&zkvm, &inputs); + let io = BasicProgramIo::valid(); + let public_values = run_zkvm_prove(&zkvm, &io); + assert_eq!(io.deserialize_outputs(&zkvm, &public_values), io.outputs()); } } diff --git a/crates/ere-zisk/src/lib.rs b/crates/ere-zisk/src/lib.rs index 327dd36..1a134a8 100644 --- a/crates/ere-zisk/src/lib.rs +++ b/crates/ere-zisk/src/lib.rs @@ -6,10 +6,10 @@ use crate::{ }; use blake3::Hash; use dashmap::{DashMap, Entry}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; use std::{ fs, - io::{self, BufRead, Write}, + io::{self, BufRead, Read, Write}, os::unix::fs::symlink, path::{Path, PathBuf}, process::{Command, Stdio}, @@ -19,8 +19,8 @@ use std::{ use tempfile::{TempDir, tempdir}; use tracing::info; use zkvm_interface::{ - Compiler, Input, InputItem, ProgramExecutionReport, ProgramProvingReport, ProverResourceType, - zkVM, zkVMError, + Compiler, Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, + ProverResourceType, PublicValues, zkVM, zkVMError, }; include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs")); @@ -74,7 +74,7 @@ impl EreZisk { } impl zkVM for EreZisk { - fn execute(&self, inputs: &Input) -> Result { + fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> { // Write ELF and serialized input to file. let input_bytes = serialize_inputs(inputs) @@ -122,14 +122,23 @@ impl zkVM for EreZisk { }) .ok_or(ZiskError::Execute(ExecuteError::TotalStepsNotFound))?; - Ok(ProgramExecutionReport { - total_num_cycles, - execution_duration, - ..Default::default() - }) + // TODO: Public values + let public_values = Vec::new(); + + Ok(( + public_values, + ProgramExecutionReport { + total_num_cycles, + execution_duration, + ..Default::default() + }, + )) } - fn prove(&self, inputs: &Input) -> Result<(Vec, ProgramProvingReport), zkVMError> { + fn prove( + &self, + inputs: &Input, + ) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> { // Make sure proving key setup is done. check_setup()?; @@ -246,10 +255,17 @@ impl zkVM for EreZisk { let bytes = bincode::serialize(&proof_with_public_values) .map_err(|err| ZiskError::Prove(ProveError::Bincode(err)))?; - Ok((bytes, ProgramProvingReport::new(proving_time))) + // TODO: Public values + let public_values = Vec::new(); + + Ok(( + public_values, + bytes, + ProgramProvingReport::new(proving_time), + )) } - fn verify(&self, bytes: &[u8]) -> Result<(), zkVMError> { + fn verify(&self, bytes: &[u8]) -> Result { // Run ELF specific setup let rom_digest = rom_setup(&self.elf)?; @@ -301,7 +317,10 @@ impl zkVM for EreZisk { }))? } - Ok(()) + // TODO: Public values + let public_values = Vec::new(); + + Ok(public_values) } fn name(&self) -> &'static str { @@ -311,6 +330,10 @@ impl zkVM for EreZisk { fn sdk_version(&self) -> &'static str { SDK_VERSION } + + fn deserialize_from(&self, _reader: R) -> Result { + todo!() + } } /// Serialize `Input` into sequence of bytes. @@ -528,7 +551,7 @@ mod tests { use super::*; use std::sync::OnceLock; use test_utils::host::{ - BasicProgramInputGen, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, + BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory, }; static BASIC_PRORGAM: OnceLock> = OnceLock::new(); @@ -548,8 +571,8 @@ mod tests { let program = basic_program(); let zkvm = EreZisk::new(program, ProverResourceType::Cpu); - let inputs = BasicProgramInputGen::valid(); - run_zkvm_execute(&zkvm, &inputs); + let io = BasicProgramIo::valid(); + run_zkvm_execute(&zkvm, &io); } #[test] @@ -558,9 +581,9 @@ mod tests { let zkvm = EreZisk::new(program, ProverResourceType::Cpu); for inputs in [ - BasicProgramInputGen::empty(), - BasicProgramInputGen::invalid_string(), - BasicProgramInputGen::invalid_type(), + BasicProgramIo::empty(), + BasicProgramIo::invalid_type(), + BasicProgramIo::invalid_data(), ] { zkvm.execute(&inputs).unwrap_err(); } @@ -571,8 +594,8 @@ mod tests { let program = basic_program(); let zkvm = EreZisk::new(program, ProverResourceType::Cpu); - let inputs = BasicProgramInputGen::valid(); - run_zkvm_prove(&zkvm, &inputs); + let io = BasicProgramIo::valid(); + run_zkvm_prove(&zkvm, &io); } #[test] @@ -581,9 +604,9 @@ mod tests { let zkvm = EreZisk::new(program, ProverResourceType::Cpu); for inputs in [ - BasicProgramInputGen::empty(), - BasicProgramInputGen::invalid_string(), - BasicProgramInputGen::invalid_type(), + BasicProgramIo::empty(), + BasicProgramIo::invalid_type(), + BasicProgramIo::invalid_data(), ] { zkvm.prove(&inputs).unwrap_err(); } diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index 4d36779..187de50 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -6,6 +6,7 @@ rust-version.workspace = true license.workspace = true [dependencies] +rand = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } # Local dependencies @@ -16,4 +17,4 @@ workspace = true [features] default = [] -host = ["dep:zkvm-interface"] +host = ["dep:rand", "dep:zkvm-interface"] diff --git a/crates/test-utils/src/guest.rs b/crates/test-utils/src/guest.rs index 41defa5..21d8a78 100644 --- a/crates/test-utils/src/guest.rs +++ b/crates/test-utils/src/guest.rs @@ -1,8 +1,9 @@ use alloc::vec::Vec; -use core::iter; use serde::{Deserialize, Serialize}; -#[derive(Default, Serialize, Deserialize)] +pub const BASIC_PROGRAM_BYTES_LENGTH: usize = 32; + +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct BasicStruct { pub a: u8, pub b: u16, @@ -12,17 +13,26 @@ pub struct BasicStruct { } impl BasicStruct { - /// Performs some computation (Xoring all fields as bytes into `[u8; 32]`). - pub fn output(&self) -> [u8; 32] { - let mut output = [0; 32]; - iter::empty() - .chain(self.a.to_le_bytes()) - .chain(self.b.to_le_bytes()) - .chain(self.c.to_le_bytes()) - .chain(self.d.to_le_bytes()) - .chain(self.e.iter().copied()) - .enumerate() - .for_each(|(idx, byte)| output[idx % output.len()] ^= byte); - output + #[cfg(feature = "host")] + pub fn random(mut rng: impl rand::Rng) -> Self { + let n = rng.random_range(16..32); + BasicStruct { + a: rng.random(), + b: rng.random(), + c: rng.random(), + d: rng.random(), + e: rng.random_iter().take(n).collect(), + } + } + + /// Performs some computation (Wrapping add all fields by 1). + pub fn output(&self) -> Self { + Self { + a: self.a.wrapping_add(1), + b: self.b.wrapping_add(1), + c: self.c.wrapping_add(1), + d: self.d.wrapping_add(1), + e: self.e.iter().map(|byte| byte.wrapping_add(1)).collect(), + } } } diff --git a/crates/test-utils/src/host.rs b/crates/test-utils/src/host.rs index ece0f0f..b9da3f0 100644 --- a/crates/test-utils/src/host.rs +++ b/crates/test-utils/src/host.rs @@ -1,6 +1,7 @@ -use crate::guest::BasicStruct; -use std::path::PathBuf; -use zkvm_interface::{Input, zkVM}; +use crate::guest::{BASIC_PROGRAM_BYTES_LENGTH, BasicStruct}; +use rand::{Rng, rng}; +use std::{fmt::Debug, io::Read, path::PathBuf}; +use zkvm_interface::{Input, PublicValues, zkVM}; fn workspace() -> PathBuf { let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); @@ -13,61 +14,112 @@ pub fn testing_guest_directory(zkvm_name: &str, program: &str) -> PathBuf { workspace().join("tests").join(zkvm_name).join(program) } -pub fn run_zkvm_execute(zkvm: &impl zkVM, inputs: &Input) { - let _report = zkvm - .execute(inputs) - .expect("execute should not fail with valid input"); +pub trait Io { + type Output: Debug + PartialEq; - // TODO: Check output are expected. + fn inputs(&self) -> Input; + + fn outputs(&self) -> Self::Output; + + fn deserialize_outputs(&self, zkvm: &impl zkVM, bytes: &[u8]) -> Self::Output; } -pub fn run_zkvm_prove(zkvm: &impl zkVM, inputs: &Input) { - let (proof, _report) = zkvm - .prove(inputs) +pub fn run_zkvm_execute(zkvm: &impl zkVM, io: &impl Io) -> PublicValues { + let (public_values, _report) = zkvm + .execute(&io.inputs()) + .expect("execute should not fail with valid input"); + + // TODO: Uncomment when most zkVMs implement the returning of public values: + // assert_eq!(io.deserialize_outputs(&zkvm, &public_values), io.outputs()); + + public_values +} + +pub fn run_zkvm_prove(zkvm: &impl zkVM, io: &impl Io) -> PublicValues { + let (prover_public_values, proof, _report) = zkvm + .prove(&io.inputs()) .expect("prove should not fail with valid input"); - zkvm.verify(&proof) + let verifier_public_values = zkvm + .verify(&proof) .expect("verify should not fail with valid input"); - // TODO: Check output are expected. + assert_eq!(prover_public_values, verifier_public_values); + + // TODO: Uncomment when most zkVMs implement the returning of public values: + // assert_eq!(io.deserialize_outputs(&zkvm, &verifier_public_values), io.outputs()); + + verifier_public_values } /// The basic program takes 2 inputs: -/// - `Vec` that supposed to be "Hello world" -/// - `BasicStruct` +/// - `Vec` - random bytes +/// - [`BasicStruct`] - structure filled with random values /// -/// Outputs `[u8; 32]` which computed by xoring fields of `BasicStruct`. -pub struct BasicProgramInputGen; +/// Commit 2 outputs: +/// - `Vec` that should be reverse of the input random bytes. +/// - [`BasicStruct`] that should be computed by [`BasicStruct::output`]. +#[derive(Clone)] +pub struct BasicProgramIo { + bytes: Vec, + basic_struct: BasicStruct, +} -impl BasicProgramInputGen { - pub fn valid() -> Input { +impl Io for BasicProgramIo { + type Output = (Vec, BasicStruct); + + fn inputs(&self) -> Input { let mut inputs = Input::new(); - inputs.write_bytes("Hello world".as_bytes().to_vec()); - inputs.write(BasicStruct { - a: 0xff, - b: 0x7777, - c: 0xffffffff, - d: 0x7777777777777777, - e: (0..u8::MAX).collect(), - }); + inputs.write_bytes(self.bytes.clone()); + inputs.write(self.basic_struct.clone()); inputs } - pub fn invalid_string() -> Input { - let mut inputs = Input::new(); - inputs.write_bytes("Unexpected string".as_bytes().to_vec()); - inputs.write(BasicStruct::default()); - inputs + fn outputs(&self) -> Self::Output { + ( + self.bytes.iter().rev().copied().collect(), + self.basic_struct.output(), + ) } - pub fn invalid_type() -> Input { - let mut inputs = Input::new(); - inputs.write(BasicStruct::default()); - inputs.write_bytes("Hello world".as_bytes().to_vec()); - inputs + fn deserialize_outputs(&self, zkvm: &impl zkVM, mut bytes: &[u8]) -> Self::Output { + let mut rev_bytes = vec![0; self.bytes.len()]; + bytes.read_exact(&mut rev_bytes).unwrap(); + let basic_struct_output = zkvm.deserialize_from(bytes).unwrap(); + (rev_bytes, basic_struct_output) + } +} + +impl BasicProgramIo { + pub fn valid() -> Self { + let rng = &mut rng(); + Self { + bytes: rng.random_iter().take(BASIC_PROGRAM_BYTES_LENGTH).collect(), + basic_struct: BasicStruct::random(rng), + } } + /// Empty input that should trigger deserialization failure in guest + /// program. pub fn empty() -> Input { Input::new() } + + /// Input with invalid type that should trigger deserialization + /// failure in guest program. + pub fn invalid_type() -> Input { + let mut inputs = Input::new(); + inputs.write(0u64); + inputs.write_bytes(vec![0, 1, 2, 3]); + inputs + } + + /// Input with invalid data that should trigger assertion failure in guest + /// program. + pub fn invalid_data() -> Input { + let mut inputs = Input::new(); + inputs.write_bytes(vec![0; BASIC_PROGRAM_BYTES_LENGTH + 1]); + inputs.write(BasicStruct::default()); + inputs + } } diff --git a/crates/zkvm-interface/src/lib.rs b/crates/zkvm-interface/src/lib.rs index e3a523b..88f81ff 100644 --- a/crates/zkvm-interface/src/lib.rs +++ b/crates/zkvm-interface/src/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] use serde::{Serialize, de::DeserializeOwned}; -use std::path::Path; +use std::{io::Read, path::Path}; use thiserror::Error; mod input; @@ -90,6 +90,15 @@ impl zkVMError { } } +/// Public values committed/revealed by guest program. +/// +/// Use [`zkVM::deserialize_from`] to deserialize object from the bytes. +pub type PublicValues = Vec; + +/// Proof generated by [`zkVM::prove`], that also includes the [`PublicValues`] +/// for [`zkVM::verify`] to work. +pub type Proof = Vec; + #[allow(non_camel_case_types)] #[auto_impl::auto_impl(&, Arc, Box)] /// zkVM trait to abstract away the differences between each zkVM. @@ -103,21 +112,35 @@ impl zkVMError { /// implementation will have their own construction function. pub trait zkVM { /// Executes the program with the provided inputs. - fn execute(&self, inputs: &Input) -> Result; + fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError>; /// Creates a proof of the program execution with given inputs. - fn prove(&self, inputs: &Input) -> Result<(Vec, ProgramProvingReport), zkVMError>; + fn prove( + &self, + inputs: &Input, + ) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError>; - /// Verifies a proof of the program used to create this zkVM instance. - /// TODO: Pass public inputs too and check that they match if they come with the - /// TODO: proof, or append them if they do not. - /// TODO: We can also just have this return the public inputs, but then the user needs - /// TODO: ensure they check it for correct #[must_use] - fn verify(&self, proof: &[u8]) -> Result<(), 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; /// Returns the name of the zkVM fn name(&self) -> &'static str; /// Returns the version of the zkVM SDK (e.g. 0.1.0) fn sdk_version(&self) -> &'static str; + + /// Deserializes an object from a [`Read`]er. + /// + /// If a guest program has multiple objects committed/revealed, one can do + /// the following to extract them in sequence: + /// + /// ```ignore + /// let public_values = zkvm.verify(&proof)?; + /// let mut reader = public_values.as_slice(); + /// let v0: T = zkvm.deserialize_from(&mut reader)?; + /// let v1: U = zkvm.deserialize_from(&mut reader)?; + /// ``` + fn deserialize_from(&self, reader: R) -> Result; } diff --git a/tests/openvm/basic/Cargo.toml b/tests/openvm/basic/Cargo.toml index 3942440..56f49d7 100644 --- a/tests/openvm/basic/Cargo.toml +++ b/tests/openvm/basic/Cargo.toml @@ -6,5 +6,6 @@ edition = "2021" [workspace] [dependencies] +bincode = "1.3.3" openvm = { git = "https://github.com/openvm-org/openvm.git", features = ["std"], tag = "v1.2.0" } test-utils = { path = "../../../crates/test-utils" } diff --git a/tests/openvm/basic/src/main.rs b/tests/openvm/basic/src/main.rs index 237ca71..3ab94d6 100644 --- a/tests/openvm/basic/src/main.rs +++ b/tests/openvm/basic/src/main.rs @@ -1,16 +1,30 @@ +use core::array::from_fn; use openvm::io::{read, read_vec, reveal_u32}; -use test_utils::guest::BasicStruct; +use test_utils::guest::{BasicStruct, BASIC_PROGRAM_BYTES_LENGTH}; fn main() { - // Read `Hello world` bytes. + // Read `bytes`. let bytes = read_vec(); - assert_eq!(String::from_utf8_lossy(&bytes), "Hello world"); - // Read `BasicStruct`. + // Read `basic_struct`. let basic_struct = read::(); - let output = basic_struct.output(); - output.chunks(4).enumerate().for_each(|(idx, bytes)| { - reveal_u32(u32::from_le_bytes(bytes.try_into().unwrap()), idx); - }); + // Check `bytes` length is as expected. + assert_eq!(bytes.len(), BASIC_PROGRAM_BYTES_LENGTH); + + // Do some computation on `basic_struct`. + let basic_struct_output = basic_struct.output(); + + // Write reversed `bytes` and `basic_struct_output` + let public_values = core::iter::empty() + .chain(bytes.into_iter().rev()) + .chain(bincode::serialize(&basic_struct_output).unwrap()) + .collect::>(); + public_values + .chunks(4) + .enumerate() + .for_each(|(idx, bytes)| { + let bytes = from_fn(|i| bytes.get(i).copied().unwrap_or_default()); + reveal_u32(u32::from_le_bytes(bytes), idx); + }); } diff --git a/tests/pico/basic/src/main.rs b/tests/pico/basic/src/main.rs index 179eb29..b8e7288 100644 --- a/tests/pico/basic/src/main.rs +++ b/tests/pico/basic/src/main.rs @@ -1,19 +1,24 @@ #![no_main] -use pico_sdk::io::{commit, read_as, read_vec}; -use test_utils::guest::BasicStruct; +use pico_sdk::io::{commit, commit_bytes, read_as, read_vec}; +use test_utils::guest::{BasicStruct, BASIC_PROGRAM_BYTES_LENGTH}; pico_sdk::entrypoint!(main); pub fn main() { - // Read `Hello world` bytes. + // Read `bytes`. let bytes = read_vec(); - assert_eq!(String::from_utf8_lossy(&bytes), "Hello world"); - // Read `BasicStruct`. + // Read `basic_struct`. let basic_struct = read_as::(); - let output = basic_struct.output(); - // Write `output` - commit(&output); + // Check `bytes` length is as expected. + assert_eq!(bytes.len(), BASIC_PROGRAM_BYTES_LENGTH); + + // Do some computation on `basic_struct`. + let basic_struct_output = basic_struct.output(); + + // Write reversed `bytes` and `basic_struct_output` + commit_bytes(&bytes.into_iter().rev().collect::>()); + commit(&basic_struct_output); } diff --git a/tests/risc0/basic/Cargo.toml b/tests/risc0/basic/Cargo.toml index c3d15d0..412b0de 100644 --- a/tests/risc0/basic/Cargo.toml +++ b/tests/risc0/basic/Cargo.toml @@ -7,4 +7,5 @@ edition = "2021" [dependencies] risc0-zkvm = { version = "3.0.1", default-features = false, features = ["std", "unstable"] } +risc0-zkvm-platform = { version = "=2.0.4" } test-utils = { path = "../../../crates/test-utils" } diff --git a/tests/risc0/basic/src/main.rs b/tests/risc0/basic/src/main.rs index a9d0214..2323e24 100644 --- a/tests/risc0/basic/src/main.rs +++ b/tests/risc0/basic/src/main.rs @@ -1,15 +1,20 @@ use risc0_zkvm::guest::env; -use test_utils::guest::BasicStruct; +use test_utils::guest::{BasicStruct, BASIC_PROGRAM_BYTES_LENGTH}; fn main() { - // Read `Hello world` bytes. + // Read `bytes`. let bytes = env::read_frame(); - assert_eq!(String::from_utf8_lossy(&bytes), "Hello world"); - // Read `BasicStruct`. + // Read `basic_struct`. let basic_struct = env::read::(); - let output = basic_struct.output(); - // Write `output` - env::commit(&output); + // Check `bytes` length is as expected. + assert_eq!(bytes.len(), BASIC_PROGRAM_BYTES_LENGTH); + + // Do some computation on `basic_struct`. + let basic_struct_output = basic_struct.output(); + + // Write reversed `bytes` and `basic_struct_output` + env::commit_slice(&bytes.into_iter().rev().collect::>()); + env::commit(&basic_struct_output); } diff --git a/tests/sp1/basic/src/main.rs b/tests/sp1/basic/src/main.rs index 2fff5c3..a5d0a49 100644 --- a/tests/sp1/basic/src/main.rs +++ b/tests/sp1/basic/src/main.rs @@ -1,18 +1,23 @@ #![no_main] -use test_utils::guest::BasicStruct; +use test_utils::guest::{BasicStruct, BASIC_PROGRAM_BYTES_LENGTH}; sp1_zkvm::entrypoint!(main); pub fn main() { - // Read `Hello world` bytes. + // Read `bytes`. let bytes = sp1_zkvm::io::read_vec(); - assert_eq!(String::from_utf8_lossy(&bytes), "Hello world"); - // Read `BasicStruct`. + // Read `basic_struct`. let basic_struct = sp1_zkvm::io::read::(); - let output = basic_struct.output(); - // Write `output` - sp1_zkvm::io::commit(&output); + // Check `bytes` length is as expected. + assert_eq!(bytes.len(), BASIC_PROGRAM_BYTES_LENGTH); + + // Do some computation on `basic_struct`. + let basic_struct_output = basic_struct.output(); + + // Write reversed `bytes` and `basic_struct_output` + sp1_zkvm::io::commit_slice(&bytes.into_iter().rev().collect::>()); + sp1_zkvm::io::commit(&basic_struct_output); } diff --git a/tests/zisk/basic/src/main.rs b/tests/zisk/basic/src/main.rs index 074d218..20e6b6e 100644 --- a/tests/zisk/basic/src/main.rs +++ b/tests/zisk/basic/src/main.rs @@ -1,6 +1,7 @@ #![no_main] -use test_utils::guest::BasicStruct; +use core::array::from_fn; +use test_utils::guest::{BasicStruct, BASIC_PROGRAM_BYTES_LENGTH}; ziskos::entrypoint!(main); @@ -8,15 +9,31 @@ fn main() { let input = ziskos::read_input(); let mut input = input.as_slice(); - // Read `Hello world` bytes. + // Read `bytes`. let bytes: Vec = bincode::deserialize_from(&mut input).unwrap(); - assert_eq!(String::from_utf8_lossy(&bytes), "Hello world"); - // Read `BasicStruct`. + // Read `basic_struct`. let basic_struct: BasicStruct = bincode::deserialize_from(&mut input).unwrap(); - let output = basic_struct.output(); - output.chunks(4).enumerate().for_each(|(idx, bytes)| { - ziskos::set_output(idx, u32::from_le_bytes(bytes.try_into().unwrap())); - }); + // Check input is fully read. + assert!(input.is_empty()); + + // Check `bytes` length is as expected. + assert_eq!(bytes.len(), BASIC_PROGRAM_BYTES_LENGTH); + + // Do some computation on `basic_struct`. + let basic_struct_output = basic_struct.output(); + + // Write reversed `bytes` and `basic_struct_output` + let public_values = core::iter::empty() + .chain(bytes.into_iter().rev()) + .chain(bincode::serialize(&basic_struct_output).unwrap()) + .collect::>(); + public_values + .chunks(4) + .enumerate() + .for_each(|(idx, bytes)| { + let bytes = from_fn(|i| bytes.get(i).copied().unwrap_or_default()); + ziskos::set_output(idx, u32::from_le_bytes(bytes)); + }); }