Prover

The zkm_sdk crate provides all the necessary tools for proof generation. Key features include the ProverClient, enabling you to:

  • Initialize proving/verifying keys via setup().
  • Execute your program via execute().
  • Generate proofs with prove().
  • Verify proofs through verify().

When generating Groth16 or PLONK proofs, the ProverClient automatically downloads the pre-generated proving key (pk) from a trusted setup by calling try_install_circuit_artifacts().

Example: Fibonacci

The following code is an example of using zkm_sdk in host.

use zkm_sdk::{include_elf, utils, ProverClient, ZKMProofWithPublicValues, ZKMStdin};

/// The ELF we want to execute inside the zkVM.
const ELF: &[u8] = include_elf!("fibonacci");

fn main() {
    // Create an input stream and write '1000' to it.
    let n = 1000u32;

    // The input stream that the guest will read from using `zkm_zkvm::io::read`. Note that the
    // types of the elements in the input stream must match the types being read in the program.
    let mut stdin = ZKMStdin::new();
    stdin.write(&n);

    // Create a `ProverClient` instance.
    let client = ProverClient::new();

    // Execute the guest using the `ProverClient.execute` method, without generating a proof.
    let (_, report) = client.execute(ELF, stdin.clone()).run().unwrap();
    println!("executed program with {} cycles", report.total_instruction_count());

    // Generate the proof for the given program and input.
    let (pk, vk) = client.setup(ELF);
    let mut proof = client.prove(&pk, stdin).run().unwrap();

    // Read and verify the output.
    //
    // Note that this output is read from values committed to in the program using
    // `zkm_zkvm::io::commit`.
    let n = proof.public_values.read::<u32>();
    let a = proof.public_values.read::<u32>();
    let b = proof.public_values.read::<u32>();

    println!("n: {}", n);
    println!("a: {}", a);
    println!("b: {}", b);

    // Verify proof and public values
    client.verify(&proof, &vk).expect("verification failed");
}

Proof Types

Ziren provides customizable proof generation options:

#![allow(unused)]
fn main() {
/// A proof generated with Ziren of a particular proof mode.
#[derive(Debug, Clone, Serialize, Deserialize, EnumDiscriminants, EnumTryAs)]
#[strum_discriminants(derive(Default, Hash, PartialOrd, Ord))]
#[strum_discriminants(name(ZKMProofKind))]
pub enum ZKMProof {
    /// A proof generated by the core proof mode.
    ///
    /// The proof size scales linearly with the number of cycles.
    #[strum_discriminants(default)]
    Core(Vec<ShardProof<CoreSC>>),
    /// A proof generated by the compress proof mode.
    ///
    /// The proof size is constant, regardless of the number of cycles.
    Compressed(Box<ZKMReduceProof<InnerSC>>),
    /// A proof generated by the Plonk proof mode.
    Plonk(PlonkBn254Proof),
    /// A proof generated by the Groth16 proof mode.
    Groth16(Groth16Bn254Proof),
}
}

Core Proof (Default)

The default prover mode generates a sequence of STARK proofs whose cumulative proof size scales linearly with the execution trace length.

#![allow(unused)]
fn main() {
let client = ProverClient::new();
client.prove(&pk, stdin).run().unwrap();
}

Compressed Proof

The compressed proving mode generates constant-sized STARK proofs, but not suitable for on-chain verification.

#![allow(unused)]
fn main() {
let client = ProverClient::new();
client.prove(&pk, stdin).compressed().run().unwrap();
}

The Groth16 proving mode ​generates succinct SNARK proofs with a compact size of approximately 260 bytes, ​and features on-chain verification.

#![allow(unused)]
fn main() {
let client = ProverClient::new();
client.prove(&pk, stdin).groth16().run().unwrap();
}

PLONK Proof

The PLONK proving mode generates succinct SNARK proofs with a compact size of approximately 868 bytes, while maintaining on-chain verifiability. In contrast to Groth16, PLONK removes the dependency on trusted setup ceremonies.

#![allow(unused)]
fn main() {
let client = ProverClient::new();
client.prove(&pk, stdin).plonk().run().unwrap();
}

Hardware Acceleration

Ziren provides hardware acceleration support for AVX256/AVX512 on x86 CPUs due to support in Plonky3.

You can check your CPU's AVX compatibility by running:

grep avx /proc/cpuinfo

Check if you can see avx2 or avx512 in the results.

To activate AVX256 optimization, add these flags to your RUSTFLAGS environment variable:

RUSTFLAGS="-C target-cpu=native" cargo run --release

To activate AVX512 optimization, add these flags to your RUSTFLAGS environment variable:

RUSTFLAGS="-C target-cpu=native -C target-feature=+avx512f" cargo run --release

Network Prover

We support a network prover via the ZKM Proof Network, accessible through our RESTful API.The network prover currently supports only the Groth16 proving mode. >The proving process consists of several stages: queuing, splitting, proving, and finalizing. Each stage may take a different amount of time.

Requirements

  • CA certificate: ca.pem, ca.key. These keys are stored here
  • Register your address to gain access.
  • SDK dependency: add zkm_sdk from the Ziren SDK to your Cargo.toml:
zkm-sdk = { git = "https://github.com/ProjectZKM/Ziren", branch = "main" }

Environment Variable Setup

Before running your application, export the following environment variables to enable the network prover:

export ZKM_PRIVATE_KEY=<your_private_key>       # Private key corresponding to your registered public key
export SSL_CERT_PATH=<path_to_ssl_certificate>  # Path to the SSL client certificate (e.g., ssl.pem)
export SSL_KEY_PATH=<path_to_ssl_key>           # Path to the SSL client private key (e.g., ssl.key)

You can generate the SSL certificate and key by running the certgen.sh script.

Optional: You can also set the following environment variables to customize the network prover behavior:

export SHARD_SIZE=<shard_size>            # Size of each shard in bytes. 
export MAX_PROVER_NUM=<max_prover_num>      # Maximum number of provers to use in parallel.

To host your own network prover, export the following variables to configure your endpoint:

export ENDPOINT=<proof_network_endpoint>        # Proof network endpoint (default: https://152.32.186.45:20002)
export CA_CERT_PATH=<path_to_ca_certificate>    # Path to CA certificate (default: ca.pem)
export DOMAIN_NAME=<domain_name>                # Domain name (default: "stage")

Example

The following example shows how to use the network prover on the host:

use std::env;
use zkm_sdk::{include_elf, ProverClient, ZKMStdin};
const FIBONACCI_ELF: &[u8] = include_elf!("fibonacci");
fn main() {
    utils::setup_logger();

    let mut stdin = ZKMStdin::new();
    stdin.write(&10usize);
    let elf = test_artifacts::FIBONACCI_ELF;
    
    // create network client
    let client = ProverClient::network();
    let (pk, vk) = client.setup(elf);
    let proof = client.prove(&pk, stdin).run().unwrap();
    client.verify(&proof, &vk).unwrap();
}