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
- Risc0 installation: dev.risczero.com/api/zkvm/install
- 16 GB RAM minimum
- Rust toolchain
# 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
- Network config: network-config.md
- All proof types: proof-types.md
Resources
- Risc Zero Docs: dev.risczero.com
- zkVM API: dev.risczero.com/api/zkvm
- Examples: github.com/risc0/risc0-rust-examples
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 detailszkverify-kurier- REST API usagezkverify-attestation- Cross-chain verification flow