| 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
- Install Risc Zero toolchain (rzup)
- Create guest program (code to prove)
- Create host program (runs zkVM)
- Generate receipt (proof)
- Submit to zkVerify
Prerequisites
curl -L https://risczero.com/install | bash
rzup
rzup --version 2.2.0
Core Concepts
| Risc0 Term | zkVerify Mapping |
|---|
| Receipt | Proof |
| Journal | Public Inputs |
| Image ID | Verification Key |
Project Setup
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.
use risc0_zkvm::guest::env;
use sha2::{Digest, Sha256};
fn main() {
let secret: String = env::read();
let mut hasher = Sha256::new();
hasher.update(secret.as_bytes());
let hash = format!("{:x}", hasher.finalize());
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.
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;
let mut bin_receipt = Vec::new();
ciborium::into_writer(&receipt, &mut bin_receipt).unwrap();
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
RISC0_DEV_MODE=0 cargo run --release -- "my secret input"
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 })
.execute({
proofData: {
proof: proof.proof,
vk: proof.image_id,
publicSignals: proof.pub_inputs,
},
domainId: 2
});
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" },
proofData: {
proof: proof.proof,
vk: proof.image_id,
publicSignals: proof.pub_inputs
},
chainId: 8453
});
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