mirror of
https://github.com/eth-act/ere.git
synced 2026-02-19 11:54:42 -05:00
tmp: use zisk-sdk of pre-develop-0.16.0
This commit is contained in:
1
.github/workflows/test-zkvm-zisk.yml
vendored
1
.github/workflows/test-zkvm-zisk.yml
vendored
@@ -19,4 +19,5 @@ jobs:
|
||||
cuda: true
|
||||
cuda_archs: '120'
|
||||
cluster: true
|
||||
test_threads: 1
|
||||
skip_prove_test: true
|
||||
|
||||
4490
Cargo.lock
generated
4490
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
31
Cargo.toml
31
Cargo.toml
@@ -48,7 +48,6 @@ license = "MIT OR Apache-2.0"
|
||||
anyhow = "1.0.98"
|
||||
auto_impl = "1.3.0"
|
||||
bincode = { version = "2.0.1", default-features = false }
|
||||
blake3 = "1.8.2"
|
||||
bytemuck = "1.25.0"
|
||||
cargo_metadata = "0.19.0"
|
||||
ciborium = { version = "0.2.2", default-features = false }
|
||||
@@ -61,11 +60,13 @@ fnv = { version = "1.0.7", default-features = false }
|
||||
futures-util = "0.3"
|
||||
http = "1"
|
||||
indexmap = "2.10.0"
|
||||
mpi = "0.8.0"
|
||||
parking_lot = "0.12.5"
|
||||
paste = "1.0.15"
|
||||
postcard = { version = "1.0.8", default-features = false }
|
||||
prost = "0.13"
|
||||
prost-build = "0.13"
|
||||
prost = "0.14"
|
||||
prost-build = "0.14"
|
||||
prost-types = "0.14"
|
||||
rand = "0.9.2"
|
||||
rkyv = { version = "0.8.12", default-features = false }
|
||||
serde = { version = "1.0.219", default-features = false }
|
||||
@@ -79,11 +80,14 @@ thiserror = "2.0.12"
|
||||
tokio = "1.0"
|
||||
toml = "0.8.23"
|
||||
tonic = "0.14"
|
||||
tonic-build = "0.14"
|
||||
tonic-prost = "0.14"
|
||||
tonic-prost-build = "0.14"
|
||||
tower-http = "0.6.6"
|
||||
tracing = "0.1.41"
|
||||
tracing-subscriber = "0.3.19"
|
||||
twirp = "0.9.1"
|
||||
twirp-build = "0.9.0"
|
||||
twirp = "0.10"
|
||||
twirp-build = "0.10"
|
||||
uuid = "1"
|
||||
wait-timeout = "0.2.1"
|
||||
|
||||
@@ -149,9 +153,17 @@ zkm-sdk = { git = "https://github.com/ProjectZKM/Ziren.git", tag = "v1.2.3" }
|
||||
zkm-zkvm = { git = "https://github.com/ProjectZKM/Ziren.git", tag = "v1.2.3", default-features = false }
|
||||
|
||||
# ZisK dependencies
|
||||
ziskos = { git = "https://github.com/0xPolygonHermez/zisk.git", tag = "v0.15.0" }
|
||||
zisk-distributed-grpc-api = { git = "https://github.com/han0110/zisk", branch = "patch/v0.15.0-cluster" }
|
||||
zisk-proofman-verifier = { git = "https://github.com/0xPolygonHermez/pil2-proofman", package = "proofman-verifier", tag = "v0.15.0" }
|
||||
ziskos = { git = "https://github.com/0xPolygonHermez/zisk.git", branch = "pre-develop-0.16.0" }
|
||||
zisk-rom-setup = { git = "https://github.com/0xPolygonHermez/zisk.git", branch = "pre-develop-0.16.0", package = "rom-setup" }
|
||||
zisk-sdk = { git = "https://github.com/0xPolygonHermez/zisk.git", branch = "pre-develop-0.16.0" }
|
||||
zisk-core = { git = "https://github.com/0xPolygonHermez/zisk.git", branch = "pre-develop-0.16.0" }
|
||||
ziskemu = { git = "https://github.com/0xPolygonHermez/zisk.git", branch = "pre-develop-0.16.0" }
|
||||
proofman = { git = "https://github.com/0xPolygonHermez/pil2-proofman.git", branch = "pre-develop-0.16.0" }
|
||||
proofman-common = { git = "https://github.com/0xPolygonHermez/pil2-proofman.git", branch = "pre-develop-0.16.0" }
|
||||
proofman-util = { git = "https://github.com/0xPolygonHermez/pil2-proofman.git", branch = "pre-develop-0.16.0" }
|
||||
proofman-fields = { git = "https://github.com/0xPolygonHermez/pil2-proofman.git", branch = "pre-develop-0.16.0", package = "fields" }
|
||||
proofman-verifier = { git = "https://github.com/0xPolygonHermez/pil2-proofman", branch = "pre-develop-0.16.0" }
|
||||
proofman-starks-lib-c = { git = "https://github.com/0xPolygonHermez/pil2-proofman.git", branch = "pre-develop-0.16.0" }
|
||||
|
||||
# Local dependencies
|
||||
ere-zkvm-interface = { path = "crates/zkvm-interface" }
|
||||
@@ -195,3 +207,6 @@ ark-ff = { git = "https://github.com/a16z/arkworks-algebra", branch = "dev/twist
|
||||
ark-ec = { git = "https://github.com/a16z/arkworks-algebra", branch = "dev/twist-shout" }
|
||||
ark-serialize = { git = "https://github.com/a16z/arkworks-algebra", branch = "dev/twist-shout" }
|
||||
allocative = { git = "https://github.com/facebookexperimental/allocative", rev = "85b773d85d526d068ce94724ff7a7b81203fc95e" }
|
||||
|
||||
# Patch vergen of zkm-sdk to use newer git2
|
||||
vergen = { git = "https://github.com/rustyhorde/vergen", branch = "legacy/v8" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// This file is @generated by prost-build.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ExecuteRequest {
|
||||
#[prost(bytes = "vec", tag = "1")]
|
||||
pub input_stdin: ::prost::alloc::vec::Vec<u8>,
|
||||
@@ -8,7 +8,7 @@ pub struct ExecuteRequest {
|
||||
pub input_proofs: ::core::option::Option<::prost::alloc::vec::Vec<u8>>,
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ExecuteResponse {
|
||||
#[prost(oneof = "execute_response::Result", tags = "1, 2")]
|
||||
pub result: ::core::option::Option<execute_response::Result>,
|
||||
@@ -16,7 +16,7 @@ pub struct ExecuteResponse {
|
||||
/// Nested message and enum types in `ExecuteResponse`.
|
||||
pub mod execute_response {
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)]
|
||||
pub enum Result {
|
||||
#[prost(message, tag = "1")]
|
||||
Ok(super::ExecuteOk),
|
||||
@@ -25,7 +25,7 @@ pub mod execute_response {
|
||||
}
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ExecuteOk {
|
||||
#[prost(bytes = "vec", tag = "1")]
|
||||
pub public_values: ::prost::alloc::vec::Vec<u8>,
|
||||
@@ -33,7 +33,7 @@ pub struct ExecuteOk {
|
||||
pub report: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProveRequest {
|
||||
#[prost(bytes = "vec", tag = "1")]
|
||||
pub input_stdin: ::prost::alloc::vec::Vec<u8>,
|
||||
@@ -43,7 +43,7 @@ pub struct ProveRequest {
|
||||
pub proof_kind: i32,
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProveResponse {
|
||||
#[prost(oneof = "prove_response::Result", tags = "1, 2")]
|
||||
pub result: ::core::option::Option<prove_response::Result>,
|
||||
@@ -51,7 +51,7 @@ pub struct ProveResponse {
|
||||
/// Nested message and enum types in `ProveResponse`.
|
||||
pub mod prove_response {
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)]
|
||||
pub enum Result {
|
||||
#[prost(message, tag = "1")]
|
||||
Ok(super::ProveOk),
|
||||
@@ -60,7 +60,7 @@ pub mod prove_response {
|
||||
}
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProveOk {
|
||||
#[prost(bytes = "vec", tag = "1")]
|
||||
pub public_values: ::prost::alloc::vec::Vec<u8>,
|
||||
@@ -70,7 +70,7 @@ pub struct ProveOk {
|
||||
pub report: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct VerifyRequest {
|
||||
#[prost(bytes = "vec", tag = "1")]
|
||||
pub proof: ::prost::alloc::vec::Vec<u8>,
|
||||
@@ -78,7 +78,7 @@ pub struct VerifyRequest {
|
||||
pub proof_kind: i32,
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct VerifyResponse {
|
||||
#[prost(oneof = "verify_response::Result", tags = "1, 2")]
|
||||
pub result: ::core::option::Option<verify_response::Result>,
|
||||
@@ -86,7 +86,7 @@ pub struct VerifyResponse {
|
||||
/// Nested message and enum types in `VerifyResponse`.
|
||||
pub mod verify_response {
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)]
|
||||
pub enum Result {
|
||||
#[prost(message, tag = "1")]
|
||||
Ok(super::VerifyOk),
|
||||
@@ -95,7 +95,7 @@ pub mod verify_response {
|
||||
}
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct VerifyOk {
|
||||
#[prost(bytes = "vec", tag = "1")]
|
||||
pub public_values: ::prost::alloc::vec::Vec<u8>,
|
||||
|
||||
@@ -122,6 +122,7 @@ where
|
||||
fn assert_output(&self, public_values: &[u8]) {
|
||||
let output = P::compute(self.test_case.clone());
|
||||
let digest = D::digest(P::Io::serialize_output(&output).unwrap());
|
||||
assert_eq!(&*digest, public_values)
|
||||
assert_eq!(&*digest, &public_values[..digest.len()]);
|
||||
assert!(public_values[digest.len()..].iter().all(|byte| *byte == 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,29 +7,40 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
blake3.workspace = true
|
||||
bytemuck = { workspace = true, features = ["extern_crate_alloc"] }
|
||||
bincode = { workspace = true, features = ["alloc"] }
|
||||
bytemuck.workspace = true
|
||||
futures-util.workspace = true
|
||||
http.workspace = true
|
||||
mpi = { workspace = true, optional = true }
|
||||
parking_lot.workspace = true
|
||||
prost = { workspace = true, optional = true }
|
||||
serde.workspace = true
|
||||
strum = { workspace = true, features = ["derive"] }
|
||||
tempfile.workspace = true
|
||||
thiserror.workspace = true
|
||||
tonic = { workspace = true, optional = true }
|
||||
tonic-prost = { workspace = true, optional = true }
|
||||
tracing.workspace = true
|
||||
uuid = { workspace = true, features = ["v4"] }
|
||||
wait-timeout.workspace = true
|
||||
|
||||
# Local dependencies
|
||||
ere-compile-utils = { workspace = true, optional = true }
|
||||
ere-zkvm-interface.workspace = true
|
||||
|
||||
# Zisk dependencies
|
||||
zisk-distributed-grpc-api = { workspace = true, optional = true }
|
||||
zisk-proofman-verifier = { workspace = true, optional = true }
|
||||
proofman = { workspace = true, optional = true }
|
||||
proofman-common = { workspace = true, optional = true }
|
||||
proofman-fields = { workspace = true, optional = true }
|
||||
proofman-util = { workspace = true, optional = true }
|
||||
proofman-verifier = { workspace = true, optional = true }
|
||||
zisk-core = { workspace = true, optional = true }
|
||||
zisk-rom-setup = { workspace = true, optional = true }
|
||||
zisk-sdk = { workspace = true, optional = true, features = ["disable_distributed"] }
|
||||
ziskemu = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tonic-prost-build = { workspace = true }
|
||||
tonic-build = { workspace = true }
|
||||
|
||||
ere-test-utils = { workspace = true, features = ["host"] }
|
||||
|
||||
[build-dependencies]
|
||||
@@ -39,11 +50,22 @@ ere-build-utils.workspace = true
|
||||
default = ["compiler", "zkvm"]
|
||||
compiler = ["dep:ere-compile-utils"]
|
||||
zkvm = [
|
||||
"dep:mpi",
|
||||
"dep:prost",
|
||||
"dep:tonic",
|
||||
"dep:zisk-distributed-grpc-api",
|
||||
"dep:zisk-proofman-verifier",
|
||||
"dep:tonic-prost",
|
||||
"dep:proofman",
|
||||
"dep:proofman-common",
|
||||
"dep:proofman-fields",
|
||||
"dep:proofman-util",
|
||||
"dep:proofman-verifier",
|
||||
"dep:zisk-rom-setup",
|
||||
"dep:zisk-core",
|
||||
"dep:zisk-sdk",
|
||||
"dep:ziskemu",
|
||||
"ere-zkvm-interface/tokio",
|
||||
]
|
||||
cuda = ["zisk-sdk/gpu"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use core::{array::from_fn, cell::UnsafeCell, hash::Hasher, ops::Deref};
|
||||
use core::{cell::UnsafeCell, hash::Hasher, ops::Deref};
|
||||
use ere_platform_trait::LengthPrefixedStdin;
|
||||
use fnv::FnvHasher;
|
||||
use ziskos::ziskos_definitions::ziskos_config::UART_ADDR;
|
||||
@@ -150,7 +150,7 @@ pub struct ZiskPlatform;
|
||||
|
||||
impl Platform for ZiskPlatform {
|
||||
fn read_whole_input() -> impl Deref<Target = [u8]> {
|
||||
LengthPrefixedStdin::new(ziskos::read_input())
|
||||
LengthPrefixedStdin::new(ziskos::io::read_vec())
|
||||
}
|
||||
|
||||
fn write_whole_output(output: &[u8]) {
|
||||
@@ -159,10 +159,7 @@ impl Platform for ZiskPlatform {
|
||||
"Maximum output size is 256 bytes, got {}",
|
||||
output.len()
|
||||
);
|
||||
output.chunks(4).enumerate().for_each(|(idx, chunk)| {
|
||||
let value = u32::from_le_bytes(from_fn(|i| chunk.get(i).copied().unwrap_or_default()));
|
||||
ziskos::set_output(idx, value)
|
||||
});
|
||||
ziskos::io::write(output);
|
||||
}
|
||||
|
||||
fn print(message: &str) {
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
use crate::{
|
||||
program::ZiskProgram,
|
||||
zkvm::sdk::{RomDigest, ZiskSdk},
|
||||
zkvm::sdk::{ProgramVk, ZiskSdk},
|
||||
};
|
||||
use anyhow::bail;
|
||||
use ere_zkvm_interface::zkvm::{
|
||||
CommonError, Input, ProgramExecutionReport, ProgramProvingReport, Proof, ProofKind,
|
||||
ProverResource, PublicValues, zkVM, zkVMProgramDigest,
|
||||
};
|
||||
use mpi as _;
|
||||
use std::time::Instant;
|
||||
|
||||
mod cluster_client;
|
||||
mod error;
|
||||
mod sdk;
|
||||
mod server;
|
||||
|
||||
pub use error::Error;
|
||||
|
||||
@@ -98,10 +97,10 @@ impl zkVM for EreZisk {
|
||||
}
|
||||
|
||||
impl zkVMProgramDigest for EreZisk {
|
||||
type ProgramDigest = RomDigest;
|
||||
type ProgramDigest = ProgramVk;
|
||||
|
||||
fn program_digest(&self) -> anyhow::Result<Self::ProgramDigest> {
|
||||
Ok(self.sdk.rom_digest()?)
|
||||
Ok(self.sdk.program_vk())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,22 +158,22 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_prove() {
|
||||
let _guard = PROVE_LOCK.lock().unwrap();
|
||||
|
||||
let program = basic_program();
|
||||
let zkvm = EreZisk::new(program, ProverResource::Cpu).unwrap();
|
||||
|
||||
let _guard = PROVE_LOCK.lock().unwrap();
|
||||
|
||||
let test_case = BasicProgram::<BincodeLegacy>::valid_test_case();
|
||||
run_zkvm_prove(&zkvm, &test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prove_invalid_test_case() {
|
||||
let _guard = PROVE_LOCK.lock().unwrap();
|
||||
|
||||
let program = basic_program();
|
||||
let zkvm = EreZisk::new(program, ProverResource::Cpu).unwrap();
|
||||
|
||||
let _guard = PROVE_LOCK.lock().unwrap();
|
||||
|
||||
for input in [
|
||||
Input::new(),
|
||||
BasicProgram::<BincodeLegacy>::invalid_test_case().input(),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::zkvm::sdk::RomDigest;
|
||||
use crate::zkvm::sdk::ProgramVk;
|
||||
use ere_zkvm_interface::zkvm::CommonError;
|
||||
use proofman_common::ProofmanError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -7,29 +8,47 @@ pub enum Error {
|
||||
#[error(transparent)]
|
||||
CommonError(#[from] CommonError),
|
||||
|
||||
// Execution
|
||||
#[error("Total steps not found in execution report")]
|
||||
TotalStepsNotFound,
|
||||
// Common
|
||||
#[error("Enable `cuda` feature to use `ProverResource::Gpu`")]
|
||||
CudaFeatureDisabled,
|
||||
|
||||
// Rom setup
|
||||
#[error("Failed to find ROM digest in output")]
|
||||
RomDigestNotFound,
|
||||
#[error("Disable `cuda` feature to use `ProverResource::Cpu`")]
|
||||
CudaFeatureEnabled,
|
||||
|
||||
#[error("`cargo-zisk rom-setup` failed in another thread")]
|
||||
RomSetupFailedBefore,
|
||||
// Emulator
|
||||
#[error("ROM transpilation failed: {0}")]
|
||||
Riscv2zisk(String),
|
||||
|
||||
// Prove
|
||||
#[error("Server crashed")]
|
||||
ServerCrashed,
|
||||
#[error("Emulation not terminated")]
|
||||
EmulatorNotTerminated,
|
||||
|
||||
#[error("Timeout waiting for server proving")]
|
||||
TimeoutWaitingServerProving,
|
||||
#[error("Emulation failure")]
|
||||
EmulatorError,
|
||||
|
||||
#[error("Timeout waiting for server ready")]
|
||||
TimeoutWaitingServerReady,
|
||||
#[error("Emulator panicked: {0}")]
|
||||
EmulatorPanic(String),
|
||||
|
||||
#[error("Unknown server status, stdout: {stdout}")]
|
||||
UnknownServerStatus { stdout: String },
|
||||
// SDK
|
||||
#[error("Check setup failed: {0}")]
|
||||
CheckSetup(#[source] ProofmanError),
|
||||
|
||||
#[error("Create ProofCtx failed: {0}")]
|
||||
ProofCtx(#[source] ProofmanError),
|
||||
|
||||
#[error("Compute program VK failed: {0}")]
|
||||
ComputeProgramVk(#[source] anyhow::Error),
|
||||
|
||||
#[error("Invalid program VK length, expected 32, got {0}")]
|
||||
InvalidProgramVkLength(usize),
|
||||
|
||||
#[error("Initialize prover failed: {0}")]
|
||||
InitProver(#[source] anyhow::Error),
|
||||
|
||||
#[error("Prove failed: {0}")]
|
||||
Prove(#[source] anyhow::Error),
|
||||
|
||||
#[error("Prove panicked: {0}")]
|
||||
ProvePanic(String),
|
||||
|
||||
// Cluster
|
||||
#[error("Invalid cluster endpoint: {0}")]
|
||||
@@ -44,6 +63,9 @@ pub enum Error {
|
||||
#[error("Cluster error: {0}")]
|
||||
ClusterError(String),
|
||||
|
||||
#[error("Invalid proof format: {0}")]
|
||||
InvalidProofFormat(anyhow::Error),
|
||||
|
||||
// Verify
|
||||
#[error("Invalid proof")]
|
||||
InvalidProof,
|
||||
@@ -57,9 +79,9 @@ pub enum Error {
|
||||
#[error("Public values length {0}, but expected at least 6")]
|
||||
InvalidPublicValuesLength(usize),
|
||||
|
||||
#[error("Unexpected ROM digest - preprocessed: {preprocessed:?}, proved: {proved:?}")]
|
||||
UnexpectedRomDigest {
|
||||
preprocessed: RomDigest,
|
||||
proved: RomDigest,
|
||||
#[error("Unexpected program VK - preprocessed: {preprocessed:?}, proved: {proved:?}")]
|
||||
UnexpectedProgramVk {
|
||||
preprocessed: ProgramVk,
|
||||
proved: ProgramVk,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,80 +1,65 @@
|
||||
use crate::zkvm::Error;
|
||||
use crate::zkvm::cluster_client::ClusterClient;
|
||||
use crate::zkvm::server::{ZiskServer, ZiskServerOptions};
|
||||
use crate::zkvm::{
|
||||
Error,
|
||||
sdk::{cluster::ClusterClient, local::LocalProver},
|
||||
};
|
||||
use ere_zkvm_interface::zkvm::{CommonError, ProverResource, ProverResourceKind, PublicValues};
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use proofman_common::{
|
||||
MpiCtx, ParamsGPU, ProofCtx, ProofType, SetupCtx, SetupsVadcop, VerboseMode,
|
||||
};
|
||||
use proofman_fields::Goldilocks;
|
||||
use proofman_util::VadcopFinalProof;
|
||||
use proofman_verifier::verify_vadcop_final;
|
||||
use std::{
|
||||
env, fs,
|
||||
io::BufRead,
|
||||
any::Any,
|
||||
env,
|
||||
mem::transmute,
|
||||
panic::{self, AssertUnwindSafe},
|
||||
path::PathBuf,
|
||||
sync::OnceLock,
|
||||
time::{Duration, Instant},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use tempfile::tempdir;
|
||||
use tracing::info;
|
||||
use zisk_core::{Riscv2zisk, ZiskRom};
|
||||
use zisk_rom_setup::rom_merkle_setup;
|
||||
use zisk_sdk::{ElfBinaryFromFile, ZISK_PUBLICS, ZiskProofWithPublicValues};
|
||||
use ziskemu::{Emu, EmuOptions};
|
||||
|
||||
mod cluster;
|
||||
mod local;
|
||||
|
||||
/// Merkle root of ROM trace.
|
||||
pub type ProgramVk = [u8; 32];
|
||||
|
||||
/// Verifying key of the aggregation proof.
|
||||
///
|
||||
/// Extracted from `$HOME/.zisk/provingKey/zisk/vadcop_final/vadcop_final.verkey.json`.
|
||||
pub const VADCOP_FINAL_VK: [u8; 32] = unsafe {
|
||||
// Use `transmute` to keep the endianness because `zisk_proofman_verifier::verify` will
|
||||
// use `bytemuck` to cast it to `[u64; 4]`.
|
||||
transmute([
|
||||
723851053263266420u64,
|
||||
2272245643171245153u64,
|
||||
9868173762158752255u64,
|
||||
6004219199197288727u64,
|
||||
5756952873125057328u64,
|
||||
1254521327410429374u64,
|
||||
17471446849604192873u64,
|
||||
13226325674217234543u64,
|
||||
])
|
||||
};
|
||||
|
||||
/// Merkle root of ROM trace generated by `cargo-zisk rom-setup`.
|
||||
pub type RomDigest = [u64; 4];
|
||||
|
||||
/// Prover backend - either local or cluster.
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum ZiskProver {
|
||||
Server(ZiskServer),
|
||||
Local(LocalProver),
|
||||
Cluster(ClusterClient),
|
||||
}
|
||||
|
||||
pub struct ZiskSdk {
|
||||
elf_path: PathBuf,
|
||||
resource: ProverResource,
|
||||
/// ROM digest will be setup when `ZiskSdk::prove` or `ZiskSdk::verify`
|
||||
/// is called, or if env variable `ERE_ZISK_SETUP_ON_INIT` is set.
|
||||
///
|
||||
/// Use `Option` inside because ROM setup might fail, we can get rid of
|
||||
/// it if `OnceLock::get_or_try_init` is stabilized.
|
||||
rom_digest: OnceLock<Option<RomDigest>>,
|
||||
/// Prover instance, either a local server or a cluster client.
|
||||
rom: ZiskRom,
|
||||
program_vk: ProgramVk,
|
||||
prover: ZiskProver,
|
||||
}
|
||||
|
||||
impl ZiskSdk {
|
||||
/// Returns SDK for the ELF.
|
||||
pub fn new(elf: Vec<u8>, resource: ProverResource) -> Result<Self, Error> {
|
||||
// Save ELF to `~/.zisk/cache` along with the ROM binaries, to avoid it
|
||||
// been cleaned up during a long run process.
|
||||
let cache_dir_path = dot_zisk_dir_path().join("cache");
|
||||
fs::create_dir_all(&cache_dir_path)
|
||||
.map_err(|err| CommonError::create_dir("cache", &cache_dir_path, err))?;
|
||||
|
||||
// Use blake3 hash as the ELF file name, since ROM setup uses blake3 as
|
||||
// unique identifier as well.
|
||||
let elf_hash = blake3::hash(&elf);
|
||||
let elf_path = cache_dir_path.join(format!("{elf_hash}.elf"));
|
||||
|
||||
fs::write(&elf_path, elf).map_err(|err| CommonError::write_file("elf", &elf_path, err))?;
|
||||
|
||||
let prover = match &resource {
|
||||
ProverResource::Cpu | ProverResource::Gpu => ZiskProver::Server(ZiskServer::new(
|
||||
&elf_path,
|
||||
resource.is_gpu(),
|
||||
ZiskServerOptions::from_env(),
|
||||
)),
|
||||
ProverResource::Cluster(config) => ZiskProver::Cluster(ClusterClient::new(config)?),
|
||||
_ => Err(CommonError::unsupported_prover_resource_kind(
|
||||
match &resource {
|
||||
ProverResource::Cpu if cfg!(feature = "cuda") => Err(Error::CudaFeatureEnabled)?,
|
||||
ProverResource::Gpu if cfg!(not(feature = "cuda")) => Err(Error::CudaFeatureDisabled)?,
|
||||
ProverResource::Network(_) => Err(CommonError::unsupported_prover_resource_kind(
|
||||
resource.kind(),
|
||||
[
|
||||
ProverResourceKind::Cpu,
|
||||
@@ -82,139 +67,105 @@ impl ZiskSdk {
|
||||
ProverResourceKind::Cluster,
|
||||
],
|
||||
))?,
|
||||
_ => {}
|
||||
};
|
||||
|
||||
let sdk = Self {
|
||||
elf_path,
|
||||
resource,
|
||||
rom_digest: OnceLock::new(),
|
||||
// Convert ELF to ZisK ROM
|
||||
let rom = Riscv2zisk::new(&elf)
|
||||
.run()
|
||||
.map_err(|e| Error::Riscv2zisk(e.to_string()))?;
|
||||
|
||||
// Compute program VK
|
||||
let program_vk = compute_program_vk(&elf)?;
|
||||
|
||||
// Initialize prover
|
||||
let prover = match &resource {
|
||||
ProverResource::Cpu | ProverResource::Gpu => ZiskProver::Local(LocalProver::new(elf)?),
|
||||
ProverResource::Cluster(config) => ZiskProver::Cluster(ClusterClient::new(config)?),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
rom,
|
||||
program_vk,
|
||||
prover,
|
||||
};
|
||||
|
||||
if env::var_os("ERE_ZISK_SETUP_ON_INIT").is_some() {
|
||||
sdk.rom_digest()?;
|
||||
|
||||
if let ZiskProver::Server(server) = &sdk.prover {
|
||||
server.ensure_ready()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(sdk)
|
||||
})
|
||||
}
|
||||
|
||||
/// Execute the ELF with the given `input`.
|
||||
pub fn execute(&self, input: &[u8]) -> Result<(PublicValues, u64), Error> {
|
||||
let tempdir = tempdir().map_err(CommonError::tempdir)?;
|
||||
let input_path = tempdir.path().join("input");
|
||||
let output_path = tempdir.path().join("output");
|
||||
pub fn program_vk(&self) -> ProgramVk {
|
||||
self.program_vk
|
||||
}
|
||||
|
||||
fs::write(&input_path, input)
|
||||
.map_err(|err| CommonError::write_file("input", &input_path, err))?;
|
||||
/// Execute the ELF with the given `stdin`.
|
||||
pub fn execute(&self, stdin: &[u8]) -> Result<(PublicValues, u64), Error> {
|
||||
let mut emu = Emu::new(&self.rom);
|
||||
emu.ctx = emu.create_emu_context(stdin.to_vec());
|
||||
|
||||
let mut cmd = Command::new("ziskemu");
|
||||
let output = cmd
|
||||
.arg("--elf")
|
||||
.arg(&self.elf_path)
|
||||
.arg("--inputs")
|
||||
.arg(input_path)
|
||||
.arg("--output")
|
||||
.arg(&output_path)
|
||||
.arg("--stats") // Enable stats in order to get total steps.
|
||||
.output()
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
panic::catch_unwind(AssertUnwindSafe(|| emu.run_fast(&EmuOptions::default())))
|
||||
.map_err(|err| Error::EmulatorPanic(panic_msg(err)))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
))?;
|
||||
if !emu.ctx.inst_ctx.end {
|
||||
return Err(Error::EmulatorNotTerminated);
|
||||
}
|
||||
|
||||
// Extract cycle count from the stdout.
|
||||
if emu.ctx.inst_ctx.error {
|
||||
return Err(Error::EmulatorError);
|
||||
}
|
||||
|
||||
let total_num_cycles = String::from_utf8_lossy(&output.stdout)
|
||||
.split_once("STEPS")
|
||||
.and_then(|(_, stats)| {
|
||||
stats
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.and_then(|steps| steps.replace(",", "").parse::<u64>().ok())
|
||||
})
|
||||
.ok_or(Error::TotalStepsNotFound)?;
|
||||
|
||||
let public_values = fs::read(&output_path)
|
||||
.map_err(|err| CommonError::read_file("output", &output_path, err))?;
|
||||
let public_values = emu.get_output_8();
|
||||
let total_num_cycles = emu.number_of_steps();
|
||||
|
||||
Ok((public_values, total_num_cycles))
|
||||
}
|
||||
|
||||
/// Returns the ROM digest of the ELF.
|
||||
///
|
||||
/// If it is not setup yet, it makes sure the global setup is done, then
|
||||
/// does ROM setup of the ELF and stores the ROM digest for later usage.
|
||||
pub fn rom_digest(&self) -> Result<RomDigest, Error> {
|
||||
// FIXME: Use `get_or_try_init` when it is stabilized
|
||||
let mut result = Ok(());
|
||||
let rom_digest = *self.rom_digest.get_or_init(|| {
|
||||
check_setup(self.resource.is_gpu())
|
||||
.and_then(|_| rom_setup(&self.elf_path))
|
||||
.map_err(|err| result = Err(err))
|
||||
.ok()
|
||||
});
|
||||
result?;
|
||||
rom_digest.ok_or(Error::RomSetupFailedBefore)
|
||||
}
|
||||
|
||||
/// Prove the ELF with the given input.
|
||||
/// Prove the ELF with the given stdin.
|
||||
///
|
||||
/// Returns the public values, proof, and proving time.
|
||||
pub fn prove(&self, input: &[u8]) -> Result<(PublicValues, Vec<u8>, Duration), Error> {
|
||||
pub fn prove(&self, stdin: &[u8]) -> Result<(PublicValues, Vec<u8>, Duration), Error> {
|
||||
let (proof, proving_time) = match &self.prover {
|
||||
ZiskProver::Cluster(client) => client.prove(input)?,
|
||||
ZiskProver::Server(server) => {
|
||||
self.rom_digest()?;
|
||||
|
||||
let start = Instant::now();
|
||||
let proof = server.prove(input)?;
|
||||
let proving_time = start.elapsed();
|
||||
|
||||
(proof, proving_time)
|
||||
}
|
||||
ZiskProver::Local(local) => local.prove(stdin)?,
|
||||
ZiskProver::Cluster(client) => client.prove(stdin)?,
|
||||
};
|
||||
|
||||
// Deserialize public values.
|
||||
let (proved_rom_digest, public_values) = deserialize_public_values(&proof)?;
|
||||
// Extract public values and program_vk
|
||||
let (public_values, proved_program_vk) = extract_public_values_and_progam_vk(&proof)?;
|
||||
|
||||
// The proved ROM digest should be equal to preprocessed one.
|
||||
let rom_digest = self.rom_digest()?;
|
||||
if proved_rom_digest != rom_digest {
|
||||
return Err(Error::UnexpectedRomDigest {
|
||||
preprocessed: rom_digest,
|
||||
proved: proved_rom_digest,
|
||||
// The proved program VK should match the preprocessed
|
||||
if proved_program_vk != self.program_vk {
|
||||
return Err(Error::UnexpectedProgramVk {
|
||||
preprocessed: self.program_vk,
|
||||
proved: proved_program_vk,
|
||||
});
|
||||
}
|
||||
|
||||
Ok((public_values, proof, proving_time))
|
||||
Ok((
|
||||
public_values,
|
||||
bincode::serde::encode_to_vec(&proof, bincode::config::legacy())
|
||||
.map_err(|err| CommonError::serialize("proof", "bincode", err))?,
|
||||
proving_time,
|
||||
))
|
||||
}
|
||||
|
||||
/// Verify the proof of the ELF, and returns public values.
|
||||
pub fn verify(&self, proof: &[u8]) -> Result<PublicValues, Error> {
|
||||
let proof = align_to_u64(proof)?;
|
||||
let proof: ZiskProofWithPublicValues =
|
||||
bincode::serde::decode_from_slice(proof, bincode::config::legacy())
|
||||
.map_err(|err| CommonError::deserialize("proof", "bincode", err))?
|
||||
.0;
|
||||
|
||||
if !zisk_proofman_verifier::verify(&proof, &VADCOP_FINAL_VK) {
|
||||
Err(Error::InvalidProof)?
|
||||
let vadcop_final_proof = vadcop_final_proof_aligned(&proof)?;
|
||||
if !verify_vadcop_final(&vadcop_final_proof, &VADCOP_FINAL_VK) {
|
||||
return Err(Error::InvalidProof);
|
||||
}
|
||||
|
||||
// Deserialize public values.
|
||||
let (proved_rom_digest, public_values) = deserialize_public_values(&proof)?;
|
||||
// Extract public values and program_vk
|
||||
let (public_values, proved_program_vk) = extract_public_values_and_progam_vk(&proof)?;
|
||||
|
||||
// The proved ROM digest should be equal to preprocessed one.
|
||||
let rom_digest = self.rom_digest()?;
|
||||
if proved_rom_digest != rom_digest {
|
||||
return Err(Error::UnexpectedRomDigest {
|
||||
preprocessed: rom_digest,
|
||||
proved: proved_rom_digest,
|
||||
// The proved program VK should match the preprocessed
|
||||
if proved_program_vk != self.program_vk {
|
||||
return Err(Error::UnexpectedProgramVk {
|
||||
preprocessed: self.program_vk,
|
||||
proved: proved_program_vk,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -222,127 +173,94 @@ impl ZiskSdk {
|
||||
}
|
||||
}
|
||||
|
||||
/// Does global setup if it is not done yet.
|
||||
fn check_setup(cuda: bool) -> Result<(), Error> {
|
||||
info!("Running command `cargo-zisk check-setup --aggregation`...");
|
||||
fn compute_program_vk(elf: &[u8]) -> Result<ProgramVk, Error> {
|
||||
let mpi_ctx = Arc::new(MpiCtx::new());
|
||||
let mut pctx = ProofCtx::create_ctx(proving_key_path(), false, VerboseMode::Info, mpi_ctx)
|
||||
.map_err(Error::ProofCtx)?;
|
||||
let mut params = ParamsGPU::new(false);
|
||||
params.with_max_number_streams(1);
|
||||
let sctx = SetupCtx::new(&pctx.global_info, &ProofType::Basic, false, ¶ms, &[]);
|
||||
let setups_vadcop = SetupsVadcop::new(&pctx.global_info, false, false, ¶ms, &[]);
|
||||
pctx.set_device_buffers(&sctx, &setups_vadcop, false, ¶ms)
|
||||
.map_err(Error::ProofCtx)?;
|
||||
|
||||
let cargo_zisk = if cuda {
|
||||
"cargo-zisk-cuda"
|
||||
} else {
|
||||
"cargo-zisk"
|
||||
let elf = ElfBinaryFromFile {
|
||||
elf: elf.to_vec(),
|
||||
name: String::new(),
|
||||
with_hints: false,
|
||||
};
|
||||
|
||||
let mut cmd = Command::new(cargo_zisk);
|
||||
let output = cmd
|
||||
.args(["check-setup", "--aggregation"])
|
||||
.output()
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
let tempdir = tempdir().map_err(CommonError::tempdir)?;
|
||||
|
||||
if !output.status.success() {
|
||||
Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
))?;
|
||||
}
|
||||
let (_, program_vk) =
|
||||
rom_merkle_setup::<Goldilocks>(&pctx, &elf, &Some(tempdir.path().to_path_buf()))
|
||||
.map_err(Error::ComputeProgramVk)?;
|
||||
|
||||
info!("Command `cargo-zisk check-setup --aggregation` succeeded");
|
||||
|
||||
Ok(())
|
||||
program_vk_from_slice(&program_vk)
|
||||
}
|
||||
|
||||
/// Does ROM setup of the ELF and returns the ROM digest.
|
||||
fn rom_setup(elf_path: &Path) -> Result<RomDigest, Error> {
|
||||
info!("Running command `cargo-zisk rom-setup` ...");
|
||||
fn extract_public_values_and_progam_vk(
|
||||
proof: &ZiskProofWithPublicValues,
|
||||
) -> Result<(PublicValues, ProgramVk), Error> {
|
||||
let program_vk = program_vk_from_slice(&proof.get_program_vk().vk)?;
|
||||
|
||||
let mut cmd = Command::new("cargo-zisk");
|
||||
let output = cmd
|
||||
.arg("rom-setup")
|
||||
.arg("--elf")
|
||||
.arg(elf_path)
|
||||
.output()
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
let mut public_values = vec![0; ZISK_PUBLICS * 4];
|
||||
proof.get_publics().read_slice(&mut public_values);
|
||||
proof.get_publics().head();
|
||||
|
||||
if !output.status.success() {
|
||||
Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
))?;
|
||||
}
|
||||
|
||||
// Parse the ROM digest from the stdout.
|
||||
let rom_digest = output
|
||||
.stdout
|
||||
.lines()
|
||||
.find_map(|line| {
|
||||
let line = line.ok()?;
|
||||
let line = line.split_once("Root hash: [")?.1;
|
||||
let line = line.strip_suffix("]")?;
|
||||
line.split(", ")
|
||||
.filter_map(|word| word.parse::<u64>().ok())
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.ok()
|
||||
})
|
||||
.ok_or(Error::RomDigestNotFound)?;
|
||||
|
||||
info!("Command `cargo-zisk rom-setup` succeeded");
|
||||
|
||||
Ok(rom_digest)
|
||||
Ok((public_values, program_vk))
|
||||
}
|
||||
|
||||
/// Deserialize public values as json string sequence, and parse the `RomDigest`
|
||||
/// and user set public values as `Vec<u8>`.
|
||||
fn deserialize_public_values(proof: &[u8]) -> Result<(RomDigest, Vec<u8>), Error> {
|
||||
let proof = align_to_u64(proof)?;
|
||||
let proof = bytemuck::cast_slice::<u8, u64>(&proof);
|
||||
fn program_vk_from_slice(program_vk: &[u8]) -> Result<ProgramVk, Error> {
|
||||
(program_vk.len() == 32)
|
||||
.then(|| program_vk.try_into().unwrap())
|
||||
.ok_or_else(|| Error::InvalidProgramVkLength(program_vk.len()))
|
||||
}
|
||||
|
||||
// The public values contain at least the the total number of public values,
|
||||
// `RomDigest`, and the number of user set public values.
|
||||
if proof.len() < 6 {
|
||||
return Err(Error::InvalidPublicValuesLength(proof.len()));
|
||||
}
|
||||
|
||||
// The first element is total number of public values.
|
||||
|
||||
// The next 4 elements of public values should be ROM digest.
|
||||
let rom_digest = proof[1..5].try_into().unwrap();
|
||||
|
||||
// The next element should be the number of user set public values.
|
||||
let num_user_public_values = proof[5] as usize;
|
||||
|
||||
// The rest elements should be user set public values and should be `u32`.
|
||||
let public_values = proof[6..]
|
||||
.iter()
|
||||
.map(|v| Some(u32::try_from(*v).ok()?.to_le_bytes()))
|
||||
.take(num_user_public_values)
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.ok_or(Error::InvalidPublicValue)?
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
Ok((rom_digest, public_values))
|
||||
fn vadcop_final_proof_aligned(
|
||||
proof: &ZiskProofWithPublicValues,
|
||||
) -> Result<VadcopFinalProof, Error> {
|
||||
let mut vadcop_final_proof = proof
|
||||
.get_vadcop_final_proof()
|
||||
.map_err(Error::InvalidProofFormat)?;
|
||||
vadcop_final_proof.proof = align_to_u64(vadcop_final_proof.proof)?;
|
||||
vadcop_final_proof.public_values = align_to_u64(vadcop_final_proof.public_values)?;
|
||||
Ok(vadcop_final_proof)
|
||||
}
|
||||
|
||||
/// Returns u64-aligned bytes.
|
||||
///
|
||||
/// Returns an error if `data.len()` is not a multiple of 8.
|
||||
fn align_to_u64(data: &[u8]) -> Result<Cow<'_, [u8]>, Error> {
|
||||
fn align_to_u64(data: Vec<u8>) -> Result<Vec<u8>, Error> {
|
||||
if !data.len().is_multiple_of(8) {
|
||||
return Err(Error::InvalidProofSize(data.len()));
|
||||
}
|
||||
Ok(if data.as_ptr().cast::<u64>().is_aligned() {
|
||||
Cow::Borrowed(data)
|
||||
data
|
||||
} else {
|
||||
let mut aligned: Vec<u64> = vec![0; data.len() / 8];
|
||||
bytemuck::cast_slice_mut(&mut aligned).copy_from_slice(data);
|
||||
Cow::Owned(bytemuck::cast_slice(&aligned).to_vec())
|
||||
bytemuck::cast_slice_mut(&mut aligned).copy_from_slice(&data);
|
||||
bytemuck::cast_slice(&aligned).to_vec()
|
||||
})
|
||||
}
|
||||
|
||||
fn panic_msg(err: Box<dyn Any + Send + 'static>) -> String {
|
||||
None.or_else(|| err.downcast_ref::<String>().cloned())
|
||||
.or_else(|| err.downcast_ref::<&'static str>().map(ToString::to_string))
|
||||
.unwrap_or_else(|| "unknown panic msg".to_string())
|
||||
}
|
||||
|
||||
/// Returns path to `~/.zisk` directory.
|
||||
pub(crate) fn dot_zisk_dir_path() -> PathBuf {
|
||||
fn dot_zisk_dir_path() -> PathBuf {
|
||||
PathBuf::from(env::var("HOME").expect("env `$HOME` should be set")).join(".zisk")
|
||||
}
|
||||
|
||||
/// Returns path to `~/.zisk/cache` directory.
|
||||
fn cache_path() -> PathBuf {
|
||||
dot_zisk_dir_path().join("cache")
|
||||
}
|
||||
|
||||
/// Returns path to `~/.zisk/provingKey` directory.
|
||||
fn proving_key_path() -> PathBuf {
|
||||
dot_zisk_dir_path().join("provingKey")
|
||||
}
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
//! Remote ZisK cluster proving.
|
||||
|
||||
use crate::zkvm::Error;
|
||||
use crate::zkvm::{
|
||||
Error,
|
||||
sdk::cluster::api::{
|
||||
ErrorResponse, HintsMode, InputMode, LaunchProofRequest, ProofStatusType,
|
||||
SubscribeToProofRequest, SystemStatusRequest, launch_proof_response,
|
||||
system_status_response, zisk_distributed_api_client::ZiskDistributedApiClient,
|
||||
},
|
||||
};
|
||||
use ere_zkvm_interface::zkvm::{RemoteProverConfig, block_on};
|
||||
use futures_util::StreamExt;
|
||||
use std::time::Duration;
|
||||
use tonic::transport::Channel;
|
||||
use tracing::debug;
|
||||
use zisk_distributed_grpc_api::{
|
||||
ErrorResponse, InputMode, LaunchProofRequest, ProofStatusType, SubscribeToProofRequest,
|
||||
SystemStatusRequest, launch_proof_response, system_status_response,
|
||||
zisk_distributed_api_client::ZiskDistributedApiClient,
|
||||
};
|
||||
use zisk_sdk::ZiskProofWithPublicValues;
|
||||
|
||||
#[rustfmt::skip]
|
||||
mod api;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
/// Wrapper for the ZisK cluster client.
|
||||
///
|
||||
@@ -27,14 +36,17 @@ impl ClusterClient {
|
||||
}
|
||||
|
||||
/// Sync wrapper for [`Self::prove_async`].
|
||||
pub fn prove(&self, input: &[u8]) -> Result<(Vec<u8>, Duration), Error> {
|
||||
pub fn prove(&self, input: &[u8]) -> Result<(ZiskProofWithPublicValues, Duration), Error> {
|
||||
block_on(self.prove_async(input))
|
||||
}
|
||||
|
||||
/// Send proof request to cluster and wait for completion.
|
||||
///
|
||||
/// Returns the proof and proving time reported by the cluster.
|
||||
async fn prove_async(&self, input: &[u8]) -> Result<(Vec<u8>, Duration), Error> {
|
||||
/// Returns the proof with public values and proving time reported by the cluster.
|
||||
async fn prove_async(
|
||||
&self,
|
||||
input: &[u8],
|
||||
) -> Result<(ZiskProofWithPublicValues, Duration), Error> {
|
||||
let mut client = self.client.clone();
|
||||
|
||||
// Check system status to get available compute capacity
|
||||
@@ -80,9 +92,12 @@ impl ClusterClient {
|
||||
let launch_request = LaunchProofRequest {
|
||||
data_id,
|
||||
compute_capacity,
|
||||
input_mode: InputMode::Data.into(),
|
||||
input_path: None,
|
||||
minimal_compute_capacity: compute_capacity,
|
||||
inputs_mode: InputMode::Data.into(),
|
||||
inputs_uri: None,
|
||||
input_data: Some(input.to_vec()),
|
||||
hints_mode: HintsMode::None.into(),
|
||||
hints_uri: None,
|
||||
simulated_node: None,
|
||||
};
|
||||
|
||||
@@ -120,16 +135,19 @@ impl ClusterClient {
|
||||
match ProofStatusType::try_from(update.status) {
|
||||
Ok(ProofStatusType::ProofStatusCompleted) => match update.final_proof {
|
||||
Some(final_proof) => {
|
||||
let proof = bytemuck::cast_slice(&final_proof.values).to_vec();
|
||||
let proof_with_publics = ZiskProofWithPublicValues::new_from_vadcop_proof(
|
||||
&final_proof.values,
|
||||
false,
|
||||
)
|
||||
.map_err(Error::InvalidProofFormat)?;
|
||||
let proving_time = Duration::from_millis(update.duration_ms);
|
||||
|
||||
debug!(
|
||||
proof_size = proof.len(),
|
||||
proving_time = ?proving_time,
|
||||
"Proof generated successfully"
|
||||
);
|
||||
|
||||
Ok((proof, proving_time))
|
||||
Ok((proof_with_publics, proving_time))
|
||||
}
|
||||
None => Err(cluster_error("Missing final proof")),
|
||||
},
|
||||
381
crates/zkvm/zisk/src/zkvm/sdk/cluster/api.rs
Normal file
381
crates/zkvm/zisk/src/zkvm/sdk/cluster/api.rs
Normal file
@@ -0,0 +1,381 @@
|
||||
// This file is @generated by prost-build.
|
||||
/// Standardized error response
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ErrorResponse {
|
||||
/// Error code
|
||||
#[prost(string, tag = "1")]
|
||||
pub code: ::prost::alloc::string::String,
|
||||
/// Human-readable error message
|
||||
#[prost(string, tag = "2")]
|
||||
pub message: ::prost::alloc::string::String,
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct SystemStatusRequest {}
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct SystemStatusResponse {
|
||||
#[prost(oneof = "system_status_response::Result", tags = "1, 2")]
|
||||
pub result: ::core::option::Option<system_status_response::Result>,
|
||||
}
|
||||
/// Nested message and enum types in `SystemStatusResponse`.
|
||||
pub mod system_status_response {
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)]
|
||||
pub enum Result {
|
||||
#[prost(message, tag = "1")]
|
||||
Status(super::SystemStatus),
|
||||
#[prost(message, tag = "2")]
|
||||
Error(super::ErrorResponse),
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct SystemStatus {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub total_workers: u32,
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub compute_capacity: u32,
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub idle_workers: u32,
|
||||
#[prost(uint32, tag = "4")]
|
||||
pub busy_workers: u32,
|
||||
#[prost(uint32, tag = "5")]
|
||||
pub active_jobs: u32,
|
||||
}
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct LaunchProofRequest {
|
||||
#[prost(string, tag = "1")]
|
||||
pub data_id: ::prost::alloc::string::String,
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub compute_capacity: u32,
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub minimal_compute_capacity: u32,
|
||||
#[prost(enumeration = "InputMode", tag = "4")]
|
||||
pub inputs_mode: i32,
|
||||
#[prost(string, optional, tag = "5")]
|
||||
pub inputs_uri: ::core::option::Option<::prost::alloc::string::String>,
|
||||
/// Input data sent directly from client
|
||||
#[prost(bytes = "vec", optional, tag = "6")]
|
||||
pub input_data: ::core::option::Option<::prost::alloc::vec::Vec<u8>>,
|
||||
#[prost(enumeration = "HintsMode", tag = "7")]
|
||||
pub hints_mode: i32,
|
||||
#[prost(string, optional, tag = "8")]
|
||||
pub hints_uri: ::core::option::Option<::prost::alloc::string::String>,
|
||||
/// If set, indicates this is a simulated worker
|
||||
#[prost(uint32, optional, tag = "9")]
|
||||
pub simulated_node: ::core::option::Option<u32>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct LaunchProofResponse {
|
||||
#[prost(oneof = "launch_proof_response::Result", tags = "1, 2")]
|
||||
pub result: ::core::option::Option<launch_proof_response::Result>,
|
||||
}
|
||||
/// Nested message and enum types in `LaunchProofResponse`.
|
||||
pub mod launch_proof_response {
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)]
|
||||
pub enum Result {
|
||||
#[prost(string, tag = "1")]
|
||||
JobId(::prost::alloc::string::String),
|
||||
#[prost(message, tag = "2")]
|
||||
Error(super::ErrorResponse),
|
||||
}
|
||||
}
|
||||
/// Subscription request message
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct SubscribeToProofRequest {
|
||||
#[prost(string, tag = "1")]
|
||||
pub job_id: ::prost::alloc::string::String,
|
||||
}
|
||||
/// Subscription status update (streamed to client)
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct ProofStatusUpdate {
|
||||
#[prost(string, tag = "1")]
|
||||
pub job_id: ::prost::alloc::string::String,
|
||||
#[prost(enumeration = "ProofStatusType", tag = "2")]
|
||||
pub status: i32,
|
||||
/// Present on success
|
||||
#[prost(message, optional, tag = "3")]
|
||||
pub final_proof: ::core::option::Option<FinalProof>,
|
||||
/// Present on failure
|
||||
#[prost(message, optional, tag = "4")]
|
||||
pub error: ::core::option::Option<ErrorResponse>,
|
||||
#[prost(uint64, tag = "5")]
|
||||
pub duration_ms: u64,
|
||||
}
|
||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||
pub struct FinalProof {
|
||||
#[prost(uint64, repeated, tag = "1")]
|
||||
pub values: ::prost::alloc::vec::Vec<u64>,
|
||||
#[prost(uint64, tag = "2")]
|
||||
pub executed_steps: u64,
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||
#[repr(i32)]
|
||||
pub enum InputMode {
|
||||
/// No input provided
|
||||
None = 0,
|
||||
/// Input will be provided as a PATH
|
||||
Path = 1,
|
||||
/// Input data will be sent directly
|
||||
Data = 2,
|
||||
}
|
||||
impl InputMode {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
///
|
||||
/// The values are not transformed in any way and thus are considered stable
|
||||
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||
pub fn as_str_name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::None => "INPUT_MODE_NONE",
|
||||
Self::Path => "INPUT_MODE_PATH",
|
||||
Self::Data => "INPUT_MODE_DATA",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"INPUT_MODE_NONE" => Some(Self::None),
|
||||
"INPUT_MODE_PATH" => Some(Self::Path),
|
||||
"INPUT_MODE_DATA" => Some(Self::Data),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||
#[repr(i32)]
|
||||
pub enum HintsMode {
|
||||
/// No hints provided
|
||||
None = 0,
|
||||
/// Hints will be provided as a PATH
|
||||
Path = 1,
|
||||
/// Hints will be sent as a stream
|
||||
Stream = 2,
|
||||
}
|
||||
impl HintsMode {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
///
|
||||
/// The values are not transformed in any way and thus are considered stable
|
||||
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||
pub fn as_str_name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::None => "HINTS_MODE_NONE",
|
||||
Self::Path => "HINTS_MODE_PATH",
|
||||
Self::Stream => "HINTS_MODE_STREAM",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"HINTS_MODE_NONE" => Some(Self::None),
|
||||
"HINTS_MODE_PATH" => Some(Self::Path),
|
||||
"HINTS_MODE_STREAM" => Some(Self::Stream),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||
#[repr(i32)]
|
||||
pub enum ProofStatusType {
|
||||
ProofStatusCompleted = 0,
|
||||
ProofStatusFailed = 1,
|
||||
}
|
||||
impl ProofStatusType {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
///
|
||||
/// The values are not transformed in any way and thus are considered stable
|
||||
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||
pub fn as_str_name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::ProofStatusCompleted => "PROOF_STATUS_COMPLETED",
|
||||
Self::ProofStatusFailed => "PROOF_STATUS_FAILED",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"PROOF_STATUS_COMPLETED" => Some(Self::ProofStatusCompleted),
|
||||
"PROOF_STATUS_FAILED" => Some(Self::ProofStatusFailed),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Generated client implementations.
|
||||
pub mod zisk_distributed_api_client {
|
||||
#![allow(
|
||||
unused_variables,
|
||||
dead_code,
|
||||
missing_docs,
|
||||
clippy::wildcard_imports,
|
||||
clippy::let_unit_value,
|
||||
)]
|
||||
use tonic::codegen::*;
|
||||
use tonic::codegen::http::Uri;
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ZiskDistributedApiClient<T> {
|
||||
inner: tonic::client::Grpc<T>,
|
||||
}
|
||||
impl ZiskDistributedApiClient<tonic::transport::Channel> {
|
||||
/// Attempt to create a new client by connecting to a given endpoint.
|
||||
pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
|
||||
where
|
||||
D: TryInto<tonic::transport::Endpoint>,
|
||||
D::Error: Into<StdError>,
|
||||
{
|
||||
let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
|
||||
Ok(Self::new(conn))
|
||||
}
|
||||
}
|
||||
impl<T> ZiskDistributedApiClient<T>
|
||||
where
|
||||
T: tonic::client::GrpcService<tonic::body::Body>,
|
||||
T::Error: Into<StdError>,
|
||||
T::ResponseBody: Body<Data = Bytes> + std::marker::Send + 'static,
|
||||
<T::ResponseBody as Body>::Error: Into<StdError> + std::marker::Send,
|
||||
{
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = tonic::client::Grpc::new(inner);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_origin(inner: T, origin: Uri) -> Self {
|
||||
let inner = tonic::client::Grpc::with_origin(inner, origin);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_interceptor<F>(
|
||||
inner: T,
|
||||
interceptor: F,
|
||||
) -> ZiskDistributedApiClient<InterceptedService<T, F>>
|
||||
where
|
||||
F: tonic::service::Interceptor,
|
||||
T::ResponseBody: Default,
|
||||
T: tonic::codegen::Service<
|
||||
http::Request<tonic::body::Body>,
|
||||
Response = http::Response<
|
||||
<T as tonic::client::GrpcService<tonic::body::Body>>::ResponseBody,
|
||||
>,
|
||||
>,
|
||||
<T as tonic::codegen::Service<
|
||||
http::Request<tonic::body::Body>,
|
||||
>>::Error: Into<StdError> + std::marker::Send + std::marker::Sync,
|
||||
{
|
||||
ZiskDistributedApiClient::new(InterceptedService::new(inner, interceptor))
|
||||
}
|
||||
/// Compress requests with the given encoding.
|
||||
///
|
||||
/// This requires the server to support it otherwise it might respond with an
|
||||
/// error.
|
||||
#[must_use]
|
||||
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.send_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Enable decompressing responses.
|
||||
#[must_use]
|
||||
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.accept_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of a decoded message.
|
||||
///
|
||||
/// Default: `4MB`
|
||||
#[must_use]
|
||||
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_decoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of an encoded message.
|
||||
///
|
||||
/// Default: `usize::MAX`
|
||||
#[must_use]
|
||||
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_encoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
/// System status
|
||||
pub async fn system_status(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::SystemStatusRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::SystemStatusResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic_prost::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/zisk.distributed.api.v1.ZiskDistributedApi/SystemStatus",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new(
|
||||
"zisk.distributed.api.v1.ZiskDistributedApi",
|
||||
"SystemStatus",
|
||||
),
|
||||
);
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
/// Launch a proof job
|
||||
pub async fn launch_proof(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::LaunchProofRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::LaunchProofResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic_prost::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/zisk.distributed.api.v1.ZiskDistributedApi/LaunchProof",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new(
|
||||
"zisk.distributed.api.v1.ZiskDistributedApi",
|
||||
"LaunchProof",
|
||||
),
|
||||
);
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
/// Client subscription to job completion
|
||||
pub async fn subscribe_to_proof(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::SubscribeToProofRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<tonic::codec::Streaming<super::ProofStatusUpdate>>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic_prost::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/zisk.distributed.api.v1.ZiskDistributedApi/SubscribeToProof",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new(
|
||||
"zisk.distributed.api.v1.ZiskDistributedApi",
|
||||
"SubscribeToProof",
|
||||
),
|
||||
);
|
||||
self.inner.server_streaming(req, path, codec).await
|
||||
}
|
||||
}
|
||||
}
|
||||
31
crates/zkvm/zisk/src/zkvm/sdk/cluster/test.rs
Normal file
31
crates/zkvm/zisk/src/zkvm/sdk/cluster/test.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use std::{env, fs, path::PathBuf};
|
||||
|
||||
/// To sync generated `api.rs`, run:
|
||||
///
|
||||
/// ```
|
||||
/// cargo test --package ere-zisk --lib --release -- zkvm::cluster_client::test::api_generation --exact
|
||||
/// ```
|
||||
#[test]
|
||||
fn api_generation() {
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
let proto_dir = dir.join("src/zkvm/sdk/cluster");
|
||||
|
||||
tonic_prost_build::configure()
|
||||
.build_server(false)
|
||||
.out_dir(tempdir.path())
|
||||
.compile_protos(
|
||||
&[&proto_dir.join("zisk_distributed_api.proto")],
|
||||
&[&proto_dir],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let latest = tempdir.path().join("zisk.distributed.api.v1.rs");
|
||||
let current = proto_dir.join("api.rs");
|
||||
|
||||
// If it's in CI env, don't overwrite but only check if it's up-to-date.
|
||||
if env::var_os("GITHUB_ACTIONS").is_none() {
|
||||
fs::copy(&latest, ¤t).unwrap();
|
||||
}
|
||||
assert_eq!(fs::read(&latest).unwrap(), fs::read(¤t).unwrap());
|
||||
}
|
||||
112
crates/zkvm/zisk/src/zkvm/sdk/cluster/zisk_distributed_api.proto
Normal file
112
crates/zkvm/zisk/src/zkvm/sdk/cluster/zisk_distributed_api.proto
Normal file
@@ -0,0 +1,112 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package zisk.distributed.api.v1;
|
||||
|
||||
// ============================================================================
|
||||
// gRPC Service Definition
|
||||
// ============================================================================
|
||||
|
||||
service ZiskDistributedApi {
|
||||
// System status
|
||||
rpc SystemStatus(SystemStatusRequest) returns (SystemStatusResponse);
|
||||
|
||||
// Launch a proof job
|
||||
rpc LaunchProof(LaunchProofRequest) returns (LaunchProofResponse);
|
||||
|
||||
// Client subscription to job completion
|
||||
rpc SubscribeToProof(SubscribeToProofRequest) returns (stream ProofStatusUpdate);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Common Types
|
||||
// ============================================================================
|
||||
|
||||
// Standardized error response
|
||||
message ErrorResponse {
|
||||
string code = 1; // Error code
|
||||
string message = 2; // Human-readable error message
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SystemStatus
|
||||
// ============================================================================
|
||||
|
||||
message SystemStatusRequest {}
|
||||
|
||||
message SystemStatusResponse {
|
||||
oneof result {
|
||||
SystemStatus status = 1;
|
||||
ErrorResponse error = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message SystemStatus {
|
||||
uint32 total_workers = 1;
|
||||
uint32 compute_capacity = 2;
|
||||
uint32 idle_workers = 3;
|
||||
uint32 busy_workers = 4;
|
||||
uint32 active_jobs = 5;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// LaunchProof
|
||||
// ============================================================================
|
||||
|
||||
message LaunchProofRequest {
|
||||
string data_id = 1;
|
||||
uint32 compute_capacity = 2;
|
||||
uint32 minimal_compute_capacity = 3;
|
||||
InputMode inputs_mode = 4;
|
||||
optional string inputs_uri = 5;
|
||||
optional bytes input_data = 6; // Input data sent directly from client
|
||||
HintsMode hints_mode = 7;
|
||||
optional string hints_uri = 8;
|
||||
optional uint32 simulated_node = 9; // If set, indicates this is a simulated worker
|
||||
}
|
||||
|
||||
enum InputMode {
|
||||
INPUT_MODE_NONE = 0; // No input provided
|
||||
INPUT_MODE_PATH = 1; // Input will be provided as a PATH
|
||||
INPUT_MODE_DATA = 2; // Input data will be sent directly
|
||||
}
|
||||
|
||||
enum HintsMode {
|
||||
HINTS_MODE_NONE = 0; // No hints provided
|
||||
HINTS_MODE_PATH = 1; // Hints will be provided as a PATH
|
||||
HINTS_MODE_STREAM = 2; // Hints will be sent as a stream
|
||||
}
|
||||
|
||||
message LaunchProofResponse {
|
||||
oneof result {
|
||||
string job_id = 1;
|
||||
ErrorResponse error = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SubscribeToProof
|
||||
// ============================================================================
|
||||
|
||||
// Subscription request message
|
||||
message SubscribeToProofRequest {
|
||||
string job_id = 1;
|
||||
}
|
||||
|
||||
// Subscription status update (streamed to client)
|
||||
message ProofStatusUpdate {
|
||||
string job_id = 1;
|
||||
ProofStatusType status = 2;
|
||||
optional FinalProof final_proof = 3; // Present on success
|
||||
optional ErrorResponse error = 4; // Present on failure
|
||||
uint64 duration_ms = 5;
|
||||
}
|
||||
|
||||
enum ProofStatusType {
|
||||
PROOF_STATUS_COMPLETED = 0;
|
||||
PROOF_STATUS_FAILED = 1;
|
||||
}
|
||||
|
||||
message FinalProof {
|
||||
repeated uint64 values = 1;
|
||||
uint64 executed_steps = 2;
|
||||
}
|
||||
100
crates/zkvm/zisk/src/zkvm/sdk/local.rs
Normal file
100
crates/zkvm/zisk/src/zkvm/sdk/local.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
use crate::zkvm::{
|
||||
Error,
|
||||
sdk::{cache_path, panic_msg, proving_key_path},
|
||||
};
|
||||
use ere_zkvm_interface::zkvm::CommonError;
|
||||
use parking_lot::{Mutex, MutexGuard};
|
||||
use proofman::ProofMan;
|
||||
use proofman_common::VerboseMode;
|
||||
use proofman_fields::Goldilocks;
|
||||
use std::{fs, ops::Deref};
|
||||
use std::{panic, time::Duration};
|
||||
use tempfile::tempdir;
|
||||
use tracing::info;
|
||||
use zisk_rom_setup::{assembly_files_exist, gen_assembly};
|
||||
use zisk_sdk::{
|
||||
Asm, ElfBinaryFromFile, ProofOpts, ProverClient, ZiskProofWithPublicValues, ZiskProver,
|
||||
ZiskStdin,
|
||||
};
|
||||
|
||||
pub struct LocalProver {
|
||||
elf: Vec<u8>,
|
||||
setup: Mutex<bool>,
|
||||
prover: Mutex<Option<ZiskProver<Asm>>>,
|
||||
}
|
||||
|
||||
impl LocalProver {
|
||||
pub fn new(elf: Vec<u8>) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
elf,
|
||||
setup: Mutex::new(false),
|
||||
prover: Mutex::new(None),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn prove(&self, stdin: &[u8]) -> Result<(ZiskProofWithPublicValues, Duration), Error> {
|
||||
self.setup()?;
|
||||
|
||||
let prover = self.prover().map_err(Error::InitProver)?;
|
||||
let stdin = ZiskStdin::from_vec(stdin.to_vec());
|
||||
let tempdir = tempdir().map_err(CommonError::tempdir)?;
|
||||
let opts = ProofOpts::default().output_dir(tempdir.path().to_path_buf());
|
||||
|
||||
let result = panic::catch_unwind(|| prover.prove(stdin).with_proof_options(opts).run())
|
||||
.map_err(|err| {
|
||||
drop(prover);
|
||||
self.prover.lock().take();
|
||||
Error::ProvePanic(panic_msg(err))
|
||||
})?
|
||||
.map_err(Error::Prove)?;
|
||||
|
||||
Ok((
|
||||
result.get_proof_with_publics().clone(),
|
||||
result.get_duration(),
|
||||
))
|
||||
}
|
||||
|
||||
fn setup(&self) -> Result<(), Error> {
|
||||
let mut guard = self.setup.lock();
|
||||
if *guard {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!("Running Proofman check_setup...");
|
||||
|
||||
ProofMan::<Goldilocks>::check_setup(proving_key_path(), true, VerboseMode::Info)
|
||||
.map_err(Error::CheckSetup)?;
|
||||
|
||||
*guard = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prover(&self) -> anyhow::Result<impl Deref<Target = ZiskProver<Asm>>> {
|
||||
let mut guard = self.prover.lock();
|
||||
|
||||
if guard.is_none() {
|
||||
let tempdir = tempdir().map_err(CommonError::tempdir)?;
|
||||
let elf_path = tempdir.path().join("elf");
|
||||
|
||||
fs::write(&elf_path, &self.elf)
|
||||
.map_err(|err| CommonError::write_file("elf", &elf_path, err))?;
|
||||
|
||||
if !assembly_files_exist(&elf_path, &cache_path(), false)? {
|
||||
gen_assembly(&elf_path, &None, &None, false, false)?;
|
||||
}
|
||||
|
||||
info!("Initializing local ZisK prover...");
|
||||
|
||||
let prover = ProverClient::builder().asm().build()?;
|
||||
|
||||
let elf_binary = ElfBinaryFromFile::new(&elf_path, false)?;
|
||||
prover.setup(&elf_binary)?;
|
||||
|
||||
info!("Local ZisK prover initialized");
|
||||
|
||||
*guard = Some(prover);
|
||||
}
|
||||
|
||||
Ok(MutexGuard::map(guard, |prover| prover.as_mut().unwrap()))
|
||||
}
|
||||
}
|
||||
@@ -1,461 +0,0 @@
|
||||
//! Local ZisK server management via `cargo-zisk` commands.
|
||||
|
||||
use crate::zkvm::{Error, sdk::dot_zisk_dir_path};
|
||||
use ere_zkvm_interface::zkvm::CommonError;
|
||||
use parking_lot::Mutex;
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
env, fs,
|
||||
io::Write,
|
||||
iter,
|
||||
net::{Ipv4Addr, TcpStream},
|
||||
path::{Path, PathBuf},
|
||||
process::{Child, Command, Stdio},
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use strum::{EnumIter, IntoEnumIterator};
|
||||
use tempfile::tempdir;
|
||||
use tracing::{error, info};
|
||||
use wait_timeout::ChildExt;
|
||||
|
||||
pub const DEFAULT_START_SERVER_TIMEOUT_SEC: u64 = 120; // 2 mins
|
||||
pub const DEFAULT_SHUTDOWN_SERVER_TIMEOUT_SEC: u64 = 30; // 30 secs
|
||||
pub const DEFAULT_PROVE_TIMEOUT_SEC: u64 = 3600; // 1 hour
|
||||
|
||||
/// ZisK server status returned from `cargo-zisk prove-client status`.
|
||||
#[derive(Debug)]
|
||||
pub enum ZiskServerStatus {
|
||||
Idle,
|
||||
Working,
|
||||
}
|
||||
|
||||
/// Options of `cargo-zisk` commands.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, EnumIter)]
|
||||
pub enum ZiskServerOption {
|
||||
Port,
|
||||
UnlockMappedMemory, // Should be set if locked memory is not enough
|
||||
MinimalMemory,
|
||||
// GPU options
|
||||
Preallocate, // Should be set only if GPU memory is enough
|
||||
SharedTables,
|
||||
MaxStreams,
|
||||
NumberThreadsWitness,
|
||||
MaxWitnessStored,
|
||||
}
|
||||
|
||||
impl ZiskServerOption {
|
||||
/// The key of the env variable to read from.
|
||||
fn env_var_key(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Port => "ERE_ZISK_PORT",
|
||||
Self::UnlockMappedMemory => "ERE_ZISK_UNLOCK_MAPPED_MEMORY",
|
||||
Self::MinimalMemory => "ERE_ZISK_MINIMAL_MEMORY",
|
||||
Self::Preallocate => "ERE_ZISK_PREALLOCATE",
|
||||
Self::SharedTables => "ERE_ZISK_SHARED_TABLES",
|
||||
Self::MaxStreams => "ERE_ZISK_MAX_STREAMS",
|
||||
Self::NumberThreadsWitness => "ERE_ZISK_NUMBER_THREADS_WITNESS",
|
||||
Self::MaxWitnessStored => "ERE_ZISK_MAX_WITNESS_STORED",
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the option is a flag (false-by-default boolean option) or not.
|
||||
///
|
||||
/// When we read the option from env variable, if the option is a flag,
|
||||
/// we only check if the env variable is set or not.
|
||||
fn is_flag(&self) -> bool {
|
||||
match self {
|
||||
Self::UnlockMappedMemory
|
||||
| Self::MinimalMemory
|
||||
| Self::Preallocate
|
||||
| Self::SharedTables => true,
|
||||
Self::Port | Self::MaxStreams | Self::NumberThreadsWitness | Self::MaxWitnessStored => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The option key to be appended to `cargo-zisk` command arguments.
|
||||
fn key(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Port => "--port",
|
||||
Self::UnlockMappedMemory => "--unlock-mapped-memory",
|
||||
// NOTE: Use snake case for `prove-client` command
|
||||
// Issue for tracking: https://github.com/eth-act/ere/issues/151.
|
||||
Self::MinimalMemory => "--minimal_memory",
|
||||
Self::Preallocate => "--preallocate",
|
||||
Self::SharedTables => "--shared-tables",
|
||||
Self::MaxStreams => "--max-streams",
|
||||
Self::NumberThreadsWitness => "--number-threads-witness",
|
||||
Self::MaxWitnessStored => "--max-witness-stored",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configurable options for `cargo-zisk server` and `cargo-zisk prove-client` commands.
|
||||
#[derive(Clone)]
|
||||
pub struct ZiskServerOptions(BTreeMap<ZiskServerOption, String>);
|
||||
|
||||
impl ZiskServerOptions {
|
||||
/// Read options from env variables.
|
||||
pub fn from_env() -> Self {
|
||||
Self(
|
||||
ZiskServerOption::iter()
|
||||
.flat_map(|option| env::var(option.env_var_key()).ok().map(|val| (option, val)))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns `cargo-zisk` command arguments by given options that have been
|
||||
/// set.
|
||||
fn args(
|
||||
&self,
|
||||
options: impl IntoIterator<Item = ZiskServerOption>,
|
||||
) -> impl Iterator<Item = &str> {
|
||||
options
|
||||
.into_iter()
|
||||
.filter(|option| self.0.contains_key(option))
|
||||
.flat_map(|option| {
|
||||
iter::once(option.key())
|
||||
.chain((!option.is_flag()).then(|| self.0[&option].as_str()))
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `cargo-zisk server` command arguments.
|
||||
pub(crate) fn server_args(&self) -> impl Iterator<Item = &str> {
|
||||
self.args([
|
||||
ZiskServerOption::Port,
|
||||
ZiskServerOption::UnlockMappedMemory,
|
||||
ZiskServerOption::Preallocate,
|
||||
ZiskServerOption::SharedTables,
|
||||
ZiskServerOption::MaxStreams,
|
||||
ZiskServerOption::NumberThreadsWitness,
|
||||
ZiskServerOption::MaxWitnessStored,
|
||||
])
|
||||
}
|
||||
|
||||
/// Returns `cargo-zisk prove-client` command arguments.
|
||||
pub(crate) fn prove_client_args(&self) -> impl Iterator<Item = &str> {
|
||||
self.args([ZiskServerOption::Port])
|
||||
}
|
||||
|
||||
/// Returns `cargo-zisk prove-client prove` command arguments.
|
||||
pub(crate) fn prove_args(&self) -> impl Iterator<Item = &str> {
|
||||
self.prove_client_args()
|
||||
.chain(self.args([ZiskServerOption::MinimalMemory]))
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper for ZisK server child process.
|
||||
pub struct ZiskServer {
|
||||
options: ZiskServerOptions,
|
||||
elf_path: PathBuf,
|
||||
cuda: bool,
|
||||
child: Mutex<Option<Child>>,
|
||||
}
|
||||
|
||||
impl Drop for ZiskServer {
|
||||
fn drop(&mut self) {
|
||||
self.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
impl ZiskServer {
|
||||
/// Create a new ZisK server for the given ELF.
|
||||
///
|
||||
/// The server process is lazily started on the first call to [`prove`](ZiskServer::prove).
|
||||
pub fn new(elf_path: &Path, cuda: bool, options: ZiskServerOptions) -> Self {
|
||||
Self {
|
||||
elf_path: elf_path.to_path_buf(),
|
||||
cuda,
|
||||
options,
|
||||
child: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Send prove request to server and wait for proof to be created.
|
||||
///
|
||||
/// Returns the proof.
|
||||
pub fn prove(&self, input: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
self.ensure_ready()?;
|
||||
|
||||
// Prefix that ZisK server will add to the file name of the proof.
|
||||
// We use constant because the file will be save to a temporary dir,
|
||||
// so there will be no conflict.
|
||||
const PREFIX: &str = "ere";
|
||||
|
||||
let tempdir = tempdir().map_err(CommonError::tempdir)?;
|
||||
let input_path = tempdir.path().join("input");
|
||||
let output_path = tempdir.path().join("output");
|
||||
let proof_path = output_path.join(format!("{PREFIX}-vadcop_final_proof.bin"));
|
||||
|
||||
fs::write(&input_path, input)
|
||||
.map_err(|err| CommonError::write_file("input", &input_path, err))?;
|
||||
|
||||
// NOTE: Use snake case for `prove-client` command
|
||||
// Issue for tracking: https://github.com/eth-act/ere/issues/151.
|
||||
let mut cmd = Command::new("cargo-zisk");
|
||||
let output = cmd
|
||||
.args(["prove-client", "prove"])
|
||||
.arg("--input")
|
||||
.arg(input_path)
|
||||
.arg("--output_dir")
|
||||
.arg(&output_path)
|
||||
.args(["-p", PREFIX])
|
||||
.args(["--aggregation", "--verify_proofs"])
|
||||
.args(self.options.prove_args())
|
||||
.output()
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
))?;
|
||||
}
|
||||
|
||||
// ZisK server will finish the `prove` requested above then respond the
|
||||
// following `status`. So if the following `status` succeeds, the proof
|
||||
// should also be ready.
|
||||
self.status(prove_timeout()).map_err(|err| {
|
||||
if matches!(err, Error::TimeoutWaitingServerReady) {
|
||||
Error::TimeoutWaitingServerProving
|
||||
} else if err.to_string().contains("EOF") {
|
||||
Error::ServerCrashed
|
||||
} else {
|
||||
err
|
||||
}
|
||||
})?;
|
||||
|
||||
let proof = fs::read(&proof_path)
|
||||
.map_err(|err| CommonError::read_file("proof", &proof_path, err))?;
|
||||
|
||||
Ok(proof)
|
||||
}
|
||||
|
||||
/// Ensure the server is running and responsive, restarting it if needed.
|
||||
pub fn ensure_ready(&self) -> Result<(), Error> {
|
||||
if self.child.lock().is_some() && self.status(start_server_timeout()).is_ok() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
const MAX_RETRY: usize = 3;
|
||||
let mut attempt = 0;
|
||||
|
||||
loop {
|
||||
self.shutdown();
|
||||
match self.start() {
|
||||
Ok(()) => return Ok(()),
|
||||
Err(Error::TimeoutWaitingServerReady) if attempt < MAX_RETRY => {
|
||||
error!("Timeout waiting server ready, restarting...");
|
||||
attempt += 1;
|
||||
continue;
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn the server process and wait until it's ready.
|
||||
fn start(&self) -> Result<(), Error> {
|
||||
info!("Starting ZisK server...");
|
||||
|
||||
let (cargo_zisk, witness_lib_name) = if self.cuda {
|
||||
("cargo-zisk-cuda", "libzisk_witness_cuda.so")
|
||||
} else {
|
||||
("cargo-zisk", "libzisk_witness.so")
|
||||
};
|
||||
let witness_lib_path = dot_zisk_dir_path().join("bin").join(witness_lib_name);
|
||||
|
||||
let mut cmd = Command::new(cargo_zisk);
|
||||
cmd.arg("server")
|
||||
.args(self.options.server_args())
|
||||
.arg("--elf")
|
||||
.arg(&self.elf_path)
|
||||
.arg("--witness-lib")
|
||||
.arg(witness_lib_path)
|
||||
.arg("--aggregation");
|
||||
|
||||
let child = cmd.spawn().map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
{
|
||||
let mut guard = self.child.lock();
|
||||
*guard = Some(child);
|
||||
}
|
||||
|
||||
self.wait_until_ready()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Wait until the server status to be idle.
|
||||
pub fn wait_until_ready(&self) -> Result<(), Error> {
|
||||
const INTERVAL: Duration = Duration::from_secs(1);
|
||||
|
||||
let timeout = start_server_timeout();
|
||||
|
||||
info!("Waiting until server is ready...");
|
||||
|
||||
let start = Instant::now();
|
||||
while !matches!(self.status(timeout), Ok(ZiskServerStatus::Idle)) {
|
||||
if start.elapsed() > timeout {
|
||||
return Err(Error::TimeoutWaitingServerReady);
|
||||
}
|
||||
thread::sleep(INTERVAL);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gracefully shut down the server, falling back to force-kill on failure.
|
||||
fn shutdown(&self) {
|
||||
let mut guard = self.child.lock();
|
||||
let Some(mut child) = guard.take() else {
|
||||
return;
|
||||
};
|
||||
|
||||
info!("Shutting down ZisK server");
|
||||
|
||||
let mut cmd = Command::new("cargo-zisk");
|
||||
let result = cmd
|
||||
.args(["prove-client", "shutdown"])
|
||||
.args(self.options.prove_client_args())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.and_then(
|
||||
|mut child| match child.wait_timeout(shutdown_server_timeout())? {
|
||||
Some(_) => child.wait_with_output(),
|
||||
None => {
|
||||
child.kill().ok();
|
||||
Err(std::io::Error::other("shutdown command timed out"))
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if result.as_ref().is_ok_and(|output| output.status.success()) {
|
||||
info!("Shutdown ZisK server");
|
||||
} else {
|
||||
error!(
|
||||
"Failed to shutdown ZisK server: {}",
|
||||
result
|
||||
.map(|output| String::from_utf8_lossy(&output.stderr).to_string())
|
||||
.unwrap_or_else(|err| err.to_string())
|
||||
);
|
||||
error!("Shutdown server child process and asm services manually...");
|
||||
let _ = child.kill();
|
||||
shutdown_asm_service(23115);
|
||||
shutdown_asm_service(23116);
|
||||
shutdown_asm_service(23117);
|
||||
remove_shm_files();
|
||||
}
|
||||
}
|
||||
|
||||
/// Get status of server.
|
||||
fn status(&self, timeout: Duration) -> Result<ZiskServerStatus, Error> {
|
||||
let mut cmd = Command::new("cargo-zisk");
|
||||
let mut child = cmd
|
||||
.args(["prove-client", "status"])
|
||||
.args(self.options.prove_client_args())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if child
|
||||
.wait_timeout(timeout)
|
||||
.map_err(|err| CommonError::command(&cmd, err))?
|
||||
.is_none()
|
||||
{
|
||||
// Timeout reached, kill the process
|
||||
child.kill().ok();
|
||||
return Err(Error::TimeoutWaitingServerReady);
|
||||
}
|
||||
|
||||
let output = child
|
||||
.wait_with_output()
|
||||
.map_err(|err| CommonError::command(&cmd, err))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(CommonError::command_exit_non_zero(
|
||||
&cmd,
|
||||
output.status,
|
||||
Some(&output),
|
||||
))?;
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
if stdout.contains("idle") {
|
||||
Ok(ZiskServerStatus::Idle)
|
||||
} else if stdout.contains("working") {
|
||||
Ok(ZiskServerStatus::Working)
|
||||
} else {
|
||||
Err(Error::UnknownServerStatus {
|
||||
stdout: stdout.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send shutdown request to ZisK asm services.
|
||||
fn shutdown_asm_service(port: u16) {
|
||||
// According to https://github.com/0xPolygonHermez/zisk/blob/v0.15.0/emulator-asm/asm-runner/src/asm_services/mod.rs#L34.
|
||||
const CMD_SHUTDOWN_REQUEST_ID: u64 = 1000000;
|
||||
if let Ok(mut stream) = TcpStream::connect((Ipv4Addr::LOCALHOST, port)) {
|
||||
let _ = stream.write_all(
|
||||
&[CMD_SHUTDOWN_REQUEST_ID, 0, 0, 0, 0]
|
||||
.into_iter()
|
||||
.flat_map(|word| word.to_le_bytes())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove shared memory created by ZisK.
|
||||
fn remove_shm_files() {
|
||||
let Ok(shm_dir) = fs::read_dir(Path::new("/dev/shm")) else {
|
||||
return;
|
||||
};
|
||||
|
||||
for entry in shm_dir.flatten() {
|
||||
let path = entry.path();
|
||||
if path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.is_some_and(|name| name.starts_with("ZISK") || name.starts_with("sem"))
|
||||
{
|
||||
let _ = fs::remove_file(&path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the server start timeout, configurable via `ERE_ZISK_START_SERVER_TIMEOUT_SEC`.
|
||||
fn start_server_timeout() -> Duration {
|
||||
timeout(
|
||||
"ERE_ZISK_START_SERVER_TIMEOUT_SEC",
|
||||
DEFAULT_START_SERVER_TIMEOUT_SEC,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the server shutdown timeout, configurable via `ERE_ZISK_SHUTDOWN_SERVER_TIMEOUT_SEC`.
|
||||
fn shutdown_server_timeout() -> Duration {
|
||||
timeout(
|
||||
"ERE_ZISK_SHUTDOWN_SERVER_TIMEOUT_SEC",
|
||||
DEFAULT_SHUTDOWN_SERVER_TIMEOUT_SEC,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the prove timeout, configurable via `ERE_ZISK_PROVE_TIMEOUT_SEC`.
|
||||
fn prove_timeout() -> Duration {
|
||||
timeout("ERE_ZISK_PROVE_TIMEOUT_SEC", DEFAULT_PROVE_TIMEOUT_SEC)
|
||||
}
|
||||
|
||||
/// Read a timeout from the given env variable key, falling back to `default`.
|
||||
fn timeout(key: &str, default: u64) -> Duration {
|
||||
let sec = env::var(key)
|
||||
.ok()
|
||||
.and_then(|timeout| timeout.parse::<u64>().ok())
|
||||
.unwrap_or(default);
|
||||
Duration::from_secs(sec)
|
||||
}
|
||||
@@ -6,9 +6,6 @@ FROM $BASE_IMAGE
|
||||
# ZisK requires Ubuntu 22.04 or higher (ere-base uses 24.04 by default).
|
||||
# We operate as root for SDK and dependency installation.
|
||||
|
||||
# Whether to enable CUDA feature or not.
|
||||
ARG CUDA
|
||||
|
||||
# Install ZisK system dependencies (for Ubuntu)
|
||||
# Taken from https://0xpolygonhermez.github.io/zisk/getting_started/installation.html
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
@@ -37,8 +34,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc-riscv64-unknown-elf \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Default to build for RTX 50 series
|
||||
ARG CUDA_ARCH=sm_120
|
||||
|
||||
# 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
|
||||
|
||||
@@ -14,6 +14,9 @@ WORKDIR /ere
|
||||
ARG CUDA
|
||||
ARG RUSTFLAGS
|
||||
|
||||
# Default to build for RTX 50 series
|
||||
ARG CUDA_ARCH=sm_120
|
||||
|
||||
RUN cargo build --release --package ere-server --bin ere-server --features zisk${CUDA:+,cuda} \
|
||||
&& mkdir bin && mv target/release/ere-server bin/ere-server \
|
||||
&& cargo clean && rm -rf $CARGO_HOME/registry/
|
||||
|
||||
@@ -28,69 +28,49 @@ echo "Installing ZisK Toolchain and SDK using ziskup (prebuilt binaries)..."
|
||||
ensure_tool_installed "curl" "to download the ziskup installer"
|
||||
ensure_tool_installed "bash" "to run the ziskup installer"
|
||||
ensure_tool_installed "rustup" "for managing Rust toolchains (ZisK installs its own)"
|
||||
ensure_tool_installed "cargo" "as cargo-zisk is a cargo subcommand"
|
||||
ensure_tool_installed "cargo" "to pre-build lib-c"
|
||||
|
||||
# Step 1: Download and run the script that installs the ziskup binary itself.
|
||||
# Export SETUP_KEY=proving-no-consttree to download proving key but avoid doing
|
||||
# cargo-zisk check-setup.
|
||||
export ZISK_VERSION="0.15.0"
|
||||
export SETUP_KEY=${SETUP_KEY:=proving-no-consttree}
|
||||
curl "https://raw.githubusercontent.com/0xPolygonHermez/zisk/main/ziskup/install.sh" | bash
|
||||
unset SETUP_KEY
|
||||
# Export SETUP_KEY=proving-no-consttree to download proving key without doing setup.
|
||||
export ZISK_VERSION="0.16.0"
|
||||
# export SETUP_KEY=${SETUP_KEY:=proving-no-consttree}
|
||||
# curl "https://raw.githubusercontent.com/0xPolygonHermez/zisk/main/ziskup/install.sh" | bash
|
||||
# unset SETUP_KEY
|
||||
|
||||
# Step 2: Ensure the installed cargo-zisk binary is in PATH for this script session.
|
||||
export PATH="$PATH:$HOME/.zisk/bin"
|
||||
|
||||
# FIXME: Issue for tracking: https://github.com/eth-act/ere/issues/200.
|
||||
# FIXME: Remove and download from prebuilt when released
|
||||
if true; then
|
||||
ZISK_DIR="$HOME/.zisk"
|
||||
BUCKET_URL="https://storage.googleapis.com/zisk-setup"
|
||||
KEY_FILE="zisk-provingkey-pre-$ZISK_VERSION.tar.gz"
|
||||
|
||||
mkdir -p "$ZISK_DIR/bin" "$ZISK_DIR/zisk/emulator-asm"
|
||||
|
||||
# Download and install proving key
|
||||
rm -rf "$ZISK_DIR/provingKey" "$ZISK_DIR/verifyKey" "$ZISK_DIR/cache"
|
||||
curl -L -#o "/tmp/$KEY_FILE" "$BUCKET_URL/$KEY_FILE"
|
||||
tar -xf "/tmp/$KEY_FILE" -C "$ZISK_DIR"
|
||||
rm -f "/tmp/$KEY_FILE"
|
||||
|
||||
# Build libziskclib.a
|
||||
WORKSPACE=$(mktemp -d)
|
||||
git clone https://github.com/han0110/zisk.git --depth 1 --branch patch/v0.15.0 "$WORKSPACE"
|
||||
cargo build --manifest-path "$WORKSPACE/Cargo.toml" --release
|
||||
cp "$WORKSPACE/target/release/cargo-zisk" "$HOME/.zisk/bin/cargo-zisk"
|
||||
cp "$WORKSPACE/target/release/libzisk_witness.so" "$HOME/.zisk/bin/libzisk_witness.so"
|
||||
rm -rf "$WORKSPACE"
|
||||
git clone --depth 1 --branch "pre-develop-$ZISK_VERSION" https://github.com/0xPolygonHermez/zisk.git "$WORKSPACE"
|
||||
cargo build --manifest-path "$WORKSPACE/Cargo.toml" --release --package ziskclib --package cargo-zisk
|
||||
|
||||
# Install toolchain
|
||||
"$WORKSPACE/target/release/cargo-zisk" sdk install-toolchain
|
||||
|
||||
# Copy files
|
||||
cp "$WORKSPACE/target/release/cargo-zisk" "$ZISK_DIR/bin/"
|
||||
cp "$WORKSPACE/target/release/libziskclib.a" "$ZISK_DIR/bin/"
|
||||
cp -r "$WORKSPACE/emulator-asm/src" "$ZISK_DIR/zisk/emulator-asm/"
|
||||
cp "$WORKSPACE/emulator-asm/Makefile" "$ZISK_DIR/zisk/emulator-asm/"
|
||||
cp -r "$WORKSPACE/lib-c" "$ZISK_DIR/zisk/"
|
||||
|
||||
# Cleanup
|
||||
rm -rf "${WORKSPACE}"
|
||||
fi
|
||||
|
||||
# Verify ZisK installation
|
||||
echo "Verifying ZisK installation..."
|
||||
|
||||
echo "Checking for 'zisk' toolchain..."
|
||||
if rustup toolchain list | grep -q "^zisk"; then
|
||||
echo "ZisK Rust toolchain found."
|
||||
else
|
||||
echo "Error: ZisK Rust toolchain ('zisk') not found after installation!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Checking for cargo-zisk CLI tool..."
|
||||
if cargo-zisk --version; then
|
||||
echo "cargo-zisk CLI tool verified successfully."
|
||||
else
|
||||
echo "Error: 'cargo-zisk --version' failed." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Step 3: Build cargo-zisk-cuda from source with `gpu` feature enabled
|
||||
if [ -n "$CUDA" ]; then
|
||||
WORKSPACE=$(mktemp -d)
|
||||
# FIXME: Issue for tracking: https://github.com/eth-act/ere/issues/200.
|
||||
# git clone https://github.com/0xPolygonHermez/zisk.git --depth 1 --tag "v$ZISK_VERSION" "$WORKSPACE"
|
||||
git clone https://github.com/han0110/zisk.git --depth 1 --branch patch/v0.15.0 "$WORKSPACE"
|
||||
cargo build --manifest-path "$WORKSPACE/Cargo.toml" --release --features gpu
|
||||
cp "$WORKSPACE/target/release/cargo-zisk" "$HOME/.zisk/bin/cargo-zisk-cuda"
|
||||
cp "$WORKSPACE/target/release/libzisk_witness.so" "$HOME/.zisk/bin/libzisk_witness_cuda.so"
|
||||
rm -rf "$WORKSPACE"
|
||||
|
||||
echo "Checking for cargo-zisk-cuda CLI tool..."
|
||||
if cargo-zisk-cuda --version; then
|
||||
echo "cargo-zisk-cuda CLI tool verified successfully."
|
||||
else
|
||||
echo "Error: 'cargo-zisk-cuda --version' failed." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Step 4: Make sure `lib-c`'s build script is ran.
|
||||
# Step 2: Make sure `lib-c`'s build script is ran.
|
||||
#
|
||||
# `ziskos` provides guest program runtime, and `lib-c` is a dependency of `ziskos`,
|
||||
# when we need to compile guest, the `build.rs` of `lib-c` will need to be ran once,
|
||||
@@ -98,8 +78,8 @@ fi
|
||||
# So here we make sure it's already ran, and the built thing will be stored in
|
||||
# `$CARGO_HOME/git/checkouts/zisk-{hash}/{rev}/lib-c/c/build`, so could be
|
||||
# re-used as long as the `ziskos` has the same version.
|
||||
WORKSPACE="/tmp/build-lib-c"
|
||||
cargo new "$WORKSPACE" --name build-lib-c
|
||||
cargo add lib-c --git https://github.com/0xPolygonHermez/zisk.git --tag "v$ZISK_VERSION" --manifest-path "$WORKSPACE/Cargo.toml"
|
||||
WORKSPACE=$(mktemp -d)
|
||||
cargo init "$WORKSPACE" --name build-lib-c
|
||||
cargo add lib-c --git https://github.com/0xPolygonHermez/zisk.git --branch "pre-develop-$ZISK_VERSION" --manifest-path "$WORKSPACE/Cargo.toml"
|
||||
cargo build --manifest-path "$WORKSPACE/Cargo.toml"
|
||||
rm -rf "$WORKSPACE"
|
||||
|
||||
@@ -11,3 +11,6 @@ require (
|
||||
github.com/usbarmory/tamago v0.0.0-20250710154000-3dd21eabac74 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
)
|
||||
|
||||
// FIXME: Remove when it is upstreamed
|
||||
replace github.com/eth-act/skunkworks-tama => github.com/han0110/skunkworks-tama v0.0.0-20260218141853-cc6e788414fe
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
github.com/eth-act/skunkworks-tama v0.0.0-20251105112532-eff8e3af014b h1:Nm1FYhFjCWnKkaRZzSjGmeWdqevsunbpOnYOayWpuoM=
|
||||
github.com/eth-act/skunkworks-tama v0.0.0-20251105112532-eff8e3af014b/go.mod h1:M9fXNuyicUdFmj5nBlXRTNcUbJIDLMMs4eY1QoZHM3g=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/han0110/skunkworks-tama v0.0.0-20260218141853-cc6e788414fe h1:lv5TcqAYJUVs6e0iGzZy4/EWUMcimTNYTeO9MXQrL+A=
|
||||
github.com/han0110/skunkworks-tama v0.0.0-20260218141853-cc6e788414fe/go.mod h1:M9fXNuyicUdFmj5nBlXRTNcUbJIDLMMs4eY1QoZHM3g=
|
||||
github.com/usbarmory/tamago v0.0.0-20250710154000-3dd21eabac74 h1:zH22Y68S2cpwW278H+9v4r2SWpdP+JwUk/AwVc9LOlw=
|
||||
github.com/usbarmory/tamago v0.0.0-20250710154000-3dd21eabac74/go.mod h1:0Bc0GnC88LvCAoCRUcd3DBFl7cribfVbCsiMJUbXyAE=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
|
||||
Reference in New Issue
Block a user