risc0-submit

star 1

Build and verify Risc Zero zkVM proofs. Covers guest program development, receipt generation, image ID handling, and zkVerify submission. Supports versions v2.1, v2.2, v2.3, and v3.0. Use when building zkVM applications that prove correct program execution.

HorizenLabs By HorizenLabs schedule Updated 2/3/2026

name: risc0-submit description: Build and verify Risc Zero zkVM proofs. Covers guest program development, receipt generation, image ID handling, and zkVerify submission. Supports versions v2.1, v2.2, v2.3, and v3.0. Use when building zkVM applications that prove correct program execution.

Risc Zero Proof Builder

Build zkVM applications with Risc Zero that prove correct program execution, then verify them on zkVerify.

Quick Start Checklist

  1. Install Risc Zero toolchain (rzup)
  2. Create guest program (code to prove)
  3. Create host program (runs zkVM)
  4. Generate receipt (proof)
  5. Submit to zkVerify

Prerequisites

# Install Risc Zero
curl -L https://risczero.com/install | bash
rzup

# Target specific version (recommended)
rzup --version 2.2.0

Core Concepts

Risc0 Term zkVerify Mapping
Receipt Proof
Journal Public Inputs
Image ID Verification Key

Project Setup

# Create new project
cargo risczero new my_app --guest-name my_guest
cd my_app

Guest Program Example

The guest program runs inside the zkVM. Its execution is what gets proven.

// methods/guest/src/main.rs
use risc0_zkvm::guest::env;
use sha2::{Digest, Sha256};

fn main() {
    // Read private input
    let secret: String = env::read();

    // Perform computation
    let mut hasher = Sha256::new();
    hasher.update(secret.as_bytes());
    let hash = format!("{:x}", hasher.finalize());

    // Write public output (journal)
    env::commit(&hash);
}

Add dependency in methods/guest/Cargo.toml:

sha2 = "0.10"

Host Program with zkVerify Export

The host runs the zkVM and exports proof data for zkVerify.

// host/src/main.rs
use risc0_zkvm::{default_prover, ExecutorEnv};
use serde::Serialize;
use std::{fs::File, io::Write};

use my_app_methods::MY_GUEST_ID;

#[derive(Serialize)]
pub struct ZkVerifyProof {
    proof: String,
    image_id: String,
    pub_inputs: String,
}

fn main() {
    let input: String = std::env::args().nth(1).unwrap();

    let env = ExecutorEnv::builder()
        .write(&input).unwrap()
        .build().unwrap();

    let prover = default_prover();
    let receipt = prover.prove(env, MY_GUEST_ID).unwrap().receipt;

    // Serialize receipt for zkVerify
    let mut bin_receipt = Vec::new();
    ciborium::into_writer(&receipt, &mut bin_receipt).unwrap();

    // Convert image_id to bytes (little-endian)
    let image_id_hex = hex::encode(
        MY_GUEST_ID
            .iter()
            .flat_map(|v| v.to_le_bytes())
            .collect::<Vec<_>>(),
    );

    let proof = ZkVerifyProof {
        proof: format!("0x{}", hex::encode(&bin_receipt)),
        image_id: format!("0x{}", image_id_hex),
        pub_inputs: format!("0x{}", hex::encode(&receipt.journal.bytes)),
    };

    let json = serde_json::to_string_pretty(&proof).unwrap();
    File::create("proof.json").unwrap().write_all(json.as_bytes()).unwrap();
}

Add dependencies in host/Cargo.toml:

ciborium = "0.2.2"
hex = "0.4.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

Running the Application

# Generate proof (disable dev mode for real proofs)
RISC0_DEV_MODE=0 cargo run --release -- "my secret input"

# Output: proof.json with proof, image_id, pub_inputs

Verification on zkVerify

Using zkVerifyJS SDK

import { zkVerifySession, ZkVerifyEvents, Risc0Version } from "zkverifyjs";
import fs from "fs";

const proof = JSON.parse(fs.readFileSync("proof.json"));
const session = await zkVerifySession.start().zkVerify().withAccount(seed);

const { events, transactionResult } = await session.verify()
  .risc0({ version: Risc0Version.V2_2 })  // Match your toolchain version
  .execute({
    proofData: {
      proof: proof.proof,
      vk: proof.image_id,
      publicSignals: proof.pub_inputs,
    },
    domainId: 2  // Optional: for cross-chain
  });

events.on(ZkVerifyEvents.Finalized, (data) => {
  console.log("Verified:", data.txHash);
});

await transactionResult;

Using Kurier REST API

const response = await axios.post(`${API_URL}/submit-proof/${API_KEY}`, {
  proofType: "risc0",
  proofOptions: { version: "V2_2" },  // V2_1, V2_2, V2_3, or V3_0
  proofData: {
    proof: proof.proof,
    vk: proof.image_id,
    publicSignals: proof.pub_inputs
  },
  chainId: 8453  // Optional: for cross-chain
});

Version Configuration

Version SDK Enum Notes
2.1 Risc0Version.V2_1 Legacy
2.2 Risc0Version.V2_2 Recommended
2.3 Risc0Version.V2_3 Latest stable
3.0 Risc0Version.V3_0 Newest

Important: Match your rzup --version with the SDK version parameter.

Technical Specifications

Parameter Value
Max public inputs 2052 bytes (2048 user + 4 overhead)
VK format 32 bytes (image_id, big-endian)
Proof serialization CBOR via ciborium
Statement hash context keccak256(b"risc0")

VK Registration

For Risc Zero, VK registration is NOT typically needed because:

  • The VK hash function is identity (vk_hash = vk)
  • Image ID is already 32 bytes

However, if your workflow requires it:

const { transactionResult } = await session
  .registerVerificationKey()
  .risc0({ version: Risc0Version.V2_2 })
  .execute(imageId);

Configuration References

Resources

Common Issues

Proof too large: Risc0 proofs can be large. Ensure you're using the correct receipt format.

Version mismatch: Always match your toolchain version (rzup --version) with the SDK version parameter.

Memory issues: Proving requires significant RAM. Use --release flag and ensure 16GB+ available.

Related Skills

  • zkverify-sdk - SDK integration details
  • zkverify-kurier - REST API usage
  • zkverify-attestation - Cross-chain verification flow
Install via CLI
npx skills add https://github.com/HorizenLabs/hl-claude-marketplace --skill risc0-submit
Repository Details
star Stars 1
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator