From hl-design-systems
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.
npx claudepluginhub horizenlabs/hl-claude-marketplace --plugin hl-design-systemsThis skill uses the workspace's default tool permissions.
Build zkVM applications with Risc Zero that prove correct program execution, then verify them on zkVerify.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Performs token-optimized structural code search using tree-sitter AST parsing to discover symbols, outline files, and unfold code without reading full files.
Build zkVM applications with Risc Zero that prove correct program execution, then verify them on zkVerify.
# Install Risc Zero
curl -L https://risczero.com/install | bash
rzup
# Target specific version (recommended)
rzup --version 2.2.0
| Risc0 Term | zkVerify Mapping |
|---|---|
| Receipt | Proof |
| Journal | Public Inputs |
| Image ID | Verification Key |
# Create new project
cargo risczero new my_app --guest-name my_guest
cd my_app
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"
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"
# 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
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;
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 | 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.
| 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") |
For Risc Zero, VK registration is NOT typically needed because:
vk_hash = vk)However, if your workflow requires it:
const { transactionResult } = await session
.registerVerificationKey()
.risc0({ version: Risc0Version.V2_2 })
.execute(imageId);
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.
zkverify-sdk - SDK integration detailszkverify-kurier - REST API usagezkverify-attestation - Cross-chain verification flow