Proof
Proof bundles a cryptographic proof body, the public values the
guest committed, and the program verification key that pins the proof
to a specific program. Use it to verify, inspect outputs, and persist
for later use.
Overview
A Proof is what a proving run produces. It carries everything an
independent process needs to verify the run — neither the guest ELF
nor the original inputs are required. ProveResult derefs to Proof,
so every method documented below is reachable directly on the result
returned by client.prove(...).
You usually do not construct a Proof yourself. The prover hands you
one through ProveResult, or you load a previously saved one with
Proof::load(...).
Types
Proof
The top-level proof container.
pub struct Proof {
pub body: ProofBody,
pub publics: PublicValues,
pub program_vk: ProgramVK,
}
| Field | Type | Description |
|---|---|---|
body | ProofBody | Kind-tagged proof payload. |
publics | PublicValues | Public values committed by the guest, with cursor access. |
program_vk | ProgramVK | Program verification key that pins the proof to a program. |
ProofKind
Identifies the proof format. Derived from ProofBody via
Proof::kind(), not stored on the proof.
pub enum ProofKind {
#[default]
VadcopFinal,
VadcopFinalMinimal,
Plonk,
}
| Variant | Description |
|---|---|
VadcopFinal | Default STARK output. Largest proof, fastest to generate. |
VadcopFinalMinimal | Smaller STARK obtained by wrapping VadcopFinal. Off-chain friendly. |
Plonk | SNARK-wrapped proof, constant size, EVM-verifiable on-chain. |
ProofBody
The kind-tagged proof payload stored in Proof::body.
pub enum ProofBody {
Vadcop {
proof: Vec<u64>,
zisk_vk: Vec<u64>,
minimal: bool,
hash: String,
},
Plonk {
proof_bytes: Vec<u8>,
plonk_vk: Box<PlonkVkBlob>,
},
}
| Variant | Carries | Produced by |
|---|---|---|
Vadcop | u64 proof words + ZisK vk + hash | VadcopFinal / VadcopFinalMinimal |
Plonk | SNARK proof bytes + PLONK vk blob | Plonk |
PublicValues
Holds the public outputs the guest committed. Internally a byte
buffer plus an atomic read cursor: each read advances the cursor
by the number of bytes consumed. Reads take &self because the
cursor is atomic.
pub struct PublicValues {
/* private */
}
The encoding follows serde rules: read::<T>() uses bincode for
typed reads; read_slice operates on raw bytes; read_abi::<T>()
decodes Solidity ABI–encoded values.
ProgramVK
The program verification key. Re-exported from zisk_common. It is
derived from the guest ELF (see GuestProgram::vk)
and is what an independent verifier pins a proof to.
pub struct ProgramVK { /* private */ }
Constructors
Proof::load
Load a previously saved proof from disk.
pub fn load(path: impl AsRef<Path>) -> Result<Proof>
| Name | Type | Description |
|---|---|---|
path | impl AsRef<Path> | Path to the proof file on disk. |
The file must have been written by Proof::save (or by tooling
using the same encoding).
use zisk_sdk::Proof;
let proof = Proof::load("my-path/proof.bin")?;
proof.verify()?;
Methods
save
Write the proof to disk. The parent directory must already exist; existing files are overwritten.
pub fn save(&self, path: impl AsRef<Path>) -> Result<()>
| Name | Type | Description |
|---|---|---|
path | impl AsRef<Path> | Destination path for the proof file. |
proof.save("my-path/proof.bin")?;
kind
Return the proof's ProofKind.
pub fn kind(&self) -> ProofKind
match proof.kind() {
ProofKind::VadcopFinal => println!("Vadcop final proof"),
ProofKind::VadcopFinalMinimal => println!("Vadcop minimal proof"),
ProofKind::Plonk => println!("Plonk SNARK proof"),
}
is_empty
Return true if the proof carries no proof body (a default /
placeholder proof).
pub fn is_empty(&self) -> bool
get_publics
Return the PublicValues buffer carried by the proof. Use it to read
the guest's committed outputs.
pub fn get_publics(&self) -> &PublicValues
let publics = proof.get_publics();
let nonce: u64 = publics.read::<u64>()?;
let digest: [u8; 32] = {
let mut out = [0u8; 32];
publics.read_slice(&mut out);
out
};
get_program_vk
Return the program verification key that pins this proof to its program. Use it to compare against a trusted key held out-of-band.
pub fn get_program_vk(&self) -> &ProgramVK
let vk = proof.get_program_vk();
assert_eq!(vk, &trusted_vk);
get_proof_bytes / get_proof_u64
Return the serialized proof payload, either as raw bytes or as u64
words. The encoding varies by ProofKind. Use these to inspect or
forward the proof body; for the typed structure use the public
body field directly.
pub fn get_proof_bytes(&self) -> Result<Vec<u8>>
pub fn get_proof_u64(&self) -> Result<Vec<u64>>
let bytes = proof.get_proof_bytes()?;
println!("proof bytes: {}", bytes.len());
match &proof.body {
ProofBody::Vadcop { proof, .. } => println!("vadcop words: {}", proof.len()),
ProofBody::Plonk { proof_bytes, .. } => println!("plonk bytes: {}", proof_bytes.len()),
}
get_vadcop_final_proof
Return the VADCOP final proof structure. Errors if the proof is a PLONK proof rather than a VADCOP one.
pub fn get_vadcop_final_proof(&self) -> Result<VadcopFinalProof>
verify
Verify the proof against the public values and program verification key already stored on it.
pub fn verify(&self) -> Result<()>
proof.verify()?;
println!("proof verified");
with_publics / with_program_vk
Begin a verify chain that overrides the embedded public values or
program verification key. Both return a ZiskVerifyBuilder you can
keep chaining before finishing with .verify().
pub fn with_publics<'a>(&'a self, publics: &'a PublicValues) -> ZiskVerifyBuilder<'a>
pub fn with_program_vk<'a>(&'a self, program_vk: &'a ProgramVK) -> ZiskVerifyBuilder<'a>
// Pin verification to an externally-held program VK
let vk = PROGRAM.vk()?;
proof.with_program_vk(&vk).verify()?;
PublicValues methods
The cursor is shared via an AtomicUsize, so reads take &self
even though they advance the cursor. Call head to rewind
before re-reading.
read
Decode the next bincode-encoded value from the buffer and advance the cursor by the bytes consumed.
pub fn read<T>(&self) -> Result<T>
where
T: serde::Serialize + serde::de::DeserializeOwned,
let count: u64 = publics.read::<u64>()?;
let digest: [u8; 32] = publics.read::<[u8; 32]>()?;
read_slice
Copy the next slice.len() bytes from the buffer into slice. The
cursor advances by slice.len().
pub fn read_slice(&self, slice: &mut [u8])
| Name | Type | Description |
|---|---|---|
slice | &mut [u8] | Destination buffer; its length is the count. |
let mut tag = [0u8; 32];
publics.read_slice(&mut tag);
read_abi
Decode the next value using Solidity ABI rules instead of bincode.
Use this when the guest committed its publics with write_abi for
on-chain consumption.
pub fn read_abi<T>(&self) -> Result<T>
where
T: alloy_sol_types::SolValue
+ From<<T::SolType as alloy_sol_types::SolType>::RustType>,
head
Reset the read cursor back to the beginning of the buffer so the next read starts from the first value again.
pub fn head(&self)
let a: u64 = publics.read()?;
publics.head(); // rewind
let a_again: u64 = publics.read()?;