mirror of
https://github.com/eth-act/ere.git
synced 2026-02-19 11:54:42 -05:00
zkVM takes opaque input (#173)
This commit is contained in:
2
.github/workflows/test-common.yml
vendored
2
.github/workflows/test-common.yml
vendored
@@ -21,6 +21,8 @@ jobs:
|
||||
run_test: true
|
||||
- crate: ere-dockerized
|
||||
run_test: false # They are run in per-zkVM workflows
|
||||
- crate: ere-io-serde
|
||||
run_test: true
|
||||
- crate: ere-build-utils
|
||||
run_test: true
|
||||
- crate: ere-compile-utils
|
||||
|
||||
70
Cargo.lock
generated
70
Cargo.lock
generated
@@ -3621,7 +3621,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ere-build-utils"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"cargo_metadata 0.19.2",
|
||||
"thiserror 2.0.12",
|
||||
@@ -3630,7 +3630,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ere-compile-utils"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"cargo_metadata 0.19.2",
|
||||
"tempfile",
|
||||
@@ -3639,10 +3639,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ere-compiler"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode 1.3.3",
|
||||
"bincode 2.0.1",
|
||||
"clap",
|
||||
"ere-jolt",
|
||||
"ere-miden",
|
||||
@@ -3660,16 +3660,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ere-dockerized"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode 1.3.3",
|
||||
"bytemuck",
|
||||
"ere-build-utils",
|
||||
"ere-server",
|
||||
"ere-test-utils",
|
||||
"ere-zkvm-interface",
|
||||
"risc0-zkvm",
|
||||
"serde",
|
||||
"tempfile",
|
||||
"thiserror 2.0.12",
|
||||
@@ -3677,9 +3674,17 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ere-io-serde"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"bincode 2.0.1",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ere-jolt"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"ark-serialize 0.5.0",
|
||||
"common",
|
||||
@@ -3688,16 +3693,15 @@ dependencies = [
|
||||
"ere-test-utils",
|
||||
"ere-zkvm-interface",
|
||||
"jolt",
|
||||
"serde",
|
||||
"tempfile",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ere-miden"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"bincode 1.3.3",
|
||||
"bincode 2.0.1",
|
||||
"ere-build-utils",
|
||||
"ere-test-utils",
|
||||
"ere-zkvm-interface",
|
||||
@@ -3713,9 +3717,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ere-nexus"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"bincode 1.3.3",
|
||||
"bincode 2.0.1",
|
||||
"ere-build-utils",
|
||||
"ere-compile-utils",
|
||||
"ere-test-utils",
|
||||
@@ -3731,7 +3735,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ere-openvm"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"ere-build-utils",
|
||||
"ere-compile-utils",
|
||||
@@ -3750,10 +3754,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ere-pico"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode 1.3.3",
|
||||
"bincode 2.0.1",
|
||||
"ere-build-utils",
|
||||
"ere-compile-utils",
|
||||
"ere-test-utils",
|
||||
@@ -3768,13 +3772,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ere-risc0"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"borsh",
|
||||
"bytemuck",
|
||||
"ere-build-utils",
|
||||
"ere-compile-utils",
|
||||
"ere-io-serde",
|
||||
"ere-test-utils",
|
||||
"ere-zkvm-interface",
|
||||
"risc0-binfmt",
|
||||
@@ -3787,10 +3791,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ere-server"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode 1.3.3",
|
||||
"bincode 2.0.1",
|
||||
"clap",
|
||||
"ere-jolt",
|
||||
"ere-miden",
|
||||
@@ -3815,14 +3819,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ere-sp1"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"bincode 1.3.3",
|
||||
"bincode 2.0.1",
|
||||
"ere-build-utils",
|
||||
"ere-compile-utils",
|
||||
"ere-test-utils",
|
||||
"ere-zkvm-interface",
|
||||
"serde",
|
||||
"sp1-sdk",
|
||||
"tempfile",
|
||||
"thiserror 2.0.12",
|
||||
@@ -3831,9 +3834,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ere-test-utils"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"bincode 1.3.3",
|
||||
"ere-io-serde",
|
||||
"ere-zkvm-interface",
|
||||
"rand 0.9.2",
|
||||
"serde",
|
||||
@@ -3842,14 +3845,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ere-ziren"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"bincode 1.3.3",
|
||||
"bincode 2.0.1",
|
||||
"ere-build-utils",
|
||||
"ere-compile-utils",
|
||||
"ere-test-utils",
|
||||
"ere-zkvm-interface",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
"tracing",
|
||||
"zkm-sdk",
|
||||
@@ -3857,16 +3859,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ere-zisk"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"bincode 1.3.3",
|
||||
"bincode 2.0.1",
|
||||
"blake3",
|
||||
"bytemuck",
|
||||
"ere-build-utils",
|
||||
"ere-compile-utils",
|
||||
"ere-test-utils",
|
||||
"ere-zkvm-interface",
|
||||
"serde",
|
||||
"strum 0.27.2",
|
||||
"tempfile",
|
||||
"thiserror 2.0.12",
|
||||
@@ -3875,12 +3876,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ere-zkvm-interface"
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
dependencies = [
|
||||
"auto_impl",
|
||||
"bincode 1.3.3",
|
||||
"bincode 2.0.1",
|
||||
"clap",
|
||||
"erased-serde",
|
||||
"indexmap 2.10.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
11
Cargo.toml
11
Cargo.toml
@@ -17,6 +17,7 @@ members = [
|
||||
"crates/dockerized/dockerized",
|
||||
"crates/dockerized/server",
|
||||
# Utils
|
||||
"crates/io-serde",
|
||||
"crates/build-utils",
|
||||
"crates/compile-utils",
|
||||
"crates/test-utils",
|
||||
@@ -24,7 +25,7 @@ members = [
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.0.13"
|
||||
version = "0.0.14"
|
||||
edition = "2024"
|
||||
rust-version = "1.85"
|
||||
license = "MIT OR Apache-2.0"
|
||||
@@ -34,23 +35,22 @@ license = "MIT OR Apache-2.0"
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0.98"
|
||||
auto_impl = "1.3.0"
|
||||
bincode = "1.3.3"
|
||||
bincode = { version = "2.0.1", default-features = false }
|
||||
blake3 = "1.8.2"
|
||||
borsh = "1.5.7"
|
||||
bytemuck = "1.23.1"
|
||||
cargo_metadata = "0.19.0"
|
||||
clap = "4.5.42"
|
||||
dashmap = "6.1.0"
|
||||
erased-serde = "0.4.6"
|
||||
indexmap = "2.10.0"
|
||||
postcard = "1.0.8"
|
||||
prost = "0.13"
|
||||
prost-build = "0.13"
|
||||
rand = "0.9.2"
|
||||
serde = "1.0.219"
|
||||
serde = { version = "1.0.219", default-features = false }
|
||||
serde_json = "1.0.142"
|
||||
serde_yaml = "0.9.34"
|
||||
sha2 = "0.10.9"
|
||||
sha2 = { version = "0.10.9", default-features = false }
|
||||
strum = "0.27.2"
|
||||
tempfile = "3.20.0"
|
||||
thiserror = "2.0.12"
|
||||
@@ -120,6 +120,7 @@ ere-compiler = { path = "crates/dockerized/compiler" }
|
||||
ere-dockerized = { path = "crates/dockerized/dockerized" }
|
||||
ere-server = { path = "crates/dockerized/server" }
|
||||
|
||||
ere-io-serde = { path = "crates/io-serde" }
|
||||
ere-build-utils = { path = "crates/build-utils" }
|
||||
ere-compile-utils = { path = "crates/compile-utils" }
|
||||
ere-test-utils = { path = "crates/test-utils" }
|
||||
|
||||
24
README.md
24
README.md
@@ -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 ere_zkvm_interface::{Compiler, Input, ProofKind, ProverResourceType, zkVM};
|
||||
use ere_zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let guest_directory = std::path::Path::new("workspace/guest");
|
||||
@@ -94,15 +94,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Create zkVM instance
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu);
|
||||
|
||||
// Prepare inputs
|
||||
let mut io = Input::new();
|
||||
io.write(42u32);
|
||||
// Serialize input
|
||||
let input = 42u32.to_le_bytes();
|
||||
|
||||
// Execute
|
||||
let (public_values, report) = zkvm.execute(&io)?;
|
||||
let (public_values, report) = zkvm.execute(&input)?;
|
||||
|
||||
// Prove
|
||||
let (public_values, proof, report) = zkvm.prove(&io, ProofKind::Compressed)?;
|
||||
let (public_values, proof, report) = zkvm.prove(&input, ProofKind::Compressed)?;
|
||||
|
||||
// Verify
|
||||
let public_values = zkvm.verify(&proof)?;
|
||||
@@ -129,7 +128,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 ere_zkvm_interface::{Compiler, Input, ProofKind, ProverResourceType, zkVM};
|
||||
use ere_zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let guest_directory = std::path::Path::new("workspace/guest");
|
||||
@@ -141,15 +140,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Create zkVM instance
|
||||
let zkvm = EreDockerizedzkVM::new(ErezkVM::SP1, program, ProverResourceType::Cpu)?;
|
||||
|
||||
// Prepare inputs
|
||||
let mut io = Input::new();
|
||||
io.write(42u32);
|
||||
// Serialize input
|
||||
let input = 42u32.to_le_bytes();
|
||||
|
||||
// Execute
|
||||
let (public_values, report) = zkvm.execute(&io)?;
|
||||
let (public_values, report) = zkvm.execute(&input)?;
|
||||
|
||||
// Prove
|
||||
let (public_values, proof, report) = zkvm.prove(&io, ProofKind::Compressed)?;
|
||||
let (public_values, proof, report) = zkvm.prove(&input, ProofKind::Compressed)?;
|
||||
|
||||
// Verify
|
||||
let public_values = zkvm.verify(&proof)?;
|
||||
@@ -184,7 +182,7 @@ Each `ere-{backend}` crate implements the above traits for its zkVM.
|
||||
|
||||
### Input Handling
|
||||
|
||||
The `Input` type supports both chunked (`Vec<Vec<u8>>`) and contiguous (`Vec<u8>`) modes to satisfy differing backend APIs.
|
||||
The input is opaque to `zkVM` and will be passed as is, de/serialization needed to be handled by guest/host themselves.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
bincode.workspace = true
|
||||
bincode = { workspace = true, features = ["std", "serde"] }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
serde.workspace = true
|
||||
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
||||
|
||||
@@ -42,8 +42,9 @@ fn main() -> Result<(), Error> {
|
||||
|
||||
let program = compile(args.guest_path)?;
|
||||
|
||||
let output = File::create(args.output_path).with_context(|| "Failed to create output")?;
|
||||
bincode::serialize_into(output, &program).with_context(|| "Failed to serialize program")?;
|
||||
let mut output = File::create(args.output_path).with_context(|| "Failed to create output")?;
|
||||
bincode::serde::encode_into_std_write(&program, &mut output, bincode::config::legacy())
|
||||
.with_context(|| "Failed to serialize program")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -7,17 +7,12 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
bincode.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
tempfile.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio = { workspace = true, features = ["rt-multi-thread"] }
|
||||
tracing.workspace = true
|
||||
|
||||
# Needed for Risc0 and OpenVM input serialization.
|
||||
bytemuck.workspace = true
|
||||
risc0-zkvm.workspace = true
|
||||
|
||||
# Local dependencies
|
||||
ere-zkvm-interface = { workspace = true, features = ["clap"] }
|
||||
ere-server.workspace = true
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
use crate::{ErezkVM, error::CommonError};
|
||||
use ere_server::input::{SerializedInput, SerializedInputItem};
|
||||
use ere_zkvm_interface::{Input, InputItem};
|
||||
use serde::Serialize;
|
||||
|
||||
impl ErezkVM {
|
||||
pub fn serialize_object(
|
||||
&self,
|
||||
obj: &(impl Serialize + ?Sized),
|
||||
) -> Result<Vec<u8>, CommonError> {
|
||||
match self {
|
||||
// Issue for tracking: https://github.com/eth-act/ere/issues/4.
|
||||
Self::Jolt => todo!(),
|
||||
Self::Miden => bincode::serialize(obj).map_err(|err| {
|
||||
CommonError::serilization(err, "Failed to serialize object with `bincode`")
|
||||
}),
|
||||
// Issue for tracking: https://github.com/eth-act/ere/issues/63.
|
||||
Self::Nexus => todo!(),
|
||||
// FIXME: Instead of using `openvm::serde::to_vec`, we use Risc0's
|
||||
// serializer, because OpenVM uses the same one, to avoid the
|
||||
// duplicated extern symbol they export.
|
||||
// It'd be better to have each zkvm provides their
|
||||
// lightweight serde crate.
|
||||
// The issue for tracking https://github.com/eth-act/ere/issues/76.
|
||||
Self::OpenVM => risc0_zkvm::serde::to_vec(obj)
|
||||
.map(|words| words.into_iter().flat_map(|w| w.to_le_bytes()).collect())
|
||||
.map_err(|err| {
|
||||
CommonError::serilization(
|
||||
err,
|
||||
"Failed to serialize object with `risc0_zkvm::serde::to_vec`",
|
||||
)
|
||||
}),
|
||||
Self::Pico => bincode::serialize(obj).map_err(|err| {
|
||||
CommonError::serilization(err, "Failed to serialize object with `bincode`")
|
||||
}),
|
||||
Self::Risc0 => risc0_zkvm::serde::to_vec(obj)
|
||||
.map(|vec| bytemuck::cast_slice(&vec).to_vec())
|
||||
.map_err(|err| {
|
||||
CommonError::serilization(
|
||||
err,
|
||||
"Failed to serialize object with `risc0_zkvm::serde::to_vec`",
|
||||
)
|
||||
}),
|
||||
Self::SP1 => bincode::serialize(obj).map_err(|err| {
|
||||
CommonError::serilization(err, "Failed to serialize object with `bincode`")
|
||||
}),
|
||||
Self::Ziren => bincode::serialize(obj).map_err(|err| {
|
||||
CommonError::serilization(err, "Failed to serialize object with `bincode`")
|
||||
}),
|
||||
Self::Zisk => bincode::serialize(obj).map_err(|err| {
|
||||
CommonError::serilization(err, "Failed to serialize object with `bincode`")
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize_inputs(&self, inputs: &Input) -> Result<SerializedInput, CommonError> {
|
||||
inputs
|
||||
.iter()
|
||||
.map(|input| {
|
||||
Ok(match input {
|
||||
InputItem::Object(obj) => {
|
||||
SerializedInputItem::SerializedObject(self.serialize_object(&**obj)?)
|
||||
}
|
||||
InputItem::SerializedObject(bytes) => {
|
||||
SerializedInputItem::SerializedObject(bytes.clone())
|
||||
}
|
||||
InputItem::Bytes(bytes) => SerializedInputItem::Bytes(bytes.clone()),
|
||||
})
|
||||
})
|
||||
.collect::<Result<_, _>>()
|
||||
.map(SerializedInput)
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@
|
||||
//! ```rust,no_run
|
||||
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! use ere_dockerized::{EreDockerizedCompiler, EreDockerizedzkVM, ErezkVM};
|
||||
//! use ere_zkvm_interface::{Compiler, Input, ProofKind, ProverResourceType, zkVM};
|
||||
//! use ere_zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
|
||||
//! use std::path::Path;
|
||||
//!
|
||||
//! // The zkVM we plan to use
|
||||
@@ -42,17 +42,15 @@
|
||||
//! let resource = ProverResourceType::Cpu;
|
||||
//! let zkvm = EreDockerizedzkVM::new(zkvm, program, resource)?;
|
||||
//!
|
||||
//! // Prepare inputs
|
||||
//! let mut inputs = Input::new();
|
||||
//! inputs.write(42u32);
|
||||
//! inputs.write(100u16);
|
||||
//! // Serialize input
|
||||
//! let input = 42u32.to_le_bytes();
|
||||
//!
|
||||
//! // Execute program
|
||||
//! let (public_values, execution_report) = zkvm.execute(&inputs)?;
|
||||
//! let (public_values, execution_report) = zkvm.execute(&input)?;
|
||||
//! println!("Execution cycles: {}", execution_report.total_num_cycles);
|
||||
//!
|
||||
//! // Generate proof
|
||||
//! let (public_values, proof, proving_report) = zkvm.prove(&inputs, ProofKind::Compressed)?;
|
||||
//! let (public_values, proof, proving_report) = zkvm.prove(&input, ProofKind::Compressed)?;
|
||||
//! println!("Proof generated in: {:?}", proving_report.proving_time);
|
||||
//!
|
||||
//! // Verify proof
|
||||
@@ -71,16 +69,14 @@ use crate::{
|
||||
};
|
||||
use ere_server::client::{Url, zkVMClient};
|
||||
use ere_zkvm_interface::{
|
||||
Compiler, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM, zkVMError,
|
||||
Compiler, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
};
|
||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
env,
|
||||
fmt::{self, Display, Formatter},
|
||||
fs,
|
||||
io::Read,
|
||||
iter,
|
||||
fs, iter,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
};
|
||||
@@ -93,8 +89,6 @@ include!(concat!(env!("OUT_DIR"), "/zkvm_sdk_version_impl.rs"));
|
||||
pub mod cuda;
|
||||
pub mod docker;
|
||||
pub mod error;
|
||||
pub mod input;
|
||||
pub mod output;
|
||||
|
||||
/// Offset of port used for `ere-server` for [`ErezkVM`]s.
|
||||
const ERE_SERVER_PORT_OFFSET: u16 = 4174;
|
||||
@@ -201,13 +195,7 @@ impl ErezkVM {
|
||||
|
||||
let cuda_arch = cuda_arch();
|
||||
match self {
|
||||
ErezkVM::OpenVM => {
|
||||
if let Some(cuda_arch) = cuda_arch {
|
||||
// OpenVM takes only the numeric part.
|
||||
cmd = cmd.build_arg("CUDA_ARCH", cuda_arch.replace("sm_", ""))
|
||||
}
|
||||
}
|
||||
ErezkVM::Risc0 | ErezkVM::Zisk => {
|
||||
ErezkVM::OpenVM | ErezkVM::Risc0 | ErezkVM::Zisk => {
|
||||
if let Some(cuda_arch) = cuda_arch {
|
||||
cmd = cmd.build_arg("CUDA_ARCH", cuda_arch)
|
||||
}
|
||||
@@ -502,13 +490,8 @@ impl EreDockerizedzkVM {
|
||||
}
|
||||
|
||||
impl zkVM for EreDockerizedzkVM {
|
||||
fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
let serialized_input = self
|
||||
.zkvm
|
||||
.serialize_inputs(inputs)
|
||||
.map_err(|err| DockerizedError::Execute(ExecuteError::Common(err)))?;
|
||||
|
||||
let (public_values, report) = block_on(self.client.execute(serialized_input))
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
let (public_values, report) = block_on(self.client.execute(input.to_vec()))
|
||||
.map_err(|err| DockerizedError::Execute(ExecuteError::Client(err)))?;
|
||||
|
||||
Ok((public_values, report))
|
||||
@@ -516,16 +499,11 @@ impl zkVM for EreDockerizedzkVM {
|
||||
|
||||
fn prove(
|
||||
&self,
|
||||
inputs: &Input,
|
||||
input: &[u8],
|
||||
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, proof_kind))
|
||||
block_on(self.client.prove(input.to_vec(), proof_kind))
|
||||
.map_err(|err| DockerizedError::Prove(ProveError::Client(err)))?;
|
||||
|
||||
Ok((public_values, proof, report))
|
||||
@@ -545,10 +523,6 @@ impl zkVM for EreDockerizedzkVM {
|
||||
fn sdk_version(&self) -> &'static str {
|
||||
self.zkvm.sdk_version()
|
||||
}
|
||||
|
||||
fn deserialize_from<R: Read, T: DeserializeOwned>(&self, reader: R) -> Result<T, zkVMError> {
|
||||
self.zkvm.deserialize_from(reader)
|
||||
}
|
||||
}
|
||||
|
||||
fn block_on<T>(future: impl Future<Output = T>) -> T {
|
||||
@@ -572,14 +546,16 @@ fn home_dir() -> PathBuf {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
EreDockerizedCompiler, EreDockerizedzkVM, ErezkVM, SerializedProgram, workspace_dir,
|
||||
};
|
||||
use ere_test_utils::{host::*, program::basic::BasicProgramInput};
|
||||
use ere_zkvm_interface::{Compiler, ProverResourceType};
|
||||
use std::sync::{Mutex, MutexGuard, OnceLock};
|
||||
|
||||
macro_rules! test_compile {
|
||||
($zkvm:ident, $program:literal) => {
|
||||
use crate::{
|
||||
EreDockerizedCompiler, EreDockerizedzkVM, ErezkVM, SerializedProgram, workspace_dir,
|
||||
};
|
||||
use ere_test_utils::host::*;
|
||||
use ere_zkvm_interface::{Compiler, ProverResourceType};
|
||||
use std::sync::{Mutex, MutexGuard, OnceLock};
|
||||
use super::*;
|
||||
|
||||
fn program() -> &'static SerializedProgram {
|
||||
static PROGRAM: OnceLock<SerializedProgram> = OnceLock::new();
|
||||
@@ -644,41 +620,43 @@ mod test {
|
||||
|
||||
mod nexus {
|
||||
test_compile!(Nexus, "basic");
|
||||
test_execute!(Nexus, BasicProgramInput::valid());
|
||||
test_prove!(Nexus, BasicProgramInput::valid());
|
||||
}
|
||||
|
||||
mod openvm {
|
||||
test_compile!(OpenVM, "basic");
|
||||
test_execute!(OpenVM, BasicProgramIo::valid().into_output_hashed_io());
|
||||
test_prove!(OpenVM, BasicProgramIo::valid().into_output_hashed_io());
|
||||
test_execute!(OpenVM, BasicProgramInput::valid().into_output_sha256());
|
||||
test_prove!(OpenVM, BasicProgramInput::valid().into_output_sha256());
|
||||
}
|
||||
|
||||
mod pico {
|
||||
test_compile!(Pico, "basic");
|
||||
test_execute!(Pico, BasicProgramIo::valid());
|
||||
test_prove!(Pico, BasicProgramIo::valid());
|
||||
test_execute!(Pico, BasicProgramInput::valid());
|
||||
test_prove!(Pico, BasicProgramInput::valid());
|
||||
}
|
||||
|
||||
mod risc0 {
|
||||
test_compile!(Risc0, "basic");
|
||||
test_execute!(Risc0, BasicProgramIo::valid());
|
||||
test_prove!(Risc0, BasicProgramIo::valid());
|
||||
test_execute!(Risc0, BasicProgramInput::valid());
|
||||
test_prove!(Risc0, BasicProgramInput::valid());
|
||||
}
|
||||
|
||||
mod sp1 {
|
||||
test_compile!(SP1, "basic");
|
||||
test_execute!(SP1, BasicProgramIo::valid());
|
||||
test_prove!(SP1, BasicProgramIo::valid());
|
||||
test_execute!(SP1, BasicProgramInput::valid());
|
||||
test_prove!(SP1, BasicProgramInput::valid());
|
||||
}
|
||||
|
||||
mod ziren {
|
||||
test_compile!(Ziren, "basic");
|
||||
test_execute!(Ziren, BasicProgramIo::valid());
|
||||
test_prove!(Ziren, BasicProgramIo::valid());
|
||||
test_execute!(Ziren, BasicProgramInput::valid());
|
||||
test_prove!(Ziren, BasicProgramInput::valid());
|
||||
}
|
||||
|
||||
mod zisk {
|
||||
test_compile!(Zisk, "basic");
|
||||
test_execute!(Zisk, BasicProgramIo::valid().into_output_hashed_io());
|
||||
test_prove!(Zisk, BasicProgramIo::valid().into_output_hashed_io());
|
||||
test_execute!(Zisk, BasicProgramInput::valid().into_output_sha256());
|
||||
test_prove!(Zisk, BasicProgramInput::valid().into_output_sha256());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
use crate::ErezkVM;
|
||||
use ere_zkvm_interface::zkVMError;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::io::Read;
|
||||
|
||||
#[path = "../../../zkvm/risc0/src/output.rs"]
|
||||
mod ere_risc0_output;
|
||||
|
||||
impl ErezkVM {
|
||||
pub fn deserialize_from<R: Read, T: DeserializeOwned>(
|
||||
&self,
|
||||
reader: R,
|
||||
) -> Result<T, zkVMError> {
|
||||
match self {
|
||||
// Issue for tracking: https://github.com/eth-act/ere/issues/4.
|
||||
Self::Jolt => todo!(),
|
||||
Self::Miden => bincode::deserialize_from(reader).map_err(zkVMError::other),
|
||||
// Issue for tracking: https://github.com/eth-act/ere/issues/63.
|
||||
Self::Nexus => todo!(),
|
||||
Self::OpenVM => unimplemented!("no native serialization in this platform"),
|
||||
Self::Pico => bincode::deserialize_from(reader).map_err(zkVMError::other),
|
||||
Self::Risc0 => ere_risc0_output::deserialize_from(reader),
|
||||
Self::SP1 => bincode::deserialize_from(reader).map_err(zkVMError::other),
|
||||
Self::Ziren => bincode::deserialize_from(reader).map_err(zkVMError::other),
|
||||
Self::Zisk => unimplemented!("no native serialization in this platform"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
bincode.workspace = true
|
||||
bincode = { workspace = true, features = ["alloc", "serde"] }
|
||||
prost.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
tokio.workspace = true
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use crate::{
|
||||
api::{
|
||||
ExecuteRequest, ExecuteResponse, ProveRequest, ProveResponse, VerifyRequest,
|
||||
VerifyResponse, ZkvmService,
|
||||
},
|
||||
input::SerializedInput,
|
||||
use crate::api::{
|
||||
ExecuteRequest, ExecuteResponse, ProveRequest, ProveResponse, VerifyRequest, VerifyResponse,
|
||||
ZkvmService,
|
||||
};
|
||||
use anyhow::{Context, Error, bail};
|
||||
use ere_zkvm_interface::{
|
||||
@@ -47,10 +44,8 @@ impl zkVMClient {
|
||||
|
||||
pub async fn execute(
|
||||
&self,
|
||||
input: SerializedInput,
|
||||
input: Vec<u8>,
|
||||
) -> Result<(PublicValues, ProgramExecutionReport), Error> {
|
||||
let input = bincode::serialize(&input).with_context(|| "Failed to serialize input")?;
|
||||
|
||||
let request = Request::new(ExecuteRequest { input });
|
||||
|
||||
let response = self
|
||||
@@ -64,19 +59,18 @@ impl zkVMClient {
|
||||
report,
|
||||
} = response.into_body();
|
||||
|
||||
let report: ProgramExecutionReport = bincode::deserialize(&report)
|
||||
.with_context(|| "Failed to deserialize execution report")?;
|
||||
let (report, _): (ProgramExecutionReport, _) =
|
||||
bincode::serde::decode_from_slice(&report, bincode::config::legacy())
|
||||
.with_context(|| "Failed to deserialize execution report")?;
|
||||
|
||||
Ok((public_values, report))
|
||||
}
|
||||
|
||||
pub async fn prove(
|
||||
&self,
|
||||
input: SerializedInput,
|
||||
input: Vec<u8>,
|
||||
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,
|
||||
proof_kind: proof_kind as i32,
|
||||
@@ -94,8 +88,9 @@ impl zkVMClient {
|
||||
report,
|
||||
} = response.into_body();
|
||||
|
||||
let report: ProgramProvingReport = bincode::deserialize(&report)
|
||||
.with_context(|| "Failed to deserialize proving report")?;
|
||||
let (report, _): (ProgramProvingReport, _) =
|
||||
bincode::serde::decode_from_slice(&report, bincode::config::legacy())
|
||||
.with_context(|| "Failed to deserialize proving report")?;
|
||||
|
||||
Ok((public_values, Proof::new(proof_kind, proof), report))
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
use ere_zkvm_interface::{Input, InputItem};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SerializedInput(pub Vec<SerializedInputItem>);
|
||||
|
||||
impl From<SerializedInput> for Input {
|
||||
fn from(value: SerializedInput) -> Self {
|
||||
Self::from(value.0.into_iter().map(Into::into).collect::<Vec<_>>())
|
||||
}
|
||||
}
|
||||
|
||||
/// `InputItem` but only `SerializedObject` and `Byte` variants remain.
|
||||
///
|
||||
/// The user must serialize the `InputItem::Object` in the way the zkVM expects.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum SerializedInputItem {
|
||||
SerializedObject(Vec<u8>),
|
||||
Bytes(Vec<u8>),
|
||||
}
|
||||
|
||||
impl From<SerializedInputItem> for InputItem {
|
||||
fn from(value: SerializedInputItem) -> Self {
|
||||
match value {
|
||||
SerializedInputItem::SerializedObject(bytes) => Self::SerializedObject(bytes),
|
||||
SerializedInputItem::Bytes(bytes) => Self::Bytes(bytes),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod client;
|
||||
pub mod input;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) mod api {
|
||||
|
||||
@@ -108,8 +108,8 @@ async fn shutdown_signal() {
|
||||
}
|
||||
|
||||
fn construct_zkvm(program: Vec<u8>, resource: ProverResourceType) -> Result<impl zkVM, Error> {
|
||||
let program =
|
||||
bincode::deserialize(&program).with_context(|| "Failed to deserialize program")?;
|
||||
let (program, _) = bincode::serde::decode_from_slice(&program, bincode::config::legacy())
|
||||
.with_context(|| "Failed to deserialize program")?;
|
||||
|
||||
#[cfg(feature = "jolt")]
|
||||
let zkvm = ere_jolt::EreJolt::new(program, resource);
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use crate::{
|
||||
api::{
|
||||
self, ExecuteRequest, ExecuteResponse, ProveRequest, ProveResponse, VerifyRequest,
|
||||
VerifyResponse, ZkvmService,
|
||||
},
|
||||
input::SerializedInput,
|
||||
use crate::api::{
|
||||
self, ExecuteRequest, ExecuteResponse, ProveRequest, ProveResponse, VerifyRequest,
|
||||
VerifyResponse, ZkvmService,
|
||||
};
|
||||
use ere_zkvm_interface::{Proof, ProofKind, zkVM};
|
||||
use twirp::{Request, Response, async_trait::async_trait, invalid_argument};
|
||||
@@ -31,9 +28,7 @@ impl<T: 'static + zkVM + Send + Sync> ZkvmService for zkVMServer<T> {
|
||||
) -> twirp::Result<Response<ExecuteResponse>> {
|
||||
let request = request.into_body();
|
||||
|
||||
let input = bincode::deserialize::<SerializedInput>(&request.input)
|
||||
.map_err(|_| invalid_argument("failed to deserialize input"))?
|
||||
.into();
|
||||
let input = request.input;
|
||||
|
||||
let (public_values, report) = self
|
||||
.zkvm
|
||||
@@ -42,7 +37,7 @@ impl<T: 'static + zkVM + Send + Sync> ZkvmService for zkVMServer<T> {
|
||||
|
||||
Ok(Response::new(ExecuteResponse {
|
||||
public_values,
|
||||
report: bincode::serialize(&report).unwrap(),
|
||||
report: bincode::serde::encode_to_vec(&report, bincode::config::legacy()).unwrap(),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -52,9 +47,7 @@ impl<T: 'static + zkVM + Send + Sync> ZkvmService for zkVMServer<T> {
|
||||
) -> twirp::Result<Response<ProveResponse>> {
|
||||
let request = request.into_body();
|
||||
|
||||
let input = bincode::deserialize::<SerializedInput>(&request.input)
|
||||
.map_err(|_| invalid_argument("failed to deserialize input"))?
|
||||
.into();
|
||||
let input = request.input;
|
||||
let proof_kind = ProofKind::from_repr(request.proof_kind as usize).ok_or_else(|| {
|
||||
invalid_argument(format!("invalid proof kind: {}", request.proof_kind))
|
||||
})?;
|
||||
@@ -67,7 +60,7 @@ impl<T: 'static + zkVM + Send + Sync> ZkvmService for zkVMServer<T> {
|
||||
Ok(Response::new(ProveResponse {
|
||||
public_values,
|
||||
proof: proof.as_bytes().to_vec(),
|
||||
report: bincode::serialize(&report).unwrap(),
|
||||
report: bincode::serde::encode_to_vec(&report, bincode::config::legacy()).unwrap(),
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
15
crates/io-serde/Cargo.toml
Normal file
15
crates/io-serde/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "ere-io-serde"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bincode = { workspace = true, features = ["alloc", "serde"] }
|
||||
serde.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
62
crates/io-serde/src/bincode.rs
Normal file
62
crates/io-serde/src/bincode.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use crate::IoSerde;
|
||||
use alloc::vec::Vec;
|
||||
use bincode::config::{Config, Configuration, Fixint, LittleEndian, NoLimit, Varint};
|
||||
use core::{
|
||||
error::Error,
|
||||
fmt::{self, Display, Formatter},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub use bincode::{
|
||||
config,
|
||||
error::{DecodeError, EncodeError},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BincodeError {
|
||||
Encode(EncodeError),
|
||||
Decode(DecodeError),
|
||||
}
|
||||
|
||||
impl Display for BincodeError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Encode(err) => write!(f, "{err:?}"),
|
||||
Self::Decode(err) => write!(f, "{err:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for BincodeError {}
|
||||
|
||||
/// IO de/serialization implementation with [`bincode`].
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Bincode<O>(pub O);
|
||||
|
||||
impl Bincode<Configuration<LittleEndian, Fixint, NoLimit>> {
|
||||
/// `Bincode` with legacy configuration, same as the default of `bincode@1`.
|
||||
pub fn legacy() -> Self {
|
||||
Self(bincode::config::legacy())
|
||||
}
|
||||
}
|
||||
|
||||
impl Bincode<Configuration<LittleEndian, Varint, NoLimit>> {
|
||||
/// `Bincode` with standard configuration.
|
||||
pub fn standard() -> Self {
|
||||
Self(bincode::config::standard())
|
||||
}
|
||||
}
|
||||
|
||||
impl<O: Config> IoSerde for Bincode<O> {
|
||||
type Error = BincodeError;
|
||||
|
||||
fn serialize<T: Serialize>(&self, value: &T) -> Result<Vec<u8>, Self::Error> {
|
||||
bincode::serde::encode_to_vec(value, self.0).map_err(BincodeError::Encode)
|
||||
}
|
||||
|
||||
fn deserialize<'a, T: Deserialize<'a>>(&self, bytes: &'a [u8]) -> Result<T, Self::Error> {
|
||||
let (value, _) = bincode::serde::borrow_decode_from_slice(bytes, self.0)
|
||||
.map_err(BincodeError::Decode)?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
22
crates/io-serde/src/lib.rs
Normal file
22
crates/io-serde/src/lib.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
//! This crate provides IO de/serialization implementation to be shared between
|
||||
//! host and guest, if the guest is also written in Rust.
|
||||
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::error::Error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod bincode;
|
||||
|
||||
/// IO de/serialization to be shared between host and guest.
|
||||
pub trait IoSerde {
|
||||
type Error: Error;
|
||||
|
||||
fn serialize<T: Serialize>(&self, value: &T) -> Result<Vec<u8>, Self::Error>;
|
||||
|
||||
fn deserialize<'a, T: Deserialize<'a>>(&self, bytes: &'a [u8]) -> Result<T, Self::Error>;
|
||||
}
|
||||
@@ -6,13 +6,13 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bincode.workspace = true
|
||||
rand = { workspace = true, optional = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
sha2.workspace = true
|
||||
|
||||
# Local dependencies
|
||||
ere-zkvm-interface = { workspace = true, optional = true }
|
||||
ere-io-serde = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,60 +1,12 @@
|
||||
use alloc::vec::Vec;
|
||||
use core::iter;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
pub struct BasicProgramCore;
|
||||
pub use sha2::{Digest, Sha256};
|
||||
|
||||
impl BasicProgramCore {
|
||||
pub const BYTES_LENGTH: usize = 32;
|
||||
/// Platform dependent methods.
|
||||
pub trait Platform {
|
||||
/// Read the whole input at once from host.
|
||||
fn read_input() -> Vec<u8>;
|
||||
|
||||
pub fn outputs(inputs: (Vec<u8>, BasicStruct)) -> (Vec<u8>, BasicStruct) {
|
||||
let (bytes, basic_struct) = inputs;
|
||||
(bytes.iter().rev().copied().collect(), basic_struct.output())
|
||||
}
|
||||
|
||||
pub fn sha256_outputs(outputs: (Vec<u8>, BasicStruct)) -> [u8; 32] {
|
||||
let (rev_bytes, basic_struct) = outputs;
|
||||
Sha256::digest(
|
||||
iter::empty()
|
||||
.chain(rev_bytes)
|
||||
.chain(bincode::serialize(&basic_struct).unwrap())
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct BasicStruct {
|
||||
pub a: u8,
|
||||
pub b: u16,
|
||||
pub c: u32,
|
||||
pub d: u64,
|
||||
pub e: Vec<u8>,
|
||||
}
|
||||
|
||||
impl BasicStruct {
|
||||
#[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(),
|
||||
}
|
||||
}
|
||||
/// Write the whole output at once to host.
|
||||
fn write_output(output: &[u8]);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::guest::{BasicProgramCore, BasicStruct};
|
||||
use ere_zkvm_interface::{Input, ProofKind, PublicValues, zkVM};
|
||||
use rand::{Rng, rng};
|
||||
use std::{fmt::Debug, io::Read, marker::PhantomData, path::PathBuf};
|
||||
use crate::program::{Program, ProgramInput};
|
||||
use ere_io_serde::IoSerde;
|
||||
use ere_zkvm_interface::{ProofKind, PublicValues, zkVM};
|
||||
use sha2::Digest;
|
||||
use std::{marker::PhantomData, path::PathBuf};
|
||||
|
||||
fn workspace() -> PathBuf {
|
||||
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
@@ -14,29 +15,19 @@ pub fn testing_guest_directory(zkvm_name: &str, program: &str) -> PathBuf {
|
||||
workspace().join("tests").join(zkvm_name).join(program)
|
||||
}
|
||||
|
||||
pub trait Io {
|
||||
type Output: Debug + PartialEq;
|
||||
|
||||
fn inputs(&self) -> Input;
|
||||
|
||||
fn outputs(&self) -> Self::Output;
|
||||
|
||||
fn deserialize_outputs(&self, zkvm: &impl zkVM, bytes: &[u8]) -> Self::Output;
|
||||
}
|
||||
|
||||
pub fn run_zkvm_execute(zkvm: &impl zkVM, io: &impl Io) -> PublicValues {
|
||||
pub fn run_zkvm_execute(zkvm: &impl zkVM, test_case: &impl TestCase) -> PublicValues {
|
||||
let (public_values, _report) = zkvm
|
||||
.execute(&io.inputs())
|
||||
.execute(&test_case.serialized_input())
|
||||
.expect("execute should not fail with valid input");
|
||||
|
||||
assert_eq!(io.deserialize_outputs(&zkvm, &public_values), io.outputs());
|
||||
test_case.assert_output(&public_values);
|
||||
|
||||
public_values
|
||||
}
|
||||
|
||||
pub fn run_zkvm_prove(zkvm: &impl zkVM, io: &impl Io) -> PublicValues {
|
||||
pub fn run_zkvm_prove(zkvm: &impl zkVM, test_case: &impl TestCase) -> PublicValues {
|
||||
let (prover_public_values, proof, _report) = zkvm
|
||||
.prove(&io.inputs(), ProofKind::default())
|
||||
.prove(&test_case.serialized_input(), ProofKind::default())
|
||||
.expect("prove should not fail with valid input");
|
||||
|
||||
let verifier_public_values = zkvm
|
||||
@@ -45,126 +36,62 @@ pub fn run_zkvm_prove(zkvm: &impl zkVM, io: &impl Io) -> PublicValues {
|
||||
|
||||
assert_eq!(prover_public_values, verifier_public_values);
|
||||
|
||||
assert_eq!(
|
||||
io.deserialize_outputs(&zkvm, &verifier_public_values),
|
||||
io.outputs()
|
||||
);
|
||||
test_case.assert_output(&verifier_public_values);
|
||||
|
||||
verifier_public_values
|
||||
}
|
||||
|
||||
/// The basic program takes 2 inputs:
|
||||
/// - `Vec<u8>` - random bytes
|
||||
/// - [`BasicStruct`] - structure filled with random values
|
||||
///
|
||||
/// Commit 2 outputs:
|
||||
/// - `Vec<u8>` that should be reverse of the input random bytes.
|
||||
/// - [`BasicStruct`] that should be computed by [`BasicStruct::output`].
|
||||
#[derive(Clone)]
|
||||
pub struct BasicProgramIo {
|
||||
bytes: Vec<u8>,
|
||||
basic_struct: BasicStruct,
|
||||
/// Test case for specific [`Program`] that provides serialized
|
||||
/// [`Program::Input`], and is able to assert if the [`PublicValues`] returned
|
||||
/// by [`zkVM`] methods is correct or not.
|
||||
pub trait TestCase {
|
||||
fn serialized_input(&self) -> Vec<u8>;
|
||||
|
||||
fn assert_output(&self, public_values: &[u8]);
|
||||
}
|
||||
|
||||
impl Io for BasicProgramIo {
|
||||
type Output = (Vec<u8>, BasicStruct);
|
||||
|
||||
fn inputs(&self) -> Input {
|
||||
let mut inputs = Input::new();
|
||||
inputs.write_bytes(self.bytes.clone());
|
||||
inputs.write(self.basic_struct.clone());
|
||||
inputs
|
||||
/// Auto-implementation [`TestCase`] for [`ProgramInput`] that can be shared
|
||||
/// between host and guest, if the guest is also written in Rust.
|
||||
impl<T: ProgramInput> TestCase for T {
|
||||
fn serialized_input(&self) -> Vec<u8> {
|
||||
T::Program::io_serde().serialize(&self.clone()).unwrap()
|
||||
}
|
||||
|
||||
fn outputs(&self) -> Self::Output {
|
||||
BasicProgramCore::outputs((self.bytes.clone(), self.basic_struct.clone()))
|
||||
}
|
||||
|
||||
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)
|
||||
fn assert_output(&self, public_values: &[u8]) {
|
||||
assert_eq!(
|
||||
T::Program::compute(self.clone()),
|
||||
T::Program::io_serde().deserialize(public_values).unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl BasicProgramIo {
|
||||
pub fn valid() -> Self {
|
||||
let rng = &mut rng();
|
||||
Self {
|
||||
bytes: rng
|
||||
.random_iter()
|
||||
.take(BasicProgramCore::BYTES_LENGTH)
|
||||
.collect(),
|
||||
basic_struct: BasicStruct::random(rng),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_output_hashed_io(self) -> impl Io {
|
||||
OutputHashedIo::new(self, BasicProgramCore::sha256_outputs)
|
||||
}
|
||||
|
||||
/// 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; BasicProgramCore::BYTES_LENGTH + 1]);
|
||||
inputs.write(BasicStruct::default());
|
||||
inputs
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OutputHashedIo<T, H, D> {
|
||||
/// Wrapper for [`TestCase`] that asserts output to be hashed.
|
||||
pub struct OutputHashedTestCase<T, D> {
|
||||
inner: T,
|
||||
hasher: H,
|
||||
_marker: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<T, H, D> OutputHashedIo<T, H, D> {
|
||||
pub fn new(inner: T, hasher: H) -> Self {
|
||||
impl<T, D> OutputHashedTestCase<T, D> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
hasher,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, H, D> Io for OutputHashedIo<T, H, D>
|
||||
impl<T, D> TestCase for OutputHashedTestCase<T, D>
|
||||
where
|
||||
T: Io,
|
||||
H: Fn(T::Output) -> D,
|
||||
D: Clone + Debug + Default + PartialEq + AsMut<[u8]>,
|
||||
T: ProgramInput,
|
||||
D: Digest,
|
||||
{
|
||||
type Output = D;
|
||||
|
||||
fn inputs(&self) -> Input {
|
||||
self.inner.inputs()
|
||||
fn serialized_input(&self) -> Vec<u8> {
|
||||
self.inner.serialized_input()
|
||||
}
|
||||
|
||||
fn outputs(&self) -> Self::Output {
|
||||
(self.hasher)(self.inner.outputs())
|
||||
}
|
||||
|
||||
fn deserialize_outputs(&self, _: &impl zkVM, bytes: &[u8]) -> Self::Output {
|
||||
let mut digest = D::default();
|
||||
assert_eq!(digest.as_mut().len(), bytes.len());
|
||||
digest.as_mut().copy_from_slice(bytes);
|
||||
digest
|
||||
fn assert_output(&self, public_values: &[u8]) {
|
||||
let output = T::Program::compute(self.inner.clone());
|
||||
let digest = D::digest(T::Program::io_serde().serialize(&output).unwrap());
|
||||
assert_eq!(digest.as_slice(), public_values)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
extern crate alloc;
|
||||
|
||||
pub mod guest;
|
||||
pub mod program;
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
pub mod host;
|
||||
|
||||
28
crates/test-utils/src/program.rs
Normal file
28
crates/test-utils/src/program.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use crate::guest::Platform;
|
||||
use core::fmt::Debug;
|
||||
use ere_io_serde::IoSerde;
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
|
||||
pub mod basic;
|
||||
|
||||
/// Program that can be ran given [`Platform`] implementation.
|
||||
pub trait Program {
|
||||
type Input: Serialize + DeserializeOwned;
|
||||
type Output: Debug + PartialEq + Serialize + DeserializeOwned;
|
||||
|
||||
fn io_serde() -> impl IoSerde;
|
||||
|
||||
fn compute(input: Self::Input) -> Self::Output;
|
||||
|
||||
fn run<P: Platform>() {
|
||||
let io_serde = Self::io_serde();
|
||||
let input = io_serde.deserialize(&P::read_input()).unwrap();
|
||||
let output = io_serde.serialize(&Self::compute(input)).unwrap();
|
||||
P::write_output(&output);
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Program::Input`] that has [`TestCase`] auto-implemented.
|
||||
pub trait ProgramInput: Clone + Serialize {
|
||||
type Program: Program<Input = Self>;
|
||||
}
|
||||
95
crates/test-utils/src/program/basic.rs
Normal file
95
crates/test-utils/src/program/basic.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use crate::program::Program;
|
||||
use alloc::vec::Vec;
|
||||
use core::panic;
|
||||
use ere_io_serde::{IoSerde, bincode::Bincode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The basic program takes `BasicProgramInput` as input, and computes
|
||||
/// `BasicProgramOutput` as output.
|
||||
pub struct BasicProgram;
|
||||
|
||||
impl Program for BasicProgram {
|
||||
type Input = BasicProgramInput;
|
||||
type Output = BasicProgramOutput;
|
||||
|
||||
fn io_serde() -> impl IoSerde {
|
||||
Bincode::legacy()
|
||||
}
|
||||
|
||||
fn compute(input: BasicProgramInput) -> BasicProgramOutput {
|
||||
if input.should_panic {
|
||||
panic!("invalid data");
|
||||
}
|
||||
BasicProgramOutput {
|
||||
a: input.a.wrapping_add(1),
|
||||
b: input.b.wrapping_add(1),
|
||||
c: input.c.wrapping_add(1),
|
||||
d: input.d.wrapping_add(1),
|
||||
e: input.e.iter().map(|byte| byte.wrapping_add(1)).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct BasicProgramInput {
|
||||
pub should_panic: bool,
|
||||
pub a: u8,
|
||||
pub b: u16,
|
||||
pub c: u32,
|
||||
pub d: u64,
|
||||
pub e: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct BasicProgramOutput {
|
||||
pub e: Vec<u8>,
|
||||
pub d: u64,
|
||||
pub c: u32,
|
||||
pub b: u16,
|
||||
pub a: u8,
|
||||
}
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
mod host {
|
||||
use crate::{
|
||||
host::{OutputHashedTestCase, TestCase},
|
||||
program::{
|
||||
ProgramInput,
|
||||
basic::{BasicProgram, BasicProgramInput},
|
||||
},
|
||||
};
|
||||
use rand::{Rng, rng};
|
||||
use sha2::Sha256;
|
||||
|
||||
impl ProgramInput for BasicProgramInput {
|
||||
type Program = BasicProgram;
|
||||
}
|
||||
|
||||
impl BasicProgramInput {
|
||||
pub fn valid() -> Self {
|
||||
let mut rng = rng();
|
||||
let n = rng.random_range(16..32);
|
||||
Self {
|
||||
should_panic: false,
|
||||
a: rng.random(),
|
||||
b: rng.random(),
|
||||
c: rng.random(),
|
||||
d: rng.random(),
|
||||
e: rng.random_iter().take(n).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Invalid input that causes panic in guest program.
|
||||
pub fn invalid() -> Self {
|
||||
Self {
|
||||
should_panic: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap into [`OutputHashedTestCase`] with [`Sha256`].
|
||||
pub fn into_output_sha256(self) -> impl TestCase {
|
||||
OutputHashedTestCase::<_, Sha256>::new(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
auto_impl.workspace = true
|
||||
erased-serde.workspace = true
|
||||
indexmap = { workspace = true, features = ["serde"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
@@ -17,7 +16,7 @@ thiserror.workspace = true
|
||||
clap = { workspace = true, features = ["derive"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
bincode.workspace = true
|
||||
bincode = { workspace = true, features = ["alloc", "serde"] }
|
||||
serde_json.workspace = true
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
use erased_serde::Serialize as ErasedSerialize;
|
||||
use serde::Serialize;
|
||||
use std::{fmt::Debug, sync::Arc};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum InputItem {
|
||||
/// A serializable object stored as a trait object
|
||||
Object(Arc<dyn ErasedSerialize + Send + Sync>),
|
||||
/// A serialized object with zkvm specific serializer.
|
||||
///
|
||||
/// This is only for `ere-dockerized` to serialize the inputs to be able to
|
||||
/// pass to `ere-server` to do the actual action, in normal case this should
|
||||
/// be avoided, instead [`InputItem::Object`] should be used.
|
||||
SerializedObject(Vec<u8>),
|
||||
/// Serialized bytes with opaque serializer (e.g. bincode)
|
||||
Bytes(Vec<u8>),
|
||||
}
|
||||
|
||||
impl Debug for InputItem {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
InputItem::Object(_) => f.write_str("Object(<erased>)"),
|
||||
InputItem::SerializedObject(bytes) => {
|
||||
f.debug_tuple("SerializedObject").field(bytes).finish()
|
||||
}
|
||||
InputItem::Bytes(bytes) => f.debug_tuple("Bytes").field(bytes).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a builder for input data to be passed to a ZKVM guest program.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Input {
|
||||
items: Vec<InputItem>,
|
||||
}
|
||||
impl Default for Input {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Input {
|
||||
/// Create an empty input buffer.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
items: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a serializable value as a trait object
|
||||
pub fn write<T: Serialize + Send + Sync + 'static>(&mut self, value: T) {
|
||||
self.items.push(InputItem::Object(Arc::new(value)));
|
||||
}
|
||||
|
||||
/// Write pre-serialized bytes directly
|
||||
pub fn write_bytes(&mut self, bytes: Vec<u8>) {
|
||||
self.items.push(InputItem::Bytes(bytes));
|
||||
}
|
||||
|
||||
/// Get the number of items stored
|
||||
pub fn len(&self) -> usize {
|
||||
self.items.len()
|
||||
}
|
||||
|
||||
/// Check if the buffer is empty
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.items.is_empty()
|
||||
}
|
||||
|
||||
/// Iterate over the items
|
||||
pub fn iter(&self) -> std::slice::Iter<'_, InputItem> {
|
||||
self.items.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<InputItem>> for Input {
|
||||
fn from(items: Vec<InputItem>) -> Self {
|
||||
Self { items }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod input_erased_tests {
|
||||
use super::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
struct Person {
|
||||
name: String,
|
||||
age: u32,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_object() {
|
||||
let mut input = Input::new();
|
||||
|
||||
let person = Person {
|
||||
name: "Alice".to_string(),
|
||||
age: 30,
|
||||
};
|
||||
|
||||
input.write(person);
|
||||
assert_eq!(input.len(), 1);
|
||||
|
||||
match &input.items[0] {
|
||||
InputItem::Object(_) => (), // Success
|
||||
InputItem::SerializedObject(_) | InputItem::Bytes(_) => {
|
||||
panic!("Expected Object, got Bytes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_bytes() {
|
||||
let mut input = Input::new();
|
||||
|
||||
let bytes = vec![1, 2, 3, 4, 5];
|
||||
input.write_bytes(bytes.clone());
|
||||
|
||||
assert_eq!(input.len(), 1);
|
||||
|
||||
match &input.items[0] {
|
||||
InputItem::Bytes(stored_bytes) => assert_eq!(stored_bytes.to_vec(), bytes),
|
||||
InputItem::Object(_) | InputItem::SerializedObject(_) => {
|
||||
panic!("Expected Bytes, got Object")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mixed_usage() {
|
||||
let mut input = Input::new();
|
||||
|
||||
let person = Person {
|
||||
name: "Charlie".to_string(),
|
||||
age: 35,
|
||||
};
|
||||
|
||||
// Mix different write methods
|
||||
input.write(42i32); // Object
|
||||
let serialized = bincode::serialize(&person).unwrap();
|
||||
input.write_bytes(serialized); // Bytes (serialized)
|
||||
input.write_bytes(vec![10, 20, 30]); // Bytes (raw)
|
||||
input.write("hello".to_string()); // Object
|
||||
|
||||
assert_eq!(input.len(), 4);
|
||||
|
||||
// Verify types
|
||||
match &input.items[0] {
|
||||
InputItem::Object(_) => (),
|
||||
_ => panic!(),
|
||||
}
|
||||
match &input.items[1] {
|
||||
InputItem::Bytes(_) => (),
|
||||
_ => panic!(),
|
||||
}
|
||||
match &input.items[2] {
|
||||
InputItem::Bytes(_) => (),
|
||||
_ => panic!(),
|
||||
}
|
||||
match &input.items[3] {
|
||||
InputItem::Object(_) => (),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iteration() {
|
||||
let mut input = Input::new();
|
||||
|
||||
input.write(1);
|
||||
input.write(2);
|
||||
input.write_bytes(vec![3, 4, 5]);
|
||||
|
||||
let count = input.iter().count();
|
||||
assert_eq!(count, 3);
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,10 @@
|
||||
#![allow(clippy::double_parens)]
|
||||
|
||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||
use std::{io::Read, path::Path};
|
||||
use std::path::Path;
|
||||
use strum::{EnumDiscriminants, EnumIs, EnumTryAs, FromRepr};
|
||||
use thiserror::Error;
|
||||
|
||||
mod input;
|
||||
pub use input::{Input, InputItem};
|
||||
|
||||
mod reports;
|
||||
pub use reports::{ProgramExecutionReport, ProgramProvingReport};
|
||||
|
||||
@@ -144,13 +141,13 @@ impl Proof {
|
||||
/// Note that a zkVM instance is created for specific program, each zkVM
|
||||
/// implementation will have their own construction function.
|
||||
pub trait zkVM {
|
||||
/// Executes the program with the provided inputs.
|
||||
fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError>;
|
||||
/// Executes the program with the given input.
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError>;
|
||||
|
||||
/// Creates a proof of the program execution with given inputs.
|
||||
/// Creates a proof of the program execution with given input.
|
||||
fn prove(
|
||||
&self,
|
||||
inputs: &Input,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError>;
|
||||
|
||||
@@ -164,17 +161,4 @@ pub trait zkVM {
|
||||
|
||||
/// 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<R: Read, T: DeserializeOwned>(&self, reader: R) -> Result<T, zkVMError>;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
serde.workspace = true
|
||||
tempfile.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ impl Compiler for RustRv32ima {
|
||||
mod tests {
|
||||
use crate::{EreJolt, compiler::RustRv32ima};
|
||||
use ere_test_utils::host::testing_guest_directory;
|
||||
use ere_zkvm_interface::{Compiler, Input, ProverResourceType, zkVM};
|
||||
use ere_zkvm_interface::{Compiler, ProverResourceType, zkVM};
|
||||
|
||||
#[test]
|
||||
fn test_compile() {
|
||||
@@ -75,6 +75,6 @@ mod tests {
|
||||
let program = RustRv32ima.compile(&guest_directory).unwrap();
|
||||
let zkvm = EreJolt::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
zkvm.execute(&Input::new()).unwrap();
|
||||
zkvm.execute(&[]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ impl Compiler for RustRv32imaCustomized {
|
||||
mod tests {
|
||||
use crate::{EreJolt, compiler::RustRv32imaCustomized};
|
||||
use ere_test_utils::host::testing_guest_directory;
|
||||
use ere_zkvm_interface::{Compiler, Input, ProverResourceType, zkVM};
|
||||
use ere_zkvm_interface::{Compiler, ProverResourceType, zkVM};
|
||||
|
||||
#[test]
|
||||
fn test_compile() {
|
||||
@@ -63,6 +63,6 @@ mod tests {
|
||||
let program = RustRv32imaCustomized.compile(&guest_directory).unwrap();
|
||||
let zkvm = EreJolt::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
zkvm.execute(&Input::new()).unwrap();
|
||||
zkvm.execute(&[]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::{EreJoltProof, error::VerifyError};
|
||||
use common::constants::{DEFAULT_MAX_BYTECODE_SIZE, DEFAULT_MAX_TRACE_LENGTH, DEFAULT_MEMORY_SIZE};
|
||||
use ere_zkvm_interface::Input;
|
||||
use jolt::{
|
||||
Jolt, JoltHyperKZGProof, JoltProverPreprocessing, JoltVerifierPreprocessing, MemoryConfig,
|
||||
MemoryLayout, RV32IJoltVM, tracer::JoltDevice,
|
||||
@@ -43,7 +42,7 @@ pub fn preprocess_verifier(
|
||||
pub fn prove_generic(
|
||||
program: &jolt::host::Program,
|
||||
preprocessing: JoltProverPreprocessing<4, jolt::F, jolt::PCS, jolt::ProofTranscript>,
|
||||
_inputs: &Input,
|
||||
_input: &[u8],
|
||||
) -> EreJoltProof {
|
||||
let mut program = program.clone();
|
||||
|
||||
|
||||
@@ -7,15 +7,11 @@ use crate::{
|
||||
};
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
|
||||
use ere_zkvm_interface::{
|
||||
Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
};
|
||||
use jolt::{JoltHyperKZGProof, JoltProverPreprocessing, JoltVerifierPreprocessing};
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::{
|
||||
env, fs,
|
||||
io::{Cursor, Read},
|
||||
};
|
||||
use std::{env, fs, io::Cursor};
|
||||
use tempfile::TempDir;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
|
||||
@@ -52,10 +48,7 @@ impl EreJolt {
|
||||
}
|
||||
|
||||
impl zkVM for EreJolt {
|
||||
fn execute(
|
||||
&self,
|
||||
_inputs: &Input,
|
||||
) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, _input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
let (_tempdir, program) = program(&self.elf)?;
|
||||
|
||||
// TODO: Check how to pass private input to jolt, issue for tracking:
|
||||
@@ -71,7 +64,7 @@ impl zkVM for EreJolt {
|
||||
|
||||
fn prove(
|
||||
&self,
|
||||
inputs: &Input,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
if proof_kind != ProofKind::Compressed {
|
||||
@@ -81,7 +74,7 @@ impl zkVM for EreJolt {
|
||||
let (_tempdir, program) = program(&self.elf)?;
|
||||
|
||||
let now = std::time::Instant::now();
|
||||
let proof = prove_generic(&program, self.prover_preprocessing.clone(), inputs);
|
||||
let proof = prove_generic(&program, self.prover_preprocessing.clone(), input);
|
||||
let elapsed = now.elapsed();
|
||||
|
||||
let mut proof_bytes = Vec::new();
|
||||
@@ -122,11 +115,6 @@ impl zkVM for EreJolt {
|
||||
fn sdk_version(&self) -> &'static str {
|
||||
SDK_VERSION
|
||||
}
|
||||
|
||||
fn deserialize_from<R: Read, T: DeserializeOwned>(&self, _reader: R) -> Result<T, zkVMError> {
|
||||
// Issue for tracking: https://github.com/eth-act/ere/issues/4.
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Create `jolt::host::Program` by storing the compiled `elf` to a temporary
|
||||
|
||||
@@ -6,7 +6,7 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bincode.workspace = true
|
||||
bincode = { workspace = true, features = ["alloc", "serde"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror.workspace = true
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ pub enum ExecuteError {
|
||||
#[error("Invalid input format: {0}")]
|
||||
InvalidInput(String),
|
||||
#[error("Serialization failed")]
|
||||
Serialization(#[from] bincode::Error),
|
||||
Serialization(#[from] bincode::error::EncodeError),
|
||||
#[error("Failed to deserialize Miden program")]
|
||||
ProgramDeserialization(#[from] DeserializationError),
|
||||
}
|
||||
@@ -62,8 +62,8 @@ pub enum ProveError {
|
||||
Proving(#[from] ExecutionError),
|
||||
#[error("Invalid input format: {0}")]
|
||||
InvalidInput(String),
|
||||
#[error("Serialization failed")]
|
||||
Serialization(#[from] bincode::Error),
|
||||
#[error("Output serialization failed")]
|
||||
OutputSerialization(#[from] bincode::error::EncodeError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -73,5 +73,7 @@ pub enum VerifyError {
|
||||
#[error("Proof or associated data deserialization failed")]
|
||||
MidenDeserialization(#[from] DeserializationError),
|
||||
#[error("Proof bundle deserialization failed")]
|
||||
BundleDeserialization(#[from] bincode::Error),
|
||||
BundleDeserialization(#[from] bincode::error::DecodeError),
|
||||
#[error("Output serialization failed")]
|
||||
OutputSerialization(#[from] bincode::error::EncodeError),
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
use crate::error::{ExecuteError, MidenError};
|
||||
use ere_zkvm_interface::{Input, InputItem, PublicValues};
|
||||
use miden_processor::{AdviceInputs, StackInputs, StackOutputs};
|
||||
|
||||
/// Returns Miden compatible inputs from `ere_zkvm_interface::Input`.
|
||||
///
|
||||
/// All inputs are serialized and concatenated, then placed onto the advice tape.
|
||||
/// The stack is left empty.
|
||||
pub fn generate_miden_inputs(inputs: &Input) -> Result<(StackInputs, AdviceInputs), MidenError> {
|
||||
let mut all_bytes = Vec::new();
|
||||
|
||||
for item in inputs.iter() {
|
||||
match item {
|
||||
InputItem::Object(obj) => {
|
||||
bincode::serialize_into(&mut all_bytes, &**obj)
|
||||
.map_err(ExecuteError::Serialization)?;
|
||||
}
|
||||
InputItem::SerializedObject(bytes) | InputItem::Bytes(bytes) => {
|
||||
all_bytes.extend_from_slice(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the byte stream into u64 words for the Miden VM.
|
||||
let advice_words: Vec<u64> = {
|
||||
let mut words: Vec<u64> = all_bytes
|
||||
.chunks_exact(8)
|
||||
.map(|chunk| u64::from_le_bytes(chunk.try_into().unwrap()))
|
||||
.collect();
|
||||
|
||||
let remainder = all_bytes.chunks_exact(8).remainder();
|
||||
if !remainder.is_empty() {
|
||||
let mut last_chunk = [0u8; 8];
|
||||
last_chunk[..remainder.len()].copy_from_slice(remainder);
|
||||
words.push(u64::from_le_bytes(last_chunk));
|
||||
}
|
||||
|
||||
words
|
||||
};
|
||||
|
||||
let advice_inputs = AdviceInputs::default()
|
||||
.with_stack_values(advice_words)
|
||||
.map_err(|e| ExecuteError::InvalidInput(e.to_string()))?;
|
||||
|
||||
Ok((StackInputs::default(), advice_inputs))
|
||||
}
|
||||
|
||||
// Convert Miden stack outputs to public values
|
||||
pub fn outputs_to_public_values(outputs: &StackOutputs) -> Result<PublicValues, bincode::Error> {
|
||||
let output_ints: Vec<u64> = outputs.iter().map(|f| f.as_int()).collect();
|
||||
bincode::serialize(&output_ints)
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
use crate::{
|
||||
compiler::MidenProgram,
|
||||
error::{ExecuteError, MidenError, VerifyError},
|
||||
io::{generate_miden_inputs, outputs_to_public_values},
|
||||
error::{ExecuteError, MidenError, ProveError, VerifyError},
|
||||
};
|
||||
use ere_zkvm_interface::{
|
||||
Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
};
|
||||
use miden_core::{
|
||||
@@ -14,17 +13,18 @@ use miden_core::{
|
||||
use miden_processor::{
|
||||
DefaultHost, ExecutionOptions, ProgramInfo, StackInputs, StackOutputs, execute as miden_execute,
|
||||
};
|
||||
use miden_prover::{ExecutionProof, ProvingOptions, prove as miden_prove};
|
||||
use miden_prover::{AdviceInputs, ExecutionProof, ProvingOptions, prove as miden_prove};
|
||||
use miden_stdlib::StdLibrary;
|
||||
use miden_verifier::verify as miden_verify;
|
||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||
use std::{env, io::Read, time::Instant};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{env, time::Instant};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
|
||||
|
||||
pub mod compiler;
|
||||
pub mod error;
|
||||
mod io;
|
||||
|
||||
pub use miden_core::{Felt, FieldElement};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct MidenProofBundle {
|
||||
@@ -33,6 +33,14 @@ struct MidenProofBundle {
|
||||
proof: Vec<u8>,
|
||||
}
|
||||
|
||||
/// [`zkVM`] implementation for Miden.
|
||||
///
|
||||
/// Miden VM takes list of field elements as input instead of bytes, so in
|
||||
/// [`zkVM::execute`] and [`zkVM::prove`] we require the given `input` is built
|
||||
/// from [`felts_to_bytes`].
|
||||
/// Similarly, the output values of Miden is also list of field elements, to
|
||||
/// be compatible with [`zkVM`], we convert it into [`PublicValues`] by
|
||||
/// [`felts_to_bytes`] as well.
|
||||
pub struct EreMiden {
|
||||
program: Program,
|
||||
}
|
||||
@@ -54,8 +62,12 @@ impl EreMiden {
|
||||
}
|
||||
|
||||
impl zkVM for EreMiden {
|
||||
fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
let (stack_inputs, advice_inputs) = generate_miden_inputs(inputs)?;
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
let stack_inputs = StackInputs::default();
|
||||
let advice_inputs = AdviceInputs::default().with_stack(
|
||||
bytes_to_felts(input)
|
||||
.map_err(|err| MidenError::Execute(ExecuteError::InvalidInput(err)))?,
|
||||
);
|
||||
let mut host = Self::setup_host()?;
|
||||
|
||||
let start = Instant::now();
|
||||
@@ -68,8 +80,7 @@ impl zkVM for EreMiden {
|
||||
)
|
||||
.map_err(|e| MidenError::Execute(e.into()))?;
|
||||
|
||||
let public_values = outputs_to_public_values(trace.stack_outputs())
|
||||
.map_err(|e| MidenError::Execute(e.into()))?;
|
||||
let public_values = felts_to_bytes(trace.stack_outputs().as_slice());
|
||||
|
||||
let report = ProgramExecutionReport {
|
||||
total_num_cycles: trace.trace_len_summary().main_trace_len() as u64,
|
||||
@@ -82,14 +93,18 @@ impl zkVM for EreMiden {
|
||||
|
||||
fn prove(
|
||||
&self,
|
||||
inputs: &Input,
|
||||
input: &[u8],
|
||||
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 stack_inputs = StackInputs::default();
|
||||
let advice_inputs = AdviceInputs::default().with_stack(
|
||||
bytes_to_felts(input)
|
||||
.map_err(|err| MidenError::Prove(ProveError::InvalidInput(err)))?,
|
||||
);
|
||||
let mut host = Self::setup_host()?;
|
||||
|
||||
let start = Instant::now();
|
||||
@@ -104,8 +119,7 @@ impl zkVM for EreMiden {
|
||||
)
|
||||
.map_err(|e| MidenError::Prove(e.into()))?;
|
||||
|
||||
let public_values =
|
||||
outputs_to_public_values(&stack_outputs).map_err(|e| MidenError::Prove(e.into()))?;
|
||||
let public_values = felts_to_bytes(stack_outputs.as_slice());
|
||||
|
||||
let bundle = MidenProofBundle {
|
||||
stack_inputs: stack_inputs.to_bytes(),
|
||||
@@ -113,7 +127,8 @@ impl zkVM for EreMiden {
|
||||
proof: proof.to_bytes(),
|
||||
};
|
||||
|
||||
let proof_bytes = bincode::serialize(&bundle).map_err(|e| MidenError::Prove(e.into()))?;
|
||||
let proof_bytes = bincode::serde::encode_to_vec(&bundle, bincode::config::legacy())
|
||||
.map_err(|e| MidenError::Prove(e.into()))?;
|
||||
|
||||
Ok((
|
||||
public_values,
|
||||
@@ -127,8 +142,9 @@ impl zkVM for EreMiden {
|
||||
return Err(zkVMError::other("Only Compressed proof kind is supported."));
|
||||
};
|
||||
|
||||
let bundle: MidenProofBundle = bincode::deserialize(proof)
|
||||
.map_err(|e| MidenError::Verify(VerifyError::BundleDeserialization(e)))?;
|
||||
let (bundle, _): (MidenProofBundle, _) =
|
||||
bincode::serde::decode_from_slice(proof, bincode::config::legacy())
|
||||
.map_err(|e| MidenError::Verify(VerifyError::BundleDeserialization(e)))?;
|
||||
|
||||
let program_info: ProgramInfo = self.program.clone().into();
|
||||
|
||||
@@ -147,12 +163,7 @@ impl zkVM for EreMiden {
|
||||
)
|
||||
.map_err(|e| MidenError::Verify(e.into()))?;
|
||||
|
||||
Ok(outputs_to_public_values(&stack_outputs)
|
||||
.map_err(|e| MidenError::Verify(VerifyError::BundleDeserialization(e)))?)
|
||||
}
|
||||
|
||||
fn deserialize_from<R: Read, T: DeserializeOwned>(&self, reader: R) -> Result<T, zkVMError> {
|
||||
bincode::deserialize_from(reader).map_err(|e| MidenError::Execute(e.into()).into())
|
||||
Ok(felts_to_bytes(stack_outputs.as_slice()))
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
@@ -164,14 +175,38 @@ impl zkVM for EreMiden {
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert Miden field elements into bytes
|
||||
pub fn felts_to_bytes(felts: &[Felt]) -> Vec<u8> {
|
||||
felts
|
||||
.iter()
|
||||
.flat_map(|felt| felt.as_int().to_le_bytes())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Convert bytes into Miden field elements.
|
||||
pub fn bytes_to_felts(bytes: &[u8]) -> Result<Vec<Felt>, String> {
|
||||
if bytes.len() % 8 != 0 {
|
||||
return Err(format!(
|
||||
"Invalid bytes length {}, expected multiple of 8",
|
||||
bytes.len()
|
||||
));
|
||||
}
|
||||
bytes
|
||||
.chunks(8)
|
||||
.map(|bytes| Felt::try_from(u64::from_le_bytes(bytes.try_into().unwrap())))
|
||||
.collect::<Result<Vec<Felt>, _>>()
|
||||
.map_err(|err| err.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
EreMiden,
|
||||
EreMiden, Felt, FieldElement, bytes_to_felts,
|
||||
compiler::{MidenAsm, MidenProgram},
|
||||
felts_to_bytes,
|
||||
};
|
||||
use ere_test_utils::host::testing_guest_directory;
|
||||
use ere_zkvm_interface::{Compiler, Input, ProofKind, ProverResourceType, zkVM};
|
||||
use ere_zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
|
||||
|
||||
fn load_miden_program(guest_name: &str) -> MidenProgram {
|
||||
MidenAsm
|
||||
@@ -184,25 +219,21 @@ mod tests {
|
||||
let program = load_miden_program("add");
|
||||
let zkvm = EreMiden::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let const_a = 2518446814u64;
|
||||
let const_b = 1949327098u64;
|
||||
let const_a = -Felt::ONE;
|
||||
let const_b = Felt::ONE / Felt::ONE.double();
|
||||
let expected_sum = const_a + const_b;
|
||||
|
||||
let mut inputs = Input::new();
|
||||
inputs.write(const_a);
|
||||
inputs.write(const_b);
|
||||
let input = felts_to_bytes(&[const_a, const_b]);
|
||||
|
||||
// Prove
|
||||
let (prover_public_values, proof, _) = zkvm.prove(&inputs, ProofKind::default()).unwrap();
|
||||
let (prover_public_values, proof, _) = zkvm.prove(&input, ProofKind::default()).unwrap();
|
||||
|
||||
// Verify
|
||||
let verifier_public_values = zkvm.verify(&proof).unwrap();
|
||||
assert_eq!(prover_public_values, verifier_public_values,);
|
||||
assert_eq!(prover_public_values, verifier_public_values);
|
||||
|
||||
// Assert output
|
||||
let output: Vec<u64> = zkvm
|
||||
.deserialize_from(verifier_public_values.as_slice())
|
||||
.unwrap();
|
||||
let output = bytes_to_felts(&verifier_public_values).unwrap();
|
||||
assert_eq!(output[0], expected_sum);
|
||||
}
|
||||
|
||||
@@ -211,38 +242,32 @@ mod tests {
|
||||
let program = load_miden_program("fib");
|
||||
let zkvm = EreMiden::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let n_iterations = 50u64;
|
||||
let expected_fib = 12_586_269_025u64;
|
||||
let n_iterations = 50u32;
|
||||
let expected_fib = Felt::try_from(12_586_269_025u64).unwrap();
|
||||
|
||||
let mut inputs = Input::new();
|
||||
inputs.write(0u64);
|
||||
inputs.write(1u64);
|
||||
inputs.write(n_iterations);
|
||||
let input = felts_to_bytes(&[Felt::from(0u32), Felt::from(1u32), Felt::from(n_iterations)]);
|
||||
|
||||
// Prove
|
||||
let (prover_public_values, proof, _) = zkvm.prove(&inputs, ProofKind::default()).unwrap();
|
||||
let (prover_public_values, proof, _) = zkvm.prove(&input, ProofKind::default()).unwrap();
|
||||
|
||||
// Verify
|
||||
let verifier_public_values = zkvm.verify(&proof).unwrap();
|
||||
assert_eq!(prover_public_values, verifier_public_values,);
|
||||
assert_eq!(prover_public_values, verifier_public_values);
|
||||
|
||||
// Assert output
|
||||
let output: Vec<u64> = zkvm
|
||||
.deserialize_from(verifier_public_values.as_slice())
|
||||
.unwrap();
|
||||
let output = bytes_to_felts(&verifier_public_values).unwrap();
|
||||
assert_eq!(output[0], expected_fib);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_inputs() {
|
||||
fn test_invalid_input() {
|
||||
let program = load_miden_program("add");
|
||||
let zkvm = EreMiden::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let empty_inputs = Input::new();
|
||||
let empty_inputs = Vec::new();
|
||||
assert!(zkvm.execute(&empty_inputs).is_err());
|
||||
|
||||
let mut insufficient_inputs = Input::new();
|
||||
insufficient_inputs.write(5u64);
|
||||
let insufficient_inputs = felts_to_bytes(&[Felt::from(5u32)]);
|
||||
assert!(zkvm.execute(&insufficient_inputs).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,15 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bincode.workspace = true
|
||||
bincode = { workspace = true, features = ["alloc", "serde"] }
|
||||
postcard.workspace = true
|
||||
serde.workspace = true
|
||||
thiserror.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
# Nexus dependencies
|
||||
nexus-sdk.workspace = true
|
||||
nexus-core.workspace = true
|
||||
nexus-sdk.workspace = true
|
||||
nexus-vm.workspace = true
|
||||
|
||||
# Local dependencies
|
||||
|
||||
@@ -39,7 +39,7 @@ pub enum ProveError {
|
||||
#[error("nexus execution failed: {0}")]
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
#[error("Serialising proof with `bincode` failed: {0}")]
|
||||
Bincode(#[from] bincode::Error),
|
||||
Bincode(#[from] bincode::error::EncodeError),
|
||||
#[error("Serialising input with `postcard` failed: {0}")]
|
||||
Postcard(String),
|
||||
}
|
||||
@@ -49,5 +49,5 @@ pub enum VerifyError {
|
||||
#[error("nexus verification failed: {0}")]
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
#[error("Deserialising proof failed: {0}")]
|
||||
Bincode(#[from] bincode::Error),
|
||||
Bincode(#[from] bincode::error::DecodeError),
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ use crate::{
|
||||
error::{NexusError, ProveError, VerifyError},
|
||||
};
|
||||
use ere_zkvm_interface::{
|
||||
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM, zkVMError,
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
};
|
||||
use nexus_core::nvm::{self, ElfFile};
|
||||
use nexus_sdk::{
|
||||
@@ -14,8 +14,8 @@ use nexus_sdk::{
|
||||
stwo::seq::{Proof as NexusProof, Stwo},
|
||||
};
|
||||
use nexus_vm::trace::Trace;
|
||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||
use std::{io::Read, time::Instant};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Instant;
|
||||
use tracing::info;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
|
||||
@@ -34,24 +34,25 @@ pub struct EreNexus {
|
||||
}
|
||||
|
||||
impl EreNexus {
|
||||
pub fn new(elf: NexusProgram, _resource_type: ProverResourceType) -> Self {
|
||||
pub fn new(elf: NexusProgram, resource: ProverResourceType) -> Self {
|
||||
if !matches!(resource, ProverResourceType::Cpu) {
|
||||
panic!("Network or GPU proving not yet implemented for Nexus. Use CPU resource type.");
|
||||
}
|
||||
Self { elf }
|
||||
}
|
||||
}
|
||||
|
||||
impl zkVM for EreNexus {
|
||||
fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
let elf = ElfFile::from_bytes(&self.elf)
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
|
||||
let input_bytes = serialize_inputs(inputs)?;
|
||||
|
||||
// Nexus sdk does not provide a trace, so we need to use core `nvm`
|
||||
// Encoding is copied directly from `prove_with_input`
|
||||
let mut private_encoded = if input_bytes.is_empty() {
|
||||
let mut private_encoded = if input.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
postcard::to_stdvec_cobs(&input_bytes)
|
||||
postcard::to_stdvec_cobs(&input)
|
||||
.map_err(|e| NexusError::Prove(ProveError::Postcard(e.to_string())))?
|
||||
};
|
||||
|
||||
@@ -64,24 +65,25 @@ impl zkVM for EreNexus {
|
||||
let start = Instant::now();
|
||||
let (view, trace) = nvm::k_trace(elf, &[], &[], private_encoded.as_slice(), 1)
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
let execution_duration = start.elapsed();
|
||||
|
||||
let public_values = view
|
||||
.public_output::<Vec<u8>>()
|
||||
.public_output()
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
|
||||
Ok((
|
||||
public_values,
|
||||
ProgramExecutionReport {
|
||||
total_num_cycles: trace.get_num_steps() as u64,
|
||||
region_cycles: Default::default(), // not available
|
||||
execution_duration: start.elapsed(),
|
||||
execution_duration,
|
||||
..Default::default()
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn prove(
|
||||
&self,
|
||||
inputs: &Input,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
if proof_kind != ProofKind::Compressed {
|
||||
@@ -94,29 +96,28 @@ impl zkVM for EreNexus {
|
||||
let prover =
|
||||
Stwo::new(&elf).map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
|
||||
let input_bytes = serialize_inputs(inputs)?;
|
||||
|
||||
let start = Instant::now();
|
||||
let (view, proof) = prover
|
||||
.prove_with_input::<Vec<u8>, ()>(&input_bytes, &())
|
||||
.prove_with_input(&input, &())
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
let proving_time = start.elapsed();
|
||||
|
||||
let public_values = view
|
||||
.public_output::<Vec<u8>>()
|
||||
.public_output()
|
||||
.map_err(|e| NexusError::Prove(ProveError::Client(e.into())))?;
|
||||
|
||||
let proof_bundle = NexusProofBundle {
|
||||
proof,
|
||||
public_values: public_values.clone(),
|
||||
public_values,
|
||||
};
|
||||
|
||||
let proof_bytes = bincode::serialize(&proof_bundle)
|
||||
let proof_bytes = bincode::serde::encode_to_vec(&proof_bundle, bincode::config::legacy())
|
||||
.map_err(|err| NexusError::Prove(ProveError::Bincode(err)))?;
|
||||
|
||||
Ok((
|
||||
public_values,
|
||||
proof_bundle.public_values,
|
||||
Proof::Compressed(proof_bytes),
|
||||
ProgramProvingReport::new(start.elapsed()),
|
||||
ProgramProvingReport::new(proving_time),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -127,8 +128,9 @@ impl zkVM for EreNexus {
|
||||
|
||||
info!("Verifying proof...");
|
||||
|
||||
let proof_bundle = bincode::deserialize::<NexusProofBundle>(proof)
|
||||
.map_err(|err| NexusError::Verify(VerifyError::Bincode(err)))?;
|
||||
let (proof_bundle, _): (NexusProofBundle, _) =
|
||||
bincode::serde::decode_from_slice(proof, bincode::config::legacy())
|
||||
.map_err(|err| NexusError::Verify(VerifyError::Bincode(err)))?;
|
||||
|
||||
proof_bundle
|
||||
.proof
|
||||
@@ -153,55 +155,21 @@ impl zkVM for EreNexus {
|
||||
fn sdk_version(&self) -> &'static str {
|
||||
SDK_VERSION
|
||||
}
|
||||
|
||||
fn deserialize_from<R: Read, T: DeserializeOwned>(&self, reader: R) -> Result<T, zkVMError> {
|
||||
let mut buf = vec![0; 1 << 20]; // allocate 1MiB as buffer.
|
||||
let (value, _) = postcard::from_io((reader, &mut buf)).map_err(zkVMError::other)?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes nexus program inputs
|
||||
pub fn serialize_inputs(inputs: &Input) -> Result<Vec<u8>, NexusError> {
|
||||
inputs
|
||||
.iter()
|
||||
.try_fold(Vec::new(), |mut acc, item| -> Result<Vec<u8>, NexusError> {
|
||||
match item {
|
||||
InputItem::Object(obj) => {
|
||||
let buffer = postcard::to_allocvec(obj.as_ref())
|
||||
.map_err(|e| NexusError::Prove(ProveError::Postcard(e.to_string())))?;
|
||||
acc.extend_from_slice(&buffer);
|
||||
Ok(acc)
|
||||
}
|
||||
InputItem::SerializedObject(bytes) => {
|
||||
acc.extend_from_slice(bytes);
|
||||
Ok(acc)
|
||||
}
|
||||
InputItem::Bytes(bytes) => {
|
||||
let buffer = postcard::to_allocvec(bytes)
|
||||
.map_err(|e| NexusError::Prove(ProveError::Postcard(e.to_string())))?;
|
||||
acc.extend_from_slice(&buffer);
|
||||
Ok(acc)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{EreNexus, compiler::RustRv32i};
|
||||
use ere_test_utils::host::{
|
||||
BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory,
|
||||
use crate::{EreNexus, NexusProgram, compiler::RustRv32i};
|
||||
use ere_test_utils::{
|
||||
host::{TestCase, run_zkvm_execute, run_zkvm_prove, testing_guest_directory},
|
||||
program::basic::BasicProgramInput,
|
||||
};
|
||||
use ere_zkvm_interface::{Compiler, Input, ProofKind, ProverResourceType, zkVM};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ere_zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
static BASIC_PROGRAM: OnceLock<Vec<u8>> = OnceLock::new();
|
||||
static FIB_PROGRAM: OnceLock<Vec<u8>> = OnceLock::new();
|
||||
|
||||
fn basic_program() -> Vec<u8> {
|
||||
BASIC_PROGRAM
|
||||
fn basic_program() -> NexusProgram {
|
||||
static PROGRAM: OnceLock<NexusProgram> = OnceLock::new();
|
||||
PROGRAM
|
||||
.get_or_init(|| {
|
||||
RustRv32i
|
||||
.compile(&testing_guest_directory("nexus", "basic"))
|
||||
@@ -210,36 +178,22 @@ mod tests {
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn fib_program() -> Vec<u8> {
|
||||
FIB_PROGRAM
|
||||
.get_or_init(|| {
|
||||
RustRv32i
|
||||
.compile(&testing_guest_directory("nexus", "fib"))
|
||||
.unwrap()
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_execute() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu);
|
||||
|
||||
let io = BasicProgramIo::valid();
|
||||
run_zkvm_execute(&zkvm, &io);
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_execute(&zkvm, &test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_execute_invalid_inputs() {
|
||||
fn test_execute_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu);
|
||||
|
||||
for inputs in [
|
||||
BasicProgramIo::empty(),
|
||||
BasicProgramIo::invalid_type(),
|
||||
BasicProgramIo::invalid_data(),
|
||||
] {
|
||||
zkvm.execute(&inputs).unwrap_err();
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.execute(&input).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,60 +202,17 @@ mod tests {
|
||||
let program = basic_program();
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu);
|
||||
|
||||
let io = BasicProgramIo::valid();
|
||||
run_zkvm_prove(&zkvm, &io);
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_prove(&zkvm, &test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prove_invalid_inputs() {
|
||||
fn test_prove_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu);
|
||||
|
||||
for inputs in [
|
||||
BasicProgramIo::empty(),
|
||||
BasicProgramIo::invalid_type(),
|
||||
BasicProgramIo::invalid_data(),
|
||||
] {
|
||||
zkvm.prove(&inputs, ProofKind::default()).unwrap_err();
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.prove(&input, ProofKind::default()).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fibonacci() {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct FibInput {
|
||||
n: u32,
|
||||
}
|
||||
|
||||
let program = fib_program();
|
||||
let zkvm = EreNexus::new(program, ProverResourceType::Cpu);
|
||||
|
||||
let mut input = Input::new();
|
||||
input.write(FibInput { n: 10 });
|
||||
|
||||
let (public_values, _report) = zkvm.execute(&input).expect("Execution failed");
|
||||
|
||||
let result: u32 = zkvm
|
||||
.deserialize_from(&public_values[..])
|
||||
.expect("Failed to deserialize output");
|
||||
assert_eq!(result, 55, "fib(10) should be 55");
|
||||
|
||||
let mut input = Input::new();
|
||||
input.write(FibInput { n: 0 });
|
||||
|
||||
let (public_values, _report) = zkvm.execute(&input).expect("Execution failed");
|
||||
let result: u32 = zkvm
|
||||
.deserialize_from(&public_values[..])
|
||||
.expect("Failed to deserialize output");
|
||||
assert_eq!(result, 0, "fib(0) should be 0");
|
||||
|
||||
let mut input = Input::new();
|
||||
input.write(FibInput { n: 1 });
|
||||
|
||||
let (public_values, _report) = zkvm.execute(&input).expect("Execution failed");
|
||||
let result: u32 = zkvm
|
||||
.deserialize_from(&public_values[..])
|
||||
.expect("Failed to deserialize output");
|
||||
assert_eq!(result, 1, "fib(1) should be 1");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
OpenVMProgram,
|
||||
compiler::OpenVMProgram,
|
||||
error::{CompileError, OpenVMError},
|
||||
};
|
||||
use ere_compile_utils::CargoBuildCmd;
|
||||
@@ -61,7 +61,7 @@ impl Compiler for RustRv32ima {
|
||||
mod tests {
|
||||
use crate::{EreOpenVM, compiler::RustRv32ima};
|
||||
use ere_test_utils::host::testing_guest_directory;
|
||||
use ere_zkvm_interface::{Compiler, Input, ProverResourceType, zkVM};
|
||||
use ere_zkvm_interface::{Compiler, ProverResourceType, zkVM};
|
||||
|
||||
#[test]
|
||||
fn test_compile() {
|
||||
@@ -76,6 +76,6 @@ mod tests {
|
||||
let program = RustRv32ima.compile(&guest_directory).unwrap();
|
||||
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
zkvm.execute(&Input::new()).unwrap();
|
||||
zkvm.execute(&[]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ use crate::{
|
||||
error::{CommonError, ExecuteError, OpenVMError, ProveError, VerifyError},
|
||||
};
|
||||
use ere_zkvm_interface::{
|
||||
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM, zkVMError,
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
};
|
||||
use openvm_circuit::arch::instructions::exe::VmExe;
|
||||
use openvm_continuations::verifier::internal::types::VmStarkProof;
|
||||
@@ -20,8 +20,7 @@ use openvm_sdk::{
|
||||
};
|
||||
use openvm_stark_sdk::openvm_stark_backend::p3_field::PrimeField32;
|
||||
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 std::{env, path::PathBuf, sync::Arc, time::Instant};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
|
||||
|
||||
@@ -103,9 +102,9 @@ impl EreOpenVM {
|
||||
}
|
||||
|
||||
impl zkVM for EreOpenVM {
|
||||
fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
let mut stdin = StdIn::default();
|
||||
serialize_inputs(&mut stdin, inputs);
|
||||
stdin.write_bytes(input);
|
||||
|
||||
let start = Instant::now();
|
||||
let public_values = self
|
||||
@@ -124,7 +123,7 @@ impl zkVM for EreOpenVM {
|
||||
|
||||
fn prove(
|
||||
&self,
|
||||
inputs: &Input,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
if proof_kind != ProofKind::Compressed {
|
||||
@@ -132,7 +131,7 @@ impl zkVM for EreOpenVM {
|
||||
}
|
||||
|
||||
let mut stdin = StdIn::default();
|
||||
serialize_inputs(&mut stdin, inputs);
|
||||
stdin.write_bytes(input);
|
||||
|
||||
let now = std::time::Instant::now();
|
||||
let (proof, app_commit) = match self.resource {
|
||||
@@ -194,21 +193,6 @@ impl zkVM for EreOpenVM {
|
||||
fn sdk_version(&self) -> &'static str {
|
||||
SDK_VERSION
|
||||
}
|
||||
|
||||
fn deserialize_from<R: Read, T: DeserializeOwned>(&self, _: R) -> Result<T, zkVMError> {
|
||||
unimplemented!("no native serialization in this platform")
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_inputs(stdin: &mut StdIn, inputs: &Input) {
|
||||
for input in inputs.iter() {
|
||||
match input {
|
||||
InputItem::Object(obj) => stdin.write(obj),
|
||||
InputItem::SerializedObject(bytes) | InputItem::Bytes(bytes) => {
|
||||
stdin.write_bytes(bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract public values in bytes from field elements.
|
||||
@@ -234,8 +218,9 @@ mod tests {
|
||||
EreOpenVM,
|
||||
compiler::{OpenVMProgram, RustRv32imaCustomized},
|
||||
};
|
||||
use ere_test_utils::host::{
|
||||
BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory,
|
||||
use ere_test_utils::{
|
||||
host::{TestCase, run_zkvm_execute, run_zkvm_prove, testing_guest_directory},
|
||||
program::basic::BasicProgramInput,
|
||||
};
|
||||
use ere_zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
|
||||
use std::sync::OnceLock;
|
||||
@@ -256,21 +241,17 @@ mod tests {
|
||||
let program = basic_program();
|
||||
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let io = BasicProgramIo::valid().into_output_hashed_io();
|
||||
run_zkvm_execute(&zkvm, &io);
|
||||
let test_case = BasicProgramInput::valid().into_output_sha256();
|
||||
run_zkvm_execute(&zkvm, &test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_execute_invalid_inputs() {
|
||||
fn test_execute_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
for inputs in [
|
||||
BasicProgramIo::empty(),
|
||||
BasicProgramIo::invalid_type(),
|
||||
BasicProgramIo::invalid_data(),
|
||||
] {
|
||||
zkvm.execute(&inputs).unwrap_err();
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.execute(&input).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,21 +260,17 @@ mod tests {
|
||||
let program = basic_program();
|
||||
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let io = BasicProgramIo::valid().into_output_hashed_io();
|
||||
run_zkvm_prove(&zkvm, &io);
|
||||
let test_case = BasicProgramInput::valid().into_output_sha256();
|
||||
run_zkvm_prove(&zkvm, &test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prove_invalid_inputs() {
|
||||
fn test_prove_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreOpenVM::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
for inputs in [
|
||||
BasicProgramIo::empty(),
|
||||
BasicProgramIo::invalid_type(),
|
||||
BasicProgramIo::invalid_data(),
|
||||
] {
|
||||
zkvm.prove(&inputs, ProofKind::default()).unwrap_err();
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.prove(&input, ProofKind::default()).unwrap_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
bincode.workspace = true
|
||||
bincode = { workspace = true, features = ["alloc", "serde"] }
|
||||
sha2.workspace = true
|
||||
serde.workspace = true
|
||||
tempfile.workspace = true
|
||||
|
||||
@@ -55,7 +55,7 @@ impl Compiler for RustRv32ima {
|
||||
mod tests {
|
||||
use crate::{ErePico, compiler::RustRv32ima};
|
||||
use ere_test_utils::host::testing_guest_directory;
|
||||
use ere_zkvm_interface::{Compiler, Input, ProverResourceType, zkVM};
|
||||
use ere_zkvm_interface::{Compiler, ProverResourceType, zkVM};
|
||||
|
||||
#[test]
|
||||
fn test_compile() {
|
||||
@@ -70,6 +70,6 @@ mod tests {
|
||||
let program = RustRv32ima.compile(&guest_directory).unwrap();
|
||||
let zkvm = ErePico::new(program, ProverResourceType::Cpu);
|
||||
|
||||
zkvm.execute(&Input::new()).unwrap();
|
||||
zkvm.execute(&[]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ pub enum ProveError {
|
||||
#[error("Pico proving failed: {0}")]
|
||||
Client(anyhow::Error),
|
||||
#[error("Serialising proof with `bincode` failed: {0}")]
|
||||
Bincode(#[from] bincode::Error),
|
||||
Bincode(#[from] bincode::error::EncodeError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -69,7 +69,7 @@ pub enum VerifyError {
|
||||
#[error("Pico verifying failed: {0}")]
|
||||
Client(anyhow::Error),
|
||||
#[error("Deserialising proof with `bincode` failed: {0}")]
|
||||
Bincode(#[from] bincode::Error),
|
||||
Bincode(#[from] bincode::error::DecodeError),
|
||||
#[error("Invalid base proof length {0}, expected 1")]
|
||||
InvalidBaseProofLength(usize),
|
||||
#[error("Invalid public values length {0}, expected at least 32")]
|
||||
|
||||
@@ -6,16 +6,14 @@ use crate::{
|
||||
error::{PicoError, ProveError, VerifyError},
|
||||
};
|
||||
use ere_zkvm_interface::{
|
||||
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM, zkVMError,
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
};
|
||||
use pico_p3_field::PrimeField32;
|
||||
use pico_vm::{configs::stark_config::KoalaBearPoseidon2, emulator::stdin::EmulatorStdinBuilder};
|
||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{
|
||||
env,
|
||||
io::Read,
|
||||
time::{self, Instant},
|
||||
};
|
||||
|
||||
@@ -49,11 +47,11 @@ impl ErePico {
|
||||
}
|
||||
|
||||
impl zkVM for ErePico {
|
||||
fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
let client = self.client();
|
||||
|
||||
let mut stdin = client.new_stdin_builder();
|
||||
serialize_inputs(&mut stdin, inputs);
|
||||
stdin.write_slice(input);
|
||||
|
||||
let start = Instant::now();
|
||||
let (total_num_cycles, public_values) = client.execute(stdin);
|
||||
@@ -70,7 +68,7 @@ impl zkVM for ErePico {
|
||||
|
||||
fn prove(
|
||||
&self,
|
||||
inputs: &Input,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<
|
||||
(
|
||||
@@ -87,7 +85,7 @@ impl zkVM for ErePico {
|
||||
let client = self.client();
|
||||
|
||||
let mut stdin = client.new_stdin_builder();
|
||||
serialize_inputs(&mut stdin, inputs);
|
||||
stdin.write_slice(input);
|
||||
|
||||
let now = time::Instant::now();
|
||||
let (public_values, proof) = client
|
||||
@@ -95,10 +93,13 @@ impl zkVM for ErePico {
|
||||
.map_err(|err| PicoError::Prove(ProveError::Client(err)))?;
|
||||
let elapsed = now.elapsed();
|
||||
|
||||
let proof_bytes = bincode::serialize(&PicoProofWithPublicValues {
|
||||
proof,
|
||||
public_values: public_values.clone(),
|
||||
})
|
||||
let proof_bytes = bincode::serde::encode_to_vec(
|
||||
&PicoProofWithPublicValues {
|
||||
proof,
|
||||
public_values: public_values.clone(),
|
||||
},
|
||||
bincode::config::legacy(),
|
||||
)
|
||||
.map_err(|err| PicoError::Prove(ProveError::Bincode(err)))?;
|
||||
|
||||
Ok((
|
||||
@@ -117,8 +118,9 @@ impl zkVM for ErePico {
|
||||
|
||||
let client = self.client();
|
||||
|
||||
let proof: PicoProofWithPublicValues = bincode::deserialize(proof)
|
||||
.map_err(|err| PicoError::Verify(VerifyError::Bincode(err)))?;
|
||||
let (proof, _): (PicoProofWithPublicValues, _) =
|
||||
bincode::serde::decode_from_slice(proof, bincode::config::legacy())
|
||||
.map_err(|err| PicoError::Verify(VerifyError::Bincode(err)))?;
|
||||
|
||||
client
|
||||
.verify(&proof.proof)
|
||||
@@ -140,21 +142,6 @@ impl zkVM for ErePico {
|
||||
fn sdk_version(&self) -> &'static str {
|
||||
SDK_VERSION
|
||||
}
|
||||
|
||||
fn deserialize_from<R: Read, T: DeserializeOwned>(&self, reader: R) -> Result<T, zkVMError> {
|
||||
bincode::deserialize_from(reader).map_err(zkVMError::other)
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_inputs(stdin: &mut EmulatorStdinBuilder<Vec<u8>, KoalaBearPoseidon2>, inputs: &Input) {
|
||||
for input in inputs.iter() {
|
||||
match input {
|
||||
InputItem::Object(serialize) => stdin.write(serialize),
|
||||
InputItem::SerializedObject(items) | InputItem::Bytes(items) => {
|
||||
stdin.write_slice(items)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract public values sha256 digest from base proof of compressed proof.
|
||||
@@ -186,8 +173,9 @@ mod tests {
|
||||
ErePico,
|
||||
compiler::{PicoProgram, RustRv32imaCustomized},
|
||||
};
|
||||
use ere_test_utils::host::{
|
||||
BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory,
|
||||
use ere_test_utils::{
|
||||
host::{TestCase, run_zkvm_execute, run_zkvm_prove, testing_guest_directory},
|
||||
program::basic::BasicProgramInput,
|
||||
};
|
||||
use ere_zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
|
||||
use std::{panic, sync::OnceLock};
|
||||
@@ -209,21 +197,19 @@ mod tests {
|
||||
let program = basic_program();
|
||||
let zkvm = ErePico::new(program, ProverResourceType::Cpu);
|
||||
|
||||
let io = BasicProgramIo::valid();
|
||||
run_zkvm_execute(&zkvm, &io);
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_execute(&zkvm, &test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_execute_invalid_inputs() {
|
||||
fn test_execute_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = ErePico::new(program, ProverResourceType::Cpu);
|
||||
|
||||
for inputs_gen in [
|
||||
BasicProgramIo::empty,
|
||||
BasicProgramIo::invalid_type,
|
||||
BasicProgramIo::invalid_data,
|
||||
] {
|
||||
panic::catch_unwind(|| zkvm.execute(&inputs_gen())).unwrap_err();
|
||||
// When guest panics Pico execute will also panics.
|
||||
// Issue for tracking: https://github.com/eth-act/ere/issues/172.
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
panic::catch_unwind(|| zkvm.execute(&input)).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,21 +218,19 @@ mod tests {
|
||||
let program = basic_program();
|
||||
let zkvm = ErePico::new(program, ProverResourceType::Cpu);
|
||||
|
||||
let io = BasicProgramIo::valid();
|
||||
run_zkvm_prove(&zkvm, &io);
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_prove(&zkvm, &test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prove_invalid_inputs() {
|
||||
fn test_prove_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = ErePico::new(program, ProverResourceType::Cpu);
|
||||
|
||||
for inputs_gen in [
|
||||
BasicProgramIo::empty,
|
||||
BasicProgramIo::invalid_type,
|
||||
BasicProgramIo::invalid_data,
|
||||
] {
|
||||
panic::catch_unwind(|| zkvm.prove(&inputs_gen(), ProofKind::default())).unwrap_err();
|
||||
// When guest panics Pico prove will also panics.
|
||||
// Issue for tracking: https://github.com/eth-act/ere/issues/172.
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
panic::catch_unwind(|| zkvm.prove(&input, ProofKind::default())).unwrap_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@ license.workspace = true
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
borsh.workspace = true
|
||||
bytemuck.workspace = true
|
||||
serde = { workspace = true, features = ["derive", "rc"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
@@ -23,6 +22,7 @@ ere-compile-utils.workspace = true
|
||||
ere-zkvm-interface.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
ere-io-serde.workspace = true
|
||||
ere-test-utils = { workspace = true, features = ["host"] }
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -66,7 +66,7 @@ impl Compiler for RustRv32ima {
|
||||
mod tests {
|
||||
use crate::{EreRisc0, compiler::RustRv32ima};
|
||||
use ere_test_utils::host::testing_guest_directory;
|
||||
use ere_zkvm_interface::{Compiler, Input, ProverResourceType, zkVM};
|
||||
use ere_zkvm_interface::{Compiler, ProverResourceType, zkVM};
|
||||
|
||||
#[test]
|
||||
fn test_compile() {
|
||||
@@ -81,6 +81,6 @@ mod tests {
|
||||
let program = RustRv32ima.compile(&guest_directory).unwrap();
|
||||
let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
zkvm.execute(&Input::new()).unwrap();
|
||||
zkvm.execute(&[]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
|
||||
use crate::{compiler::Risc0Program, output::deserialize_from};
|
||||
use crate::compiler::Risc0Program;
|
||||
use ere_zkvm_interface::{
|
||||
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM, zkVMError,
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
};
|
||||
use risc0_zkvm::{
|
||||
DEFAULT_MAX_PO2, DefaultProver, ExecutorEnv, ExecutorEnvBuilder, ExternalProver, InnerReceipt,
|
||||
ProverOpts, Receipt, default_executor, default_prover,
|
||||
DEFAULT_MAX_PO2, DefaultProver, ExecutorEnv, ExternalProver, InnerReceipt, ProverOpts, Receipt,
|
||||
default_executor, default_prover,
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::{env, io::Read, ops::RangeInclusive, rc::Rc, time::Instant};
|
||||
use std::{env, ops::RangeInclusive, rc::Rc, time::Instant};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
|
||||
|
||||
pub mod compiler;
|
||||
pub mod error;
|
||||
mod output;
|
||||
|
||||
/// Default logarithmic segment size from [`DEFAULT_SEGMENT_LIMIT_PO2`].
|
||||
///
|
||||
@@ -84,11 +82,12 @@ impl EreRisc0 {
|
||||
}
|
||||
|
||||
impl zkVM for EreRisc0 {
|
||||
fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
let executor = default_executor();
|
||||
let mut env = ExecutorEnv::builder();
|
||||
serialize_inputs(&mut env, inputs).map_err(zkVMError::other)?;
|
||||
let env = env.build().map_err(zkVMError::other)?;
|
||||
let env = ExecutorEnv::builder()
|
||||
.write_slice(input)
|
||||
.build()
|
||||
.map_err(zkVMError::other)?;
|
||||
|
||||
let start = Instant::now();
|
||||
let session_info = executor
|
||||
@@ -109,7 +108,7 @@ impl zkVM for EreRisc0 {
|
||||
|
||||
fn prove(
|
||||
&self,
|
||||
inputs: &Input,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
let prover = match self.resource {
|
||||
@@ -135,9 +134,8 @@ impl zkVM for EreRisc0 {
|
||||
}
|
||||
};
|
||||
|
||||
let mut env = ExecutorEnv::builder();
|
||||
serialize_inputs(&mut env, inputs).map_err(zkVMError::other)?;
|
||||
let env = env
|
||||
let env = ExecutorEnv::builder()
|
||||
.write_slice(input)
|
||||
.segment_limit_po2(self.segment_po2 as _)
|
||||
.keccak_max_po2(self.keccak_po2 as _)
|
||||
.map_err(zkVMError::other)?
|
||||
@@ -199,30 +197,6 @@ impl zkVM for EreRisc0 {
|
||||
fn sdk_version(&self) -> &'static str {
|
||||
SDK_VERSION
|
||||
}
|
||||
|
||||
fn deserialize_from<R: Read, T: DeserializeOwned>(&self, reader: R) -> Result<T, zkVMError> {
|
||||
deserialize_from(reader)
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_inputs(env: &mut ExecutorEnvBuilder, inputs: &Input) -> Result<(), anyhow::Error> {
|
||||
for input in inputs.iter() {
|
||||
match input {
|
||||
// Corresponding to `env.read::<T>()`.
|
||||
InputItem::Object(obj) => env.write(obj)?,
|
||||
// Corresponding to `env.read::<T>()`.
|
||||
//
|
||||
// Note that we call `write_slice` to append the bytes to the inputs
|
||||
// directly, to avoid double serailization.
|
||||
InputItem::SerializedObject(bytes) => env.write_slice(bytes),
|
||||
// Corresponding to `env.read_frame()`.
|
||||
//
|
||||
// Note that `write_frame` is different from `write_slice`, it
|
||||
// prepends the `bytes.len().to_le_bytes()`.
|
||||
InputItem::Bytes(bytes) => env.write_frame(bytes),
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -231,10 +205,11 @@ mod tests {
|
||||
EreRisc0,
|
||||
compiler::{Risc0Program, RustRv32imaCustomized},
|
||||
};
|
||||
use ere_test_utils::host::{
|
||||
BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory,
|
||||
use ere_test_utils::{
|
||||
host::{TestCase, run_zkvm_execute, run_zkvm_prove, testing_guest_directory},
|
||||
program::basic::BasicProgramInput,
|
||||
};
|
||||
use ere_zkvm_interface::{Compiler, Input, ProofKind, ProverResourceType, zkVM};
|
||||
use ere_zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
static BASIC_PROGRAM: OnceLock<Risc0Program> = OnceLock::new();
|
||||
@@ -254,21 +229,17 @@ mod tests {
|
||||
let program = basic_program();
|
||||
let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let io = BasicProgramIo::valid();
|
||||
run_zkvm_execute(&zkvm, &io);
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_execute(&zkvm, &test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_execute_invalid_inputs() {
|
||||
fn test_execute_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
for inputs in [
|
||||
BasicProgramIo::empty(),
|
||||
BasicProgramIo::invalid_type(),
|
||||
BasicProgramIo::invalid_data(),
|
||||
] {
|
||||
zkvm.execute(&inputs).unwrap_err();
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.execute(&input).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,21 +248,17 @@ mod tests {
|
||||
let program = basic_program();
|
||||
let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let io = BasicProgramIo::valid();
|
||||
run_zkvm_prove(&zkvm, &io);
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_prove(&zkvm, &test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prove_invalid_inputs() {
|
||||
fn test_prove_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreRisc0::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
for inputs in [
|
||||
BasicProgramIo::empty(),
|
||||
BasicProgramIo::invalid_type(),
|
||||
BasicProgramIo::invalid_data(),
|
||||
] {
|
||||
zkvm.prove(&inputs, ProofKind::default()).unwrap_err();
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.prove(&input, ProofKind::default()).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,11 +268,10 @@ mod tests {
|
||||
.compile(&testing_guest_directory("risc0", "allocs_alignment"))
|
||||
.unwrap();
|
||||
|
||||
for i in 1..=16_usize {
|
||||
for i in 1..=16_u32 {
|
||||
let zkvm = EreRisc0::new(program.clone(), ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let mut input = Input::new();
|
||||
input.write(i);
|
||||
let input = i.to_le_bytes();
|
||||
|
||||
if i.is_power_of_two() {
|
||||
zkvm.execute(&input)
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
use ere_zkvm_interface::zkVMError;
|
||||
use risc0_zkvm::serde::{Deserializer, Error, WordRead};
|
||||
use serde::de::{DeserializeOwned, Error as _};
|
||||
use std::io::Read;
|
||||
|
||||
pub fn deserialize_from<R: Read, T: DeserializeOwned>(reader: R) -> Result<T, zkVMError> {
|
||||
struct WordReadAdapter<R>(R);
|
||||
|
||||
impl<R: Read> WordRead for WordReadAdapter<R> {
|
||||
fn read_words(&mut self, words: &mut [u32]) -> Result<(), Error> {
|
||||
let bytes = bytemuck::cast_slice_mut(words);
|
||||
self.0.read_exact(bytes).map_err(Error::custom)
|
||||
}
|
||||
|
||||
fn read_padded_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> {
|
||||
let mut padded_bytes = vec![0u8; bytes.len().next_multiple_of(4) - bytes.len()];
|
||||
self.0.read_exact(bytes).map_err(Error::custom)?;
|
||||
self.0.read_exact(&mut padded_bytes).map_err(Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
T::deserialize(&mut Deserializer::new(WordReadAdapter(reader))).map_err(zkVMError::other)
|
||||
}
|
||||
@@ -6,8 +6,7 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bincode.workspace = true
|
||||
serde.workspace = true
|
||||
bincode = { workspace = true, features = ["alloc", "serde"] }
|
||||
tempfile.workspace = true
|
||||
thiserror.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
@@ -55,7 +55,7 @@ impl Compiler for RustRv32ima {
|
||||
mod tests {
|
||||
use crate::{EreSP1, compiler::RustRv32ima};
|
||||
use ere_test_utils::host::testing_guest_directory;
|
||||
use ere_zkvm_interface::{Compiler, Input, ProverResourceType, zkVM};
|
||||
use ere_zkvm_interface::{Compiler, ProverResourceType, zkVM};
|
||||
|
||||
#[test]
|
||||
fn test_compile() {
|
||||
@@ -70,6 +70,6 @@ mod tests {
|
||||
let program = RustRv32ima.compile(&guest_directory).unwrap();
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu);
|
||||
|
||||
zkvm.execute(&Input::new()).unwrap();
|
||||
zkvm.execute(&[]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,13 +73,13 @@ pub enum ProveError {
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
|
||||
#[error("Serialising proof with `bincode` failed: {0}")]
|
||||
Bincode(#[from] bincode::Error),
|
||||
Bincode(#[from] bincode::error::EncodeError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum VerifyError {
|
||||
#[error("Deserialising proof failed: {0}")]
|
||||
Bincode(#[from] bincode::Error),
|
||||
Bincode(#[from] bincode::error::DecodeError),
|
||||
|
||||
#[error("Invalid proof kind, expected: {}, got: {}", 0.to_string(), 1.to_string() )]
|
||||
InvalidProofKind(ProofKind, SP1ProofMode),
|
||||
|
||||
@@ -5,15 +5,14 @@ use crate::{
|
||||
error::{ExecuteError, ProveError, SP1Error, VerifyError},
|
||||
};
|
||||
use ere_zkvm_interface::{
|
||||
Input, InputItem, NetworkProverConfig, ProgramExecutionReport, ProgramProvingReport, Proof,
|
||||
ProofKind, ProverResourceType, PublicValues, zkVM, zkVMError,
|
||||
NetworkProverConfig, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM, zkVMError,
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
use sp1_sdk::{
|
||||
CpuProver, CudaProver, NetworkProver, Prover, ProverClient, SP1ProofMode,
|
||||
SP1ProofWithPublicValues, SP1ProvingKey, SP1Stdin, SP1VerifyingKey,
|
||||
};
|
||||
use std::{io::Read, time::Instant};
|
||||
use std::time::Instant;
|
||||
use tracing::info;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/name_and_sdk_version.rs"));
|
||||
@@ -145,9 +144,9 @@ impl EreSP1 {
|
||||
}
|
||||
|
||||
impl zkVM for EreSP1 {
|
||||
fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
let mut stdin = SP1Stdin::new();
|
||||
serialize_inputs(&mut stdin, inputs);
|
||||
stdin.write_slice(input);
|
||||
|
||||
let client = Self::create_client(&self.resource);
|
||||
let start = Instant::now();
|
||||
@@ -165,13 +164,13 @@ impl zkVM for EreSP1 {
|
||||
|
||||
fn prove(
|
||||
&self,
|
||||
inputs: &Input,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
info!("Generating proof…");
|
||||
|
||||
let mut stdin = SP1Stdin::new();
|
||||
serialize_inputs(&mut stdin, inputs);
|
||||
stdin.write_slice(input);
|
||||
|
||||
let mode = match proof_kind {
|
||||
ProofKind::Compressed => SP1ProofMode::Compressed,
|
||||
@@ -186,7 +185,8 @@ impl zkVM for EreSP1 {
|
||||
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)))?,
|
||||
bincode::serde::encode_to_vec(&proof, bincode::config::legacy())
|
||||
.map_err(|err| SP1Error::Prove(ProveError::Bincode(err)))?,
|
||||
);
|
||||
|
||||
Ok((
|
||||
@@ -201,8 +201,9 @@ impl zkVM for EreSP1 {
|
||||
|
||||
let proof_kind = proof.kind();
|
||||
|
||||
let proof: SP1ProofWithPublicValues = bincode::deserialize(proof.as_bytes())
|
||||
.map_err(|err| SP1Error::Verify(VerifyError::Bincode(err)))?;
|
||||
let (proof, _): (SP1ProofWithPublicValues, _) =
|
||||
bincode::serde::decode_from_slice(proof.as_bytes(), bincode::config::legacy())
|
||||
.map_err(|err| SP1Error::Verify(VerifyError::Bincode(err)))?;
|
||||
let inner_proof_kind = SP1ProofMode::from(&proof.proof);
|
||||
|
||||
if !matches!(
|
||||
@@ -231,28 +232,14 @@ impl zkVM for EreSP1 {
|
||||
fn sdk_version(&self) -> &'static str {
|
||||
SDK_VERSION
|
||||
}
|
||||
|
||||
fn deserialize_from<R: Read, T: DeserializeOwned>(&self, reader: R) -> Result<T, zkVMError> {
|
||||
bincode::deserialize_from(reader).map_err(zkVMError::other)
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_inputs(stdin: &mut SP1Stdin, inputs: &Input) {
|
||||
for input in inputs.iter() {
|
||||
match input {
|
||||
InputItem::Object(obj) => stdin.write(obj),
|
||||
InputItem::SerializedObject(bytes) | InputItem::Bytes(bytes) => {
|
||||
stdin.write_slice(bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{EreSP1, compiler::RustRv32imaCustomized};
|
||||
use ere_test_utils::host::{
|
||||
BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory,
|
||||
use ere_test_utils::{
|
||||
host::{TestCase, run_zkvm_execute, run_zkvm_prove, testing_guest_directory},
|
||||
program::basic::BasicProgramInput,
|
||||
};
|
||||
use ere_zkvm_interface::{Compiler, NetworkProverConfig, ProofKind, ProverResourceType, zkVM};
|
||||
use std::{panic, sync::OnceLock};
|
||||
@@ -274,21 +261,17 @@ mod tests {
|
||||
let program = basic_program();
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu);
|
||||
|
||||
let io = BasicProgramIo::valid();
|
||||
run_zkvm_execute(&zkvm, &io);
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_execute(&zkvm, &test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_execute_invalid_inputs() {
|
||||
fn test_execute_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu);
|
||||
|
||||
for inputs in [
|
||||
BasicProgramIo::empty(),
|
||||
BasicProgramIo::invalid_type(),
|
||||
BasicProgramIo::invalid_data(),
|
||||
] {
|
||||
zkvm.execute(&inputs).unwrap_err();
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.execute(&input).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,26 +280,19 @@ mod tests {
|
||||
let program = basic_program();
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu);
|
||||
|
||||
let io = BasicProgramIo::valid();
|
||||
run_zkvm_prove(&zkvm, &io);
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_prove(&zkvm, &test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prove_invalid_inputs() {
|
||||
fn test_prove_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Cpu);
|
||||
|
||||
// On invalid inputs SP1 prove will panics, the issue for tracking:
|
||||
// https://github.com/eth-act/ere/issues/16.
|
||||
//
|
||||
// Note that we iterate on methods because `InputItem::Object` doesn't
|
||||
// implement `RefUnwindSafe`.
|
||||
for inputs_gen in [
|
||||
BasicProgramIo::empty,
|
||||
BasicProgramIo::invalid_type,
|
||||
BasicProgramIo::invalid_data,
|
||||
] {
|
||||
panic::catch_unwind(|| zkvm.prove(&inputs_gen(), ProofKind::default())).unwrap_err();
|
||||
// When guest panics SP1 prove will also panics.
|
||||
// Issue for tracking: https://github.com/eth-act/ere/issues/172.
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
panic::catch_unwind(|| zkvm.prove(&input, ProofKind::default())).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,7 +313,7 @@ mod tests {
|
||||
let program = basic_program();
|
||||
let zkvm = EreSP1::new(program, ProverResourceType::Network(network_config));
|
||||
|
||||
let io = BasicProgramIo::valid();
|
||||
run_zkvm_prove(&zkvm, &io);
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_prove(&zkvm, &test_case);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bincode.workspace = true
|
||||
serde.workspace = true
|
||||
bincode = { workspace = true, features = ["std", "serde"] }
|
||||
thiserror.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ pub enum ExecuteError {
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ProveError {
|
||||
#[error("Serialising proof with `bincode` failed: {0}")]
|
||||
Bincode(#[from] bincode::Error),
|
||||
Bincode(#[from] bincode::error::EncodeError),
|
||||
|
||||
#[error("Ziren proving failed: {0}")]
|
||||
Client(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
@@ -76,7 +76,7 @@ pub enum ProveError {
|
||||
#[derive(Debug, Error)]
|
||||
pub enum VerifyError {
|
||||
#[error("Deserialising proof with `bincode` failed: {0}")]
|
||||
Bincode(#[from] bincode::Error),
|
||||
Bincode(#[from] bincode::error::DecodeError),
|
||||
|
||||
#[error("Invalid proof kind, expected: {}, got: {}", 0.to_string(), 1.to_string() )]
|
||||
InvalidProofKind(ProofKind, ZKMProofKind),
|
||||
|
||||
@@ -5,11 +5,10 @@ use crate::{
|
||||
error::{ExecuteError, ProveError, VerifyError, ZirenError},
|
||||
};
|
||||
use ere_zkvm_interface::{
|
||||
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM, zkVMError,
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::{io::Read, time::Instant};
|
||||
use std::time::Instant;
|
||||
use tracing::info;
|
||||
use zkm_sdk::{
|
||||
CpuProver, Prover, ZKMProofKind, ZKMProofWithPublicValues, ZKMProvingKey, ZKMStdin,
|
||||
@@ -41,9 +40,9 @@ impl EreZiren {
|
||||
}
|
||||
|
||||
impl zkVM for EreZiren {
|
||||
fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
let mut stdin = ZKMStdin::new();
|
||||
serialize_inputs(&mut stdin, inputs);
|
||||
stdin.write_slice(input);
|
||||
|
||||
let start = Instant::now();
|
||||
let (public_inputs, exec_report) = CpuProver::new()
|
||||
@@ -63,13 +62,13 @@ impl zkVM for EreZiren {
|
||||
|
||||
fn prove(
|
||||
&self,
|
||||
inputs: &Input,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
info!("Generating proof…");
|
||||
|
||||
let mut stdin = ZKMStdin::new();
|
||||
serialize_inputs(&mut stdin, inputs);
|
||||
stdin.write_slice(input);
|
||||
|
||||
let inner_proof_kind = match proof_kind {
|
||||
ProofKind::Compressed => ZKMProofKind::Compressed,
|
||||
@@ -85,7 +84,7 @@ impl zkVM for EreZiren {
|
||||
let public_values = proof.public_values.to_vec();
|
||||
let proof = Proof::new(
|
||||
proof_kind,
|
||||
bincode::serialize(&proof)
|
||||
bincode::serde::encode_to_vec(&proof, bincode::config::legacy())
|
||||
.map_err(|err| ZirenError::Prove(ProveError::Bincode(err)))?,
|
||||
);
|
||||
|
||||
@@ -101,8 +100,9 @@ impl zkVM for EreZiren {
|
||||
|
||||
let proof_kind = proof.kind();
|
||||
|
||||
let proof: ZKMProofWithPublicValues = bincode::deserialize(proof.as_bytes())
|
||||
.map_err(|err| ZirenError::Verify(VerifyError::Bincode(err)))?;
|
||||
let (proof, _): (ZKMProofWithPublicValues, _) =
|
||||
bincode::serde::decode_from_slice(proof.as_bytes(), bincode::config::legacy())
|
||||
.map_err(|err| ZirenError::Verify(VerifyError::Bincode(err)))?;
|
||||
let inner_proof_kind = ZKMProofKind::from(&proof.proof);
|
||||
|
||||
if !matches!(
|
||||
@@ -130,28 +130,14 @@ impl zkVM for EreZiren {
|
||||
fn sdk_version(&self) -> &'static str {
|
||||
SDK_VERSION
|
||||
}
|
||||
|
||||
fn deserialize_from<R: Read, T: DeserializeOwned>(&self, reader: R) -> Result<T, zkVMError> {
|
||||
bincode::deserialize_from(reader).map_err(zkVMError::other)
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_inputs(stdin: &mut ZKMStdin, inputs: &Input) {
|
||||
for input in inputs.iter() {
|
||||
match input {
|
||||
InputItem::Object(obj) => stdin.write(obj),
|
||||
InputItem::SerializedObject(bytes) | InputItem::Bytes(bytes) => {
|
||||
stdin.write_slice(bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{EreZiren, compiler::RustMips32r2Customized};
|
||||
use ere_test_utils::host::{
|
||||
BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory,
|
||||
use ere_test_utils::{
|
||||
host::{TestCase, run_zkvm_execute, run_zkvm_prove, testing_guest_directory},
|
||||
program::basic::BasicProgramInput,
|
||||
};
|
||||
use ere_zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
|
||||
use std::{panic, sync::OnceLock};
|
||||
@@ -173,30 +159,17 @@ mod tests {
|
||||
let program = basic_program();
|
||||
let zkvm = EreZiren::new(program, ProverResourceType::Cpu);
|
||||
|
||||
let io = BasicProgramIo::valid();
|
||||
run_zkvm_execute(&zkvm, &io);
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_execute(&zkvm, &test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_execute_invalid_inputs() {
|
||||
type F = fn() -> ere_zkvm_interface::Input;
|
||||
|
||||
fn test_execute_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreZiren::new(program, ProverResourceType::Cpu);
|
||||
|
||||
// Note that for some invalid cases the execution panics, but some not.
|
||||
for (inputs_gen, should_panic) in [
|
||||
// For empty input (insufficient input), the syscall reading input causes host to panics.
|
||||
(BasicProgramIo::empty as F, true),
|
||||
// For invalid type/data, the guest panics but handled properly by the host.
|
||||
(BasicProgramIo::invalid_type as F, false),
|
||||
(BasicProgramIo::invalid_data as F, false),
|
||||
] {
|
||||
if should_panic {
|
||||
panic::catch_unwind(|| zkvm.execute(&inputs_gen())).unwrap_err();
|
||||
} else {
|
||||
zkvm.execute(&inputs_gen()).unwrap_err();
|
||||
}
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.execute(&input).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,21 +178,19 @@ mod tests {
|
||||
let program = basic_program();
|
||||
let zkvm = EreZiren::new(program, ProverResourceType::Cpu);
|
||||
|
||||
let io = BasicProgramIo::valid();
|
||||
run_zkvm_prove(&zkvm, &io);
|
||||
let test_case = BasicProgramInput::valid();
|
||||
run_zkvm_prove(&zkvm, &test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prove_invalid_inputs() {
|
||||
fn test_prove_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreZiren::new(program, ProverResourceType::Cpu);
|
||||
|
||||
for inputs_gen in [
|
||||
BasicProgramIo::empty,
|
||||
BasicProgramIo::invalid_type,
|
||||
BasicProgramIo::invalid_data,
|
||||
] {
|
||||
panic::catch_unwind(|| zkvm.prove(&inputs_gen(), ProofKind::default())).unwrap_err();
|
||||
// When guest panics Ziren prove will also panics.
|
||||
// Issue for tracking: https://github.com/eth-act/ere/issues/172.
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
panic::catch_unwind(|| zkvm.prove(&input, ProofKind::default())).unwrap_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,9 @@ rust-version.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bincode.workspace = true
|
||||
bincode = { workspace = true, features = ["alloc", "serde"] }
|
||||
blake3.workspace = true
|
||||
bytemuck.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
tempfile.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
@@ -49,8 +49,10 @@ pub enum ZiskError {
|
||||
CompileUtilError(#[from] ere_compile_utils::CompileError),
|
||||
|
||||
// Serialization
|
||||
#[error("Bincode serialization/deserialization failed: {0}")]
|
||||
Bincode(#[from] bincode::Error),
|
||||
#[error("Bincode encode failed: {0}")]
|
||||
BincodeEncode(#[from] bincode::error::EncodeError),
|
||||
#[error("Bincode decode failed: {0}")]
|
||||
BincodeDecode(#[from] bincode::error::DecodeError),
|
||||
|
||||
// Execution
|
||||
#[error("Failed to execute `ziskemu`: {0}")]
|
||||
|
||||
@@ -6,12 +6,10 @@ use crate::{
|
||||
error::ZiskError,
|
||||
};
|
||||
use ere_zkvm_interface::{
|
||||
Input, InputItem, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResourceType, PublicValues, zkVM, zkVMError,
|
||||
ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind, ProverResourceType,
|
||||
PublicValues, zkVM, zkVMError,
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::{
|
||||
io::Read,
|
||||
sync::{Mutex, MutexGuard},
|
||||
time::Instant,
|
||||
};
|
||||
@@ -65,11 +63,9 @@ impl EreZisk {
|
||||
}
|
||||
|
||||
impl zkVM for EreZisk {
|
||||
fn execute(&self, inputs: &Input) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
let input_bytes = serialize_inputs(inputs)?;
|
||||
|
||||
fn execute(&self, input: &[u8]) -> Result<(PublicValues, ProgramExecutionReport), zkVMError> {
|
||||
let start = Instant::now();
|
||||
let (public_values, total_num_cycles) = self.sdk.execute(&input_bytes)?;
|
||||
let (public_values, total_num_cycles) = self.sdk.execute(input)?;
|
||||
let execution_duration = start.elapsed();
|
||||
|
||||
Ok((
|
||||
@@ -84,7 +80,7 @@ impl zkVM for EreZisk {
|
||||
|
||||
fn prove(
|
||||
&self,
|
||||
inputs: &Input,
|
||||
input: &[u8],
|
||||
proof_kind: ProofKind,
|
||||
) -> Result<(PublicValues, Proof, ProgramProvingReport), zkVMError> {
|
||||
if proof_kind != ProofKind::Compressed {
|
||||
@@ -94,10 +90,8 @@ impl zkVM for EreZisk {
|
||||
let mut server = self.server()?;
|
||||
let server = server.as_mut().expect("server initialized");
|
||||
|
||||
let input_bytes = serialize_inputs(inputs)?;
|
||||
|
||||
let start = Instant::now();
|
||||
let (public_values, proof) = server.prove(&input_bytes)?;
|
||||
let (public_values, proof) = server.prove(input)?;
|
||||
let proving_time = start.elapsed();
|
||||
|
||||
Ok((
|
||||
@@ -122,33 +116,14 @@ impl zkVM for EreZisk {
|
||||
fn sdk_version(&self) -> &'static str {
|
||||
SDK_VERSION
|
||||
}
|
||||
|
||||
fn deserialize_from<R: Read, T: DeserializeOwned>(&self, _: R) -> Result<T, zkVMError> {
|
||||
unimplemented!("no native serialization in this platform")
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize `Input` into sequence of bytes.
|
||||
///
|
||||
/// Because ZisK doesn't provide stdin API so we need to handle multiple inputs,
|
||||
/// the current approach naively serializes each `InputItem` individually, then
|
||||
/// concat them into single `Vec<u8>`.
|
||||
fn serialize_inputs(inputs: &Input) -> Result<Vec<u8>, ZiskError> {
|
||||
inputs.iter().try_fold(Vec::new(), |mut acc, item| {
|
||||
match item {
|
||||
InputItem::Object(obj) => bincode::serialize_into(&mut acc, &**obj)?,
|
||||
InputItem::SerializedObject(bytes) => acc.extend(bytes),
|
||||
InputItem::Bytes(bytes) => bincode::serialize_into(&mut acc, bytes)?,
|
||||
};
|
||||
Ok(acc)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{EreZisk, compiler::RustRv64imaCustomized};
|
||||
use ere_test_utils::host::{
|
||||
BasicProgramIo, run_zkvm_execute, run_zkvm_prove, testing_guest_directory,
|
||||
use ere_test_utils::{
|
||||
host::{TestCase, run_zkvm_execute, run_zkvm_prove, testing_guest_directory},
|
||||
program::basic::BasicProgramInput,
|
||||
};
|
||||
use ere_zkvm_interface::{Compiler, ProofKind, ProverResourceType, zkVM};
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
@@ -174,21 +149,17 @@ mod tests {
|
||||
let program = basic_program();
|
||||
let zkvm = EreZisk::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let io = BasicProgramIo::valid().into_output_hashed_io();
|
||||
run_zkvm_execute(&zkvm, &io);
|
||||
let test_case = BasicProgramInput::valid().into_output_sha256();
|
||||
run_zkvm_execute(&zkvm, &test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_execute_invalid_inputs() {
|
||||
fn test_execute_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreZisk::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
for inputs in [
|
||||
BasicProgramIo::empty(),
|
||||
BasicProgramIo::invalid_type(),
|
||||
BasicProgramIo::invalid_data(),
|
||||
] {
|
||||
zkvm.execute(&inputs).unwrap_err();
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.execute(&input).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,23 +170,19 @@ mod tests {
|
||||
|
||||
let _guard = PROVE_LOCK.lock().unwrap();
|
||||
|
||||
let io = BasicProgramIo::valid().into_output_hashed_io();
|
||||
run_zkvm_prove(&zkvm, &io);
|
||||
let test_case = BasicProgramInput::valid().into_output_sha256();
|
||||
run_zkvm_prove(&zkvm, &test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prove_invalid_inputs() {
|
||||
fn test_prove_invalid_input() {
|
||||
let program = basic_program();
|
||||
let zkvm = EreZisk::new(program, ProverResourceType::Cpu).unwrap();
|
||||
|
||||
let _guard = PROVE_LOCK.lock().unwrap();
|
||||
|
||||
for inputs in [
|
||||
BasicProgramIo::empty(),
|
||||
BasicProgramIo::invalid_type(),
|
||||
BasicProgramIo::invalid_data(),
|
||||
] {
|
||||
zkvm.prove(&inputs, ProofKind::default()).unwrap_err();
|
||||
for input in [Vec::new(), BasicProgramInput::invalid().serialized_input()] {
|
||||
zkvm.prove(&input, ProofKind::default()).unwrap_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,10 @@ FROM base${CUDA:+_cuda}
|
||||
ARG CUDA
|
||||
|
||||
# Default to build for RTX 50 series
|
||||
ARG CUDA_ARCH=120
|
||||
ENV CUDA_ARCH=$CUDA_ARCH
|
||||
ARG CUDA_ARCH=sm_120
|
||||
|
||||
# Env variable read by OpenVM crate `cuda-builder`, need to persist it for building `ere-openvm`.
|
||||
ENV CUDA_ARCH=${CUDA_ARCH#sm_}
|
||||
|
||||
# Copy the OpenVM SDK installer script from the workspace context
|
||||
COPY --chmod=755 scripts/sdk_installers/install_openvm_sdk.sh /tmp/install_openvm_sdk.sh
|
||||
|
||||
@@ -43,7 +43,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
|
||||
# Default to build for RTX 50 series
|
||||
ARG CUDA_ARCH=sm_120
|
||||
ENV CUDA_ARCH=$CUDA_ARCH
|
||||
|
||||
# Copy the ZisK SDK installer script from the workspace context
|
||||
COPY --chmod=755 scripts/sdk_installers/install_zisk_sdk.sh /tmp/install_zisk_sdk.sh
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
[target.riscv32i-unknown-none-elf]
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Tlink.x",
|
||||
]
|
||||
runner="nexus-run"
|
||||
@@ -5,12 +5,6 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
nexus-rt = { git = "https://github.com/nexus-xyz/nexus-zkvm.git", tag = "v0.3.4" }
|
||||
postcard = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
|
||||
ere-test-utils = { path = "../../../crates/test-utils" }
|
||||
|
||||
# Generated by cargo-nexus, do not remove!
|
||||
#
|
||||
[features]
|
||||
cycles = [] # Enable cycle counting for run command
|
||||
[workspace]
|
||||
|
||||
|
||||
@@ -3,58 +3,25 @@
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use ere_test_utils::{
|
||||
guest::Platform,
|
||||
program::{basic::BasicProgram, Program},
|
||||
};
|
||||
use nexus_rt::{read_private_input, write_public_output};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
struct NexusPlatform;
|
||||
|
||||
impl Platform for NexusPlatform {
|
||||
fn read_input() -> Vec<u8> {
|
||||
read_private_input().unwrap()
|
||||
}
|
||||
|
||||
fn write_output(output: &[u8]) {
|
||||
write_public_output(&output).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[nexus_rt::main]
|
||||
fn main() {
|
||||
let input_bytes: Vec<u8> = read_private_input().expect("failed to read input");
|
||||
|
||||
// Deserialize the first input (Vec<u8>)
|
||||
let (bytes, remaining): (Vec<u8>, &[u8]) =
|
||||
postcard::take_from_bytes(&input_bytes).expect("failed to deserialize bytes");
|
||||
|
||||
// Deserialize the second input (BasicStruct)
|
||||
let basic_struct: BasicStruct =
|
||||
postcard::from_bytes(remaining).expect("failed to deserialize struct");
|
||||
|
||||
// Check `bytes` length is as expected.
|
||||
assert_eq!(bytes.len(), BYTES_LENGTH);
|
||||
|
||||
// Do some computation on `bytes` and `basic_struct`.
|
||||
let rev_bytes: Vec<u8> = bytes.iter().rev().copied().collect();
|
||||
let basic_struct_output = basic_struct.output();
|
||||
|
||||
// Write `rev_bytes` and `basic_struct_output`
|
||||
let mut output_bytes = Vec::new();
|
||||
output_bytes.extend_from_slice(&rev_bytes);
|
||||
output_bytes.extend_from_slice(&postcard::to_allocvec(&basic_struct_output).unwrap());
|
||||
|
||||
write_public_output(&output_bytes).expect("failed to write output");
|
||||
}
|
||||
|
||||
// Copied from test_utils
|
||||
// test_utils is not used due to no_std conflicts with sha2 dependency.
|
||||
const BYTES_LENGTH: usize = 32;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct BasicStruct {
|
||||
pub a: u8,
|
||||
pub b: u16,
|
||||
pub c: u32,
|
||||
pub d: u64,
|
||||
pub e: Vec<u8>,
|
||||
}
|
||||
|
||||
impl BasicStruct {
|
||||
/// 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(),
|
||||
}
|
||||
}
|
||||
BasicProgram::run::<NexusPlatform>();
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
[package]
|
||||
name = "ere-nexus-guest-fib"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
nexus-rt = { git = "https://github.com/nexus-xyz/nexus-zkvm.git", tag = "v0.3.4" }
|
||||
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
|
||||
postcard = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||
|
||||
# Generated by cargo-nexus, do not remove!
|
||||
#
|
||||
[features]
|
||||
cycles = [] # Enable cycle counting for run command
|
||||
[workspace]
|
||||
@@ -1,50 +0,0 @@
|
||||
#![cfg_attr(target_arch = "riscv32", no_std, no_main)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use nexus_rt::{read_private_input, write_public_output};
|
||||
use postcard;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct FibInput {
|
||||
n: u32,
|
||||
}
|
||||
|
||||
#[nexus_rt::main]
|
||||
fn main() {
|
||||
let input_bytes: Vec<u8> = read_private_input().expect("failed to read input");
|
||||
|
||||
// Deserialize FibInput from the postcard bytes
|
||||
let fib_input: FibInput =
|
||||
postcard::from_bytes(&input_bytes).expect("failed to deserialize input");
|
||||
|
||||
let n = fib_input.n;
|
||||
let result = fibonacci(n);
|
||||
|
||||
// Serialize result to bytes before writing
|
||||
let output_bytes = postcard::to_allocvec(&result).expect("failed to serialize output");
|
||||
|
||||
write_public_output(&output_bytes).expect("failed to write output");
|
||||
}
|
||||
|
||||
fn fibonacci(n: u32) -> u32 {
|
||||
if n == 0 {
|
||||
return 0;
|
||||
}
|
||||
if n == 1 {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let mut a = 0u32;
|
||||
let mut b = 1u32;
|
||||
|
||||
for _ in 2..=n {
|
||||
let temp = a.wrapping_add(b);
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
b
|
||||
}
|
||||
@@ -6,6 +6,5 @@ edition = "2021"
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.3.3"
|
||||
openvm = { git = "https://github.com/openvm-org/openvm.git", features = ["std"], tag = "v1.4.0" }
|
||||
ere-test-utils = { path = "../../../crates/test-utils" }
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
use openvm::io::{read, read_vec, reveal_bytes32};
|
||||
use ere_test_utils::guest::{BasicProgramCore, BasicStruct};
|
||||
use ere_test_utils::{
|
||||
guest::{Digest, Platform, Sha256},
|
||||
program::{basic::BasicProgram, Program},
|
||||
};
|
||||
use openvm::io::{read_vec, reveal_bytes32};
|
||||
|
||||
struct OpenVMPlatform;
|
||||
|
||||
impl Platform for OpenVMPlatform {
|
||||
fn read_input() -> Vec<u8> {
|
||||
read_vec()
|
||||
}
|
||||
|
||||
fn write_output(output: &[u8]) {
|
||||
let digest = Sha256::digest(output);
|
||||
reveal_bytes32(digest.into());
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Read `bytes`.
|
||||
let bytes = read_vec();
|
||||
|
||||
// Read `basic_struct`.
|
||||
let basic_struct = read::<BasicStruct>();
|
||||
|
||||
// Check `bytes` length is as expected.
|
||||
assert_eq!(bytes.len(), BasicProgramCore::BYTES_LENGTH);
|
||||
|
||||
// Do some computation on `bytes` and `basic_struct`.
|
||||
let outputs = BasicProgramCore::outputs((bytes, basic_struct));
|
||||
|
||||
// Hash `outputs` into digest.
|
||||
let digest = BasicProgramCore::sha256_outputs(outputs);
|
||||
|
||||
// Write `digest`
|
||||
reveal_bytes32(digest);
|
||||
BasicProgram::run::<OpenVMPlatform>();
|
||||
}
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
#![no_main]
|
||||
|
||||
use pico_sdk::io::{commit, commit_bytes, read_as, read_vec};
|
||||
use ere_test_utils::guest::{BasicProgramCore, BasicStruct};
|
||||
use ere_test_utils::{
|
||||
guest::Platform,
|
||||
program::{basic::BasicProgram, Program},
|
||||
};
|
||||
use pico_sdk::io::{commit_bytes, read_vec};
|
||||
|
||||
pico_sdk::entrypoint!(main);
|
||||
|
||||
pub fn main() {
|
||||
// Read `bytes`.
|
||||
let bytes = read_vec();
|
||||
struct PicoPlatform;
|
||||
|
||||
// Read `basic_struct`.
|
||||
let basic_struct = read_as::<BasicStruct>();
|
||||
impl Platform for PicoPlatform {
|
||||
fn read_input() -> Vec<u8> {
|
||||
read_vec()
|
||||
}
|
||||
|
||||
// Check `bytes` length is as expected.
|
||||
assert_eq!(bytes.len(), BasicProgramCore::BYTES_LENGTH);
|
||||
|
||||
// Do some computation on `bytes` and `basic_struct`.
|
||||
let (rev_bytes, basic_struct_output) = BasicProgramCore::outputs((bytes, basic_struct));
|
||||
|
||||
// Write `rev_bytes` and `basic_struct_output`
|
||||
commit_bytes(&rev_bytes);
|
||||
commit(&basic_struct_output);
|
||||
fn write_output(output: &[u8]) {
|
||||
commit_bytes(output);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
BasicProgram::run::<PicoPlatform>();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
use risc0_zkvm::guest::env;
|
||||
|
||||
fn main() {
|
||||
let alignment = env::read::<usize>();
|
||||
let alignment = {
|
||||
let mut buf = [0; 4];
|
||||
env::read_slice(&mut buf);
|
||||
u32::from_le_bytes(buf) as usize
|
||||
};
|
||||
|
||||
let layout = std::alloc::Layout::from_size_align(1, alignment).unwrap();
|
||||
let ptr = unsafe { std::alloc::alloc(layout) };
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
use ere_test_utils::{
|
||||
guest::Platform,
|
||||
program::{basic::BasicProgram, Program},
|
||||
};
|
||||
use risc0_zkvm::guest::env;
|
||||
use ere_test_utils::guest::{BasicProgramCore, BasicStruct};
|
||||
use std::io::Read;
|
||||
|
||||
struct Risc0Platform;
|
||||
|
||||
impl Platform for Risc0Platform {
|
||||
fn read_input() -> Vec<u8> {
|
||||
let mut input = Vec::new();
|
||||
env::stdin().read_to_end(&mut input).unwrap();
|
||||
input
|
||||
}
|
||||
|
||||
fn write_output(output: &[u8]) {
|
||||
env::commit_slice(output);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Read `bytes`.
|
||||
let bytes = env::read_frame();
|
||||
|
||||
// Read `basic_struct`.
|
||||
let basic_struct = env::read::<BasicStruct>();
|
||||
|
||||
// Check `bytes` length is as expected.
|
||||
assert_eq!(bytes.len(), BasicProgramCore::BYTES_LENGTH);
|
||||
|
||||
// Do some computation on `bytes` and `basic_struct`.
|
||||
let (rev_bytes, basic_struct_output) = BasicProgramCore::outputs((bytes, basic_struct));
|
||||
|
||||
// Write `rev_bytes` and `basic_struct_output`
|
||||
env::commit_slice(&rev_bytes);
|
||||
env::commit(&basic_struct_output);
|
||||
BasicProgram::run::<Risc0Platform>();
|
||||
}
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
#![no_main]
|
||||
|
||||
use ere_test_utils::guest::{BasicProgramCore, BasicStruct};
|
||||
use ere_test_utils::{
|
||||
guest::Platform,
|
||||
program::{basic::BasicProgram, Program},
|
||||
};
|
||||
|
||||
sp1_zkvm::entrypoint!(main);
|
||||
|
||||
pub fn main() {
|
||||
// Read `bytes`.
|
||||
let bytes = sp1_zkvm::io::read_vec();
|
||||
struct SP1Platform;
|
||||
|
||||
// Read `basic_struct`.
|
||||
let basic_struct = sp1_zkvm::io::read::<BasicStruct>();
|
||||
impl Platform for SP1Platform {
|
||||
fn read_input() -> Vec<u8> {
|
||||
sp1_zkvm::io::read_vec()
|
||||
}
|
||||
|
||||
// Check `bytes` length is as expected.
|
||||
assert_eq!(bytes.len(), BasicProgramCore::BYTES_LENGTH);
|
||||
|
||||
// Do some computation on `bytes` and `basic_struct`.
|
||||
let (rev_bytes, basic_struct_output) = BasicProgramCore::outputs((bytes, basic_struct));
|
||||
|
||||
// Write `rev_bytes` and `basic_struct_output`
|
||||
sp1_zkvm::io::commit_slice(&rev_bytes);
|
||||
sp1_zkvm::io::commit(&basic_struct_output);
|
||||
fn write_output(output: &[u8]) {
|
||||
sp1_zkvm::io::commit_slice(output);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
BasicProgram::run::<SP1Platform>();
|
||||
}
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
#![no_main]
|
||||
|
||||
use ere_test_utils::guest::{BasicProgramCore, BasicStruct};
|
||||
use ere_test_utils::{
|
||||
guest::Platform,
|
||||
program::{basic::BasicProgram, Program},
|
||||
};
|
||||
|
||||
zkm_zkvm::entrypoint!(main);
|
||||
|
||||
pub fn main() {
|
||||
// Read `bytes`.
|
||||
let bytes = zkm_zkvm::io::read_vec();
|
||||
struct ZirenPlatform;
|
||||
|
||||
// Read `basic_struct`.
|
||||
let basic_struct = zkm_zkvm::io::read::<BasicStruct>();
|
||||
impl Platform for ZirenPlatform {
|
||||
fn read_input() -> Vec<u8> {
|
||||
zkm_zkvm::io::read_vec()
|
||||
}
|
||||
|
||||
// Check `bytes` length is as expected.
|
||||
assert_eq!(bytes.len(), BasicProgramCore::BYTES_LENGTH);
|
||||
|
||||
// Do some computation on `bytes` and `basic_struct`.
|
||||
let (rev_bytes, basic_struct_output) = BasicProgramCore::outputs((bytes, basic_struct));
|
||||
|
||||
// Write `rev_bytes` and `basic_struct_output`
|
||||
zkm_zkvm::io::commit_slice(&rev_bytes);
|
||||
zkm_zkvm::io::commit(&basic_struct_output);
|
||||
fn write_output(output: &[u8]) {
|
||||
zkm_zkvm::io::commit_slice(output);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
BasicProgram::run::<ZirenPlatform>();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,5 @@ edition = "2021"
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.3.3"
|
||||
ziskos = { git = "https://github.com/0xPolygonHermez/zisk.git", tag = "v0.12.0" }
|
||||
ere-test-utils = { path = "../../../crates/test-utils" }
|
||||
|
||||
@@ -1,33 +1,27 @@
|
||||
#![no_main]
|
||||
|
||||
use ere_test_utils::guest::{BasicProgramCore, BasicStruct};
|
||||
use ere_test_utils::{
|
||||
guest::{Digest, Platform, Sha256},
|
||||
program::{basic::BasicProgram, Program},
|
||||
};
|
||||
|
||||
ziskos::entrypoint!(main);
|
||||
|
||||
fn main() {
|
||||
let input = ziskos::read_input();
|
||||
let mut input = input.as_slice();
|
||||
struct ZiskPlatform;
|
||||
|
||||
// Read `bytes`.
|
||||
let bytes: Vec<u8> = bincode::deserialize_from(&mut input).unwrap();
|
||||
impl Platform for ZiskPlatform {
|
||||
fn read_input() -> Vec<u8> {
|
||||
ziskos::read_input()
|
||||
}
|
||||
|
||||
// Read `basic_struct`.
|
||||
let basic_struct: BasicStruct = bincode::deserialize_from(&mut input).unwrap();
|
||||
|
||||
// Check input is fully read.
|
||||
assert!(input.is_empty());
|
||||
|
||||
// Check `bytes` length is as expected.
|
||||
assert_eq!(bytes.len(), BasicProgramCore::BYTES_LENGTH);
|
||||
|
||||
// Do some computation on `bytes` and `basic_struct`.
|
||||
let outputs = BasicProgramCore::outputs((bytes, basic_struct));
|
||||
|
||||
// Hash `outputs` into digest.
|
||||
let digest = BasicProgramCore::sha256_outputs(outputs);
|
||||
|
||||
// Write `digest`
|
||||
digest.chunks_exact(4).enumerate().for_each(|(idx, bytes)| {
|
||||
ziskos::set_output(idx, u32::from_le_bytes(bytes.try_into().unwrap()))
|
||||
});
|
||||
fn write_output(output: &[u8]) {
|
||||
let digest = Sha256::digest(output);
|
||||
digest.chunks_exact(4).enumerate().for_each(|(idx, bytes)| {
|
||||
ziskos::set_output(idx, u32::from_le_bytes(bytes.try_into().unwrap()))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
BasicProgram::run::<ZiskPlatform>();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user