Skip to main content

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,
}
FieldTypeDescription
bodyProofBodyKind-tagged proof payload.
publicsPublicValuesPublic values committed by the guest, with cursor access.
program_vkProgramVKProgram 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,
}
VariantDescription
VadcopFinalDefault STARK output. Largest proof, fastest to generate.
VadcopFinalMinimalSmaller STARK obtained by wrapping VadcopFinal. Off-chain friendly.
PlonkSNARK-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>,
},
}
VariantCarriesProduced by
Vadcopu64 proof words + ZisK vk + hashVadcopFinal / VadcopFinalMinimal
PlonkSNARK proof bytes + PLONK vk blobPlonk

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>
NameTypeDescription
pathimpl 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<()>
NameTypeDescription
pathimpl 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])
NameTypeDescription
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>,

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()?;