
Risc0
- 2 installs
- 4 repo stars
- Updated February 25, 2026
- hairyf/blockchain-skills
Reference RISC Zero zkVM - guest/host code, receipts, proving options, and on-chain Ethereum verification of verifiable computation.
About
A reference skill for the RISC Zero zkVM, a zk-STARK RISC-V platform that runs arbitrary code and produces verifiable receipts. A developer uses it when building zkVM guest/host programs, coprocessors, or on-chain proof verification.
- Guest/host code with journal+seal receipts
- On-chain Ethereum verification via Groth16 contracts
Risc0 by the numbers
- 2 all-time installs (skills.sh)
- Ranked #408 of 479 Web3 & Blockchain skills by installs in the Skillselion catalog
- Data as of Jul 13, 2026 (Skillselion catalog sync)
npx skills add https://github.com/hairyf/blockchain-skills --skill risc0Add your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| repo stars | ★ 4 |
| Last updated | February 25, 2026 |
| Repository | hairyf/blockchain-skills ↗ |
What it does
Reference RISC Zero zkVM - guest/host code, receipts, proving options, and on-chain Ethereum verification of verifiable computation.
Files
Skill is based on RISC Zero (risc0/risc0), generated at the listed date.
RISC Zero is a zero-knowledge verifiable computing platform based on zk-STARKs and RISC-V. The zkVM runs arbitrary code (Rust, C, C++) and produces receipts (journal + seal) that anyone can verify with the program’s image ID, without re-running the program or seeing private inputs. Use it for coprocessors, attestation, and on-chain verification (e.g. Ethereum via Groth16 verifier contracts).
Core References
| Topic | Description | Reference |
|---|---|---|
| zkVM overview | Guest, host, method, image ID, journal, receipt, seal | core-zkvm-overview |
| Guest code | Entry macro, env read/write/commit, no_std | core-guest-code |
| Host code | ExecutorEnv, prove, verify, journal decode | core-host-code |
| Receipts | Structure, verify, journal, serialization, receipt kinds | core-receipts |
Features
| Topic | Description | Reference |
|---|---|---|
| Proving options | Dev-mode, local, remote (Boundless), prove_with_opts | features-proving-options |
| Precompiles | Crypto precompiles, patched crates (sha2, k256, etc.) | features-precompiles |
| Proof composition | Verify receipts in guest, assumptions, resolve | features-composition |
| Recursion | Segment → lift → join → Groth16, receipt kinds | features-recursion |
| Ethereum integration | Verifier contracts, Groth16, shrink-wrapping | features-blockchain-ethereum |
Best practices
| Topic | Description | Reference |
|---|---|---|
| Guest optimization | Cycles, paging, precompiles, profiling, alignment | best-practices-guest-optimization |
Advanced
| Topic | Description | Reference |
|---|---|---|
| Security model | Components, soundness, ZK caveats, audits | advanced-security-model |
Generation Info
- Source:
sources/risc0 - Doc path:
website/api_versioned_docs/version-3.0/,website/docs/, README - Git SHA:
e41f129cd96f8def78f026fbe3d1683a05ba5993 - Generated: 2026-02-24
Security Model
RISC Zero’s security model covers the toolchain, RISC-V prover, recursion prover, STARK-to-SNARK prover, and on-chain verifier contracts. Each component has been audited; see the rz-security repo for reports and dates.
Components
- cargo risczero — Builds guest code to RISC-V ELF deterministically.
- RISC-V Prover — Executes and proves ELF (STARK).
- Recursion Prover — Aggregates proofs (lift, join, resolve); identified by control ID / control root.
- STARK-to-SNARK Prover — Compresses to Groth16; control root as public input allows prover updates without a new trusted setup.
- Verifier contracts — Verify Groth16 on-chain; control root is fixed in the contract (see version-management for upgrades and deprecation).
Security of guest programs and smart contracts is the integrator’s responsibility (secure development, audits).
Soundness and zero-knowledge
- Soundness — A valid receipt implies correct execution of the claimed program (image ID) producing the given journal. On-chain verifiers target ~96 bits security; recursion and STARK-to-SNARK have their own assumptions (see docs).
- Zero-knowledge — Default design aims for perfect ZK (hides inputs and witness). There is no published full ZK proof yet; treat as a caveat for strict privacy requirements.
- Who can see secrets — The prover sees all inputs. For private data, use local proving. Proofs from the RISC-V prover that are not passed through the recursion prover can leak execution length; use Succinct/Groth16 (recursion pipeline) to avoid that.
Groth16 and BN254
The STARK-to-SNARK step uses Groth16 over BN254. Security relies on the elliptic curve and the Groth16 trusted setup. Not post-quantum safe; STARK provers are quantum-safe.
When implementing or auditing, use the official security calculator and ethSTARK/BN254 references linked in the security model docs.
<!-- Source references:
- sources/risc0/website/api_versioned_docs/version-3.0/security-model.md
-->
Guest Optimization
Proving cost and time correlate with guest execution cycles. Optimize the guest like a normal program (measure first), but account for zkVM-specific costs: paging, no floating point, alignment, and precompiles.
Measure first
- Use
env::cycle_count()in the guest to measure hot paths; print witheprintln!or use a profiler. - Profiling: Set
RISC0_PPROF_OUT=./out.pb, run in dev-mode (RISC0_DEV_MODE=1), then e.g.go tool pprof -http 127.0.0.1:8000 out.pband inspect flamegraphs. Run in dev-mode to avoid proving overhead.
zkVM-specific costs
- Paging — Memory is split into ~1 kB pages. First access to a page in a segment triggers page-in (Merkle verification); first write triggers page-out at segment end. Each costs on the order of ~1k–5k cycles. Improve locality and reduce working set to cut paging.
- Floating point — Not native; emulated in software (tens to hundreds of cycles per op). Prefer integers where possible.
- Alignment — Unaligned
u32access is much costlier than aligned. Keep structs and slice indices word-aligned where it matters. - RISC-V cycles — Most ops 1–2 cycles; div/rem 2. Relative cost of div vs add is smaller than on a real CPU; prefer clearer code and measure.
Use precompiles
- Use patched crypto crates (sha2, k256, etc.) so SHA-256 and elliptic-curve ops use precompiles and far fewer cycles. See features-precompiles.
- Prefer BTreeMap over HashMap in the guest (determinism and often better for small collections).
I/O and serialization
- For raw bytes from host, use
env::read_sliceorstdin().read_to_end()instead ofenv::readwhen you don’t need deserialization to avoid extra copy/reinterpret. - On host, use
ExecutorEnvBuilder::write_slicefor raw bytes. - For large inputs where only part is needed, consider Merklized input (e.g. provide root + chunks with proofs) so the guest only touches needed chunks; see “Where’s Waldo” style examples.
Prover acceleration
- CUDA (NVIDIA): build with
cudafeature and install CUDA toolkit for faster proving. - Metal (Apple): used automatically on Apple Silicon.
- Segment size: if memory is tight (<10 GB), consider reducing segment size (e.g.
segment_limit_po2) to lower peak RAM.
Quick wins
- Profile; optimize the hottest code.
- Use precompiled crypto (patched crates).
- Prefer integers over float; keep data aligned; reduce memory footprint and improve locality to reduce paging.
- Try compiler options:
lto = "thin",opt-level = 2or3,codegen-units = 1(measure each).
<!-- Source references:
- sources/risc0/website/api_versioned_docs/version-3.0/zkvm/optimization.md
- sources/risc0/website/api_versioned_docs/version-3.0/zkvm/profiling.md
-->
Guest Code
The guest is the code executed and proven inside the zkVM. It runs in a RISC-V (riscv32im) environment with a restricted runtime.
Boilerplate
#![no_std]
#![no_main]
risc0_zkvm_guest::entry!(main);
fn main() {
// read inputs, compute, commit outputs
}#![no_std]— No standard library for size/performance.#![no_main]— Guest is not a standalone binary.risc0_zkvm_guest::entry!(main)— Declares the guest entry point for the host.
Reading inputs
env::read<T>()— Deserialize one value from host (host usesExecutorEnv::builder().write(&t)).env::read_slice(bytes)— Read raw bytes (no deserialization).env::stdin()— Byte-oriented input (e.g.stdin().read_to_end(&mut vec)).
Writing to host (private)
env::write(&t),env::write_slice,env::stdout(),env::stderr()— Send data to host only; not in the receipt.
Committing to journal (public)
env::commit(&t)— Commit a value to the journal (public output).env::commit_slice(slice)— Commit raw bytes.
Only journal data is part of the receipt and visible to verifiers.
Debugging and measurement
env::cycle_count()— Current execution cycles (for optimization).env::log(msg)— Debug logging.
Use the risc0_zkvm::guest::env module; see docs.rs/risc0-zkvm for the full guest API.
<!-- Source references:
- sources/risc0/website/api_versioned_docs/version-3.0/zkvm/guest-code-101.md
-->
Host Code
The host builds the execution environment, runs the prover, and obtains the receipt. It does not see private guest state except what the guest sends via env::write/stdout/stderr.
Minimal host: prove and verify
use risc0_zkvm::{default_prover, ExecutorEnv};
use risc0_zkvm_methods::MY_METHOD_ELF;
use risc0_zkvm_methods::MY_METHOD_ID;
// Build env (optional: .write(&input), .write_slice(...), etc.)
let env = ExecutorEnv::builder().build().unwrap();
let prover = default_prover();
let receipt = prover.prove(env, MY_METHOD_ELF).unwrap().receipt;
// Verify (e.g. before sending to a third party)
receipt.verify(MY_METHOD_ID).unwrap();
// Use public output
let output: MyOutput = receipt.journal.decode().unwrap();MY_METHOD_ELFandMY_METHOD_IDcome from your methods crate (built withrisc0-build).ExecutorEnv::builder()— Add inputs with.write(&t)/.write_slice(&bytes), then.build().prover.prove(env, ELF)— Runs guest and produces a receipt (or errors).receipt.verify(image_id)— Cryptographically verifies the receipt for that method.receipt.journal— Decode withreceipt.journal.decode::<T>()for the committed type.
Passing input to the guest
let input = MyInput { ... };
let env = ExecutorEnv::builder()
.write(&input).unwrap()
.build().unwrap();Guest reads with env::read::<MyInput>(). Use write_slice for raw bytes; guest uses env::read_slice or env::stdin().read_to_end().
Serializing receipts
Use serde (e.g. bincode::serialize(&receipt)) to send receipts off-process or on-chain. Verifier needs the same image ID and the receipt bytes.
<!-- Source references:
- sources/risc0/website/api_versioned_docs/version-3.0/zkvm/host-code-101.md
- sources/risc0/website/api_versioned_docs/version-3.0/zkvm/receipts.md
-->
Receipts
A receipt is the output of a successful zkVM run: it bundles the journal (public outputs) with a seal (cryptographic proof). Verifiers only need the receipt and the expected image ID to be convinced the journal was produced by correct execution of that method.
Structure
- Journal — Public outputs committed by the guest via
env::commit/env::commit_slice. Verifier can read it. - Seal — Opaque proof blob; tampering or wrong execution makes verification fail.
Verifying
receipt.verify(expected_image_id).unwrap();Verification ensures (1) the execution was valid, and (2) the executed program matches expected_image_id. Use the image ID from your methods crate (MY_METHOD_ID).
Extracting the journal
let value: T = receipt.journal.decode().unwrap();
// or raw bytes: receipt.journal.bytesSerialization
Receipts implement serde. Example:
let bytes = bincode::serialize(&receipt).unwrap();
// send bytes; receiver:
let receipt: Receipt = bincode::deserialize(&bytes).unwrap();
receipt.verify(image_id).unwrap();Receipt kinds
- Composite — Default; larger, full segment receipts.
- Succinct — From
prove_with_opts(..., ReceiptKind::Succinct); compressed via recursion. - Groth16 — From
prove_with_opts(..., ReceiptKind::Groth16); for on-chain verification (small, constant size). Requiresrzup install risc0-groth16.
Use prove_with_opts when you need Succinct or Groth16 (e.g. for composition or Ethereum verifier contracts).
<!-- Source references:
- sources/risc0/website/api_versioned_docs/version-3.0/zkvm/receipts.md
- sources/risc0/website/api_versioned_docs/version-3.0/generating-proofs/proving-options.md
- sources/risc0/website/api_versioned_docs/version-3.0/blockchain-integration/shrink-wrapping.md
-->
zkVM Overview
RISC Zero is a zero-knowledge verifiable computing platform based on zk-STARKs and the RISC-V microarchitecture. The zkVM emulates a small RISC-V machine so arbitrary code (Rust, C, C++) can run and be proven without revealing inputs or execution state.
Core terminology
- Method — Code to be proven, compiled to a RISC-V ELF with a special entry point. Built with
risc0-build/cargo risczero build. - Image ID — Cryptographic hash of the method ELF; required for verification. Use the same image ID as the verifier (e.g. from your methods crate).
- Guest — The logical RISC-V machine running inside the zkVM; the code that is executed and proven.
- Host — The prover process that runs the zkVM, provides inputs, and obtains the receipt. Cannot modify guest execution or the proof is invalid.
- Journal — Append-only log written by the guest; the public output of the computation. Part of the receipt.
- Receipt — Proof of correct execution: journal + seal (opaque cryptographic blob). Verifiers check the seal and can read the journal.
- Seal — The cryptographic part of the receipt attesting validity.
Flow
1. Compile guest code into a method (ELF); compute image ID. 2. Host builds ExecutorEnv, runs the prover with the method ELF, gets a receipt. 3. Receipt contains journal (public outputs) and seal. 4. Any party with the same image ID can verify the receipt and read the journal; they learn nothing else about inputs or execution.
Key point
Verification only needs the receipt and the expected image ID. The verifier does not re-run the program and does not see private inputs or host–guest traffic (except what the guest committed to the journal).
<!-- Source references:
- https://github.com/risc0/risc0 (README)
- sources/risc0/website/api_versioned_docs/version-3.0/zkvm/zkvm-overview.md
- sources/risc0/website/api_versioned_docs/version-3.0/introduction.md
-->
RISC Zero on Ethereum
Prove computation in the zkVM and verify on Ethereum (or other EVM chains) using RISC Zero’s verifier contracts. On-chain verification expects a Groth16 receipt (shrink-wrapped); use prove_with_opts(..., ReceiptKind::Groth16) and install the Groth16 prover component (rzup install risc0-groth16).
Verifier usage
Contracts call an IRiscZeroVerifier-compatible contract. Typical flow:
1. Construct the expected journal bytes (e.g. abi.encode(x) for a single value). 2. Call verifier.verify(seal, image_id, sha256(journal)) (or the exact interface of the deployed verifier). Reverts if the seal is invalid or journal doesn’t match.
Example (Solidity-style):
bytes memory journal = abi.encode(x);
verifier.verify(seal, IS_EVEN_ID, sha256(journal));The image ID must match the method that produced the receipt. The seal is the Groth16 proof (and any other inputs required by the contract). The contract hashes the journal and checks it against the claim in the proof.
Verifier router
RISC Zero deploys a RiscZeroVerifierRouter that routes to the correct base verifier per zkVM version. Prefer the router so your app supports multiple receipt types and future versions. Contract addresses and supported chains (Ethereum mainnet, Sepolia, Arbitrum, Base, etc.) are in the verifier contract docs; use the repo’s version-management design for upgrades and emergency stop.
Shrink-wrapping (Groth16)
Default proofs are composite (STARK). For on-chain verification you need a Groth16 receipt:
- Use
ProverOpts { receipt_kind: ReceiptKind::Groth16, ... }withprove_with_opts. - Install Groth16 component:
rzup install risc0-groth16(rzup >= 0.5.0).
The Groth16 receipt is small and verified by the on-chain verifier contract.
Integration examples
- risc0-ethereum — Verifier contracts, Steel (view-call proofs), and blockchain examples.
- Foundry template — Minimal app with Boundless remote proving and verifier call.
- Governance example — Batched signature verification for DAO votes (e.g. OpenZeppelin Governor), large gas savings.
- Zeth — zkEVM (EVM block proofs) using revm in the zkVM.
Use the zkVM as a coprocessor: heavy or awkward logic (e.g. ed25519, custom parsers) runs in the guest; the contract only verifies the receipt and reads the journal.
<!-- Source references:
- sources/risc0/website/api_versioned_docs/version-3.0/blockchain-integration/risc-zero-on-eth.md
- sources/risc0/website/api_versioned_docs/version-3.0/blockchain-integration/contracts/verifier.md
- sources/risc0/website/api_versioned_docs/version-3.0/blockchain-integration/shrink-wrapping.md
-->
Proof Composition
Proof composition lets one zkVM guest verify another program’s receipt and fold that into a single receipt. The guest does not run the full verifier (which would not compress); instead it adds assumptions to the receipt claim, and the prover resolves them when producing a Succinct or Groth16 receipt.
Flow
1. Host: Before proving, call env.add_assumption(receipt) (or equivalent) to register a receipt the guest will verify. 2. Guest: Call env::verify(&receipt) (or the appropriate API). This adds an assumption to the current receipt claim (a “conditional receipt”). 3. Prover: When you call prove_with_opts with ReceiptKind::Succinct or ReceiptKind::Groth16, the prover resolves assumptions (by checking the assumed receipts and folding them into the final proof).
If assumptions are not resolved (e.g. you only produce a composite receipt), the receipt remains conditional and may not be acceptable to verifiers that expect a resolved claim.
When to use
- Build modular apps: one method verifies receipts of other methods and commits to a combined claim.
- Aggregate or chain proofs without the verifier re-running all sub-proofs.
Use the composition example in the RISC Zero repo for a full host/guest pattern (add_assumption on the host, env::verify in the guest, then prove_with_opts with Succinct or Groth16).
Key APIs
- Host:
ExecutorEnvBuilder::add_assumption(or prover API that adds assumptions). - Guest:
risc0_zkvm::guest::env::verify(or equivalent) to verify an incoming receipt and add its claim as an assumption. - Prover:
Prover::prove_with_opts(..., ReceiptKind::Succinct)orReceiptKind::Groth16so resolution runs.
<!-- Source references:
- sources/risc0/website/api_versioned_docs/version-3.0/zkvm/composition.md
- sources/risc0/website/api_versioned_docs/version-2.3/recursion.md (resolve program)
-->
Precompiles
The zkVM implements precompiles for crypto (e.g. SHA-256, 256-bit modular mul, elliptic curve, RSA). Using them reduces cycles and proving cost compared to pure-software implementations. Use patched crates from RISC Zero’s forks so your guest automatically uses these precompiles.
Patched crates (guest Cargo.toml)
Pin the exact version and patch from the fork. Example for SHA-2:
[dependencies]
sha2 = "=0.10.8"
[patch.crates-io]
sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.8-risczero.0" }Check each fork’s releases/tags for the correct tag (e.g. sha2-v0.10.8-risczero.0, k256/v0.13.3-risczero.1). If the patch is applied indirectly, you may need:
cargo update -p sha2 --precise 0.10.8Confirm in Cargo.lock that the crate references the RISC Zero fork. Commit Cargo.lock for the guest.
Commonly used patched crates
- sha2 — SHA-256 (RustCrypto-hashes).
- tiny-keccak — Keccak (may require unstable feature).
- k256 / p256 — secp256k1 / P-256 (RustCrypto-elliptic-curves).
- curve25519-dalek — Curve25519 (e.g. for ed25519-dalek).
- rsa — RSA (RustCrypto-RSA).
- bls12_381, blst, c-kzg — BLS / KZG (see precompiles doc for blst/c-kzg dependency).
- crypto-bigint — 256-bit modular ops.
Using these in the guest (without changing algorithm code) gives precompile acceleration. For ECDSA verification, see the official ECDSA example and its guest Cargo.toml for a full patched set (e.g. sha2, crypto-bigint, k256).
Unstable precompiles
Some optimizations require the unstable feature on risc0-zkvm (guest) and risc0-build (build). Check the precompiles table for which crates need it. Production users can avoid unstable until those precompiles are stabilized.
Timing and private data
Precompiles do not guarantee constant-time execution or constant proving time. Avoid using them with secrets (e.g. signing with a private key in the guest) if an attacker can observe cycle counts or proving time.
Debugging precompile usage
Run with RISC0_INFO=1 to see precompile-usage statistics for your guest.
<!-- Source references:
- sources/risc0/website/api_versioned_docs/version-3.0/zkvm/precompiles.md
-->
Proving Options
RISC Zero supports three ways to run the zkVM: dev-mode (no proof), local proving (your CPU/GPU), and remote proving (Boundless). Use prove_with_opts when you need Succinct or Groth16 receipts.
1. Dev-mode (rapid iteration)
No proof is generated; execution only. Receipts are “fake” and only verify when the verifier also runs with dev-mode.
- Set env:
RISC0_DEV_MODE=1(ortrue) when running. - Receipts still contain the journal; verification is pass-through in dev-mode.
- For production, build with the
disable-dev-modefeature so dev-mode cannot be enabled; ifRISC0_DEV_MODEis set with that feature, the prover panics.
RISC0_DEV_MODE=1 cargo run --release2. Local proving
- Use
default_prover()or a custom prover; proofs run on your machine. - Use when you have private inputs so data never leaves the host.
- Hardware: CPU (any modern x86/ARM), NVIDIA GPU (CUDA), or Apple Metal. Groth16 prover is x86-only (see project issues for Apple Silicon).
- Constrained memory: consider adjusting segment size (e.g.
ExecutorEnvBuilder::segment_limit_po2). See benchmarks for memory expectations.
3. Remote proving (Boundless)
- Send proof requests to Boundless; permissionless provers generate proofs. Good when you don’t need to keep inputs private and want to avoid local hardware.
- Integrate via Boundless docs (quick start, “request a proof” flow).
Receipt kind (prove_with_opts)
Prover::prove(env, elf)— Produces a composite receipt (default).Prover::prove_with_opts(env, elf, opts)— SetProverOpts::receipt_kind:ReceiptKind::Composite— Default.ReceiptKind::Succinct— For proof composition and smaller proofs.ReceiptKind::Groth16— For on-chain verification; requiresrzup install risc0-groth16. See shrink-wrapping docs.
Use Succinct or Groth16 when composing proofs or verifying on Ethereum (or other chains with a RISC Zero verifier contract).
<!-- Source references:
- sources/risc0/website/api_versioned_docs/version-3.0/generating-proofs/proving-options.md
- sources/risc0/website/api_versioned_docs/version-3.0/generating-proofs/dev-mode.md
- sources/risc0/website/api_versioned_docs/version-3.0/generating-proofs/local-proving.md
- sources/risc0/website/api_versioned_docs/version-3.0/generating-proofs/remote-proving.md
- sources/risc0/website/api_versioned_docs/version-3.0/blockchain-integration/shrink-wrapping.md
-->
Recursive Proving
RISC Zero uses recursive proving to support unbounded computation, constant proof size, aggregation, and composition. The prover pipeline turns execution segments into segment receipts, then compresses them via the recursion circuit into a single receipt. Users select the receipt kind via prove_with_opts.
Pipeline (conceptual)
1. Execute — Guest run is split into segments. 2. Segment receipts — Each segment is proven (RISC-V STARK). 3. Lift — Segment receipts are “lifted” into SuccinctReceipts (recursion circuit verifies STARKs). 4. Join — Pairs of SuccinctReceipts are joined until one remains (constant-time verification per segment). 5. identity_p254 — Optional step for Groth16: convert to Poseidon254 for SNARK. 6. Compress — Produce Groth16Receipt for on-chain verification (small, fixed size).
Receipt kinds
- Composite — Segment-level receipts; no recursion. Larger, default from
prove(). - Succinct — After lift + join (and optionally resolve for composition). Use
ReceiptKind::Succinct. - Groth16 — After full pipeline including STARK-to-SNARK; for verifier contracts. Use
ReceiptKind::Groth16and installrisc0-groth16via rzup.
Recursion programs
The recursion circuit runs fixed programs: lift (verify RISC-V STARK), join (verify two recursion STARKs), resolve (used in composition to remove an assumption), identity_p254 (prepare for Groth16). You don’t implement these; the prover uses them when you request Succinct or Groth16.
When to use which
- Use Composite for simple flows and when you don’t need on-chain verification or composition.
- Use Succinct for composition and smaller proof size off-chain.
- Use Groth16 when verifying on Ethereum (or other chains with a RISC Zero Groth16 verifier contract).
<!-- Source references:
- sources/risc0/website/api_versioned_docs/version-2.3/recursion.md
- sources/risc0/website/api_versioned_docs/version-3.0/blockchain-integration/shrink-wrapping.md
- sources/risc0/website/api_versioned_docs/version-3.0/security-model.md
-->