Documentation Index Fetch the complete documentation index at: https://mintlify.com/risc0/risc0/llms.txt
Use this file to discover all available pages before exploring further.
RISC Zero can convert STARK proofs to Groth16 SNARKs, enabling efficient verification on blockchains like Ethereum. Groth16 proofs are constant-size (around 200-300 bytes) and can be verified in a single smart contract transaction.
Overview
The STARK-to-SNARK conversion process (also called “shrink wrapping”) transforms a RISC Zero STARK proof into a Groth16 proof:
Input : SuccinctReceipt (STARK proof, ~200kB)
Output : Groth16Receipt (SNARK proof, ~300 bytes)
Verification : On-chain using the RISC Zero Verifier Contract
Platform Requirements : Groth16 proving currently only works on x86 architecture . Apple Silicon (M-series) is not supported, even via Docker. See #1520 and #1749 .
Generating Groth16 Proofs
Using ProverOpts (Recommended)
The simplest way to generate a Groth16 proof:
use risc0_zkvm :: {default_prover, ExecutorEnv , ProverOpts };
use methods :: { METHOD_ELF , METHOD_ID };
fn main () {
// Build execution environment
let env = ExecutorEnv :: builder ()
. write ( & input_data ) . unwrap ()
. build () . unwrap ();
// Generate Groth16 proof directly
let opts = ProverOpts :: groth16 ();
let receipt = default_prover ()
. prove_with_opts ( env , METHOD_ELF , & opts )
. unwrap ()
. receipt;
// Verify the Groth16 receipt
receipt . verify ( METHOD_ID ) . unwrap ();
// Receipt is now ready for on-chain verification
println! ( "Groth16 proof generated successfully!" );
}
Using Bonsai SDK
Generate Groth16 proofs remotely via Bonsai:
use std :: time :: Duration ;
use bonsai_sdk :: blocking :: Client ;
use risc0_zkvm :: {compute_image_id, Receipt };
fn prove_groth16_on_bonsai ( input_data : Vec < u8 >) -> Result < Receipt > {
let client = Client :: from_env ( risc0_zkvm :: VERSION ) ? ;
// Upload image and input (same as regular proving)
let image_id = hex :: encode ( compute_image_id ( METHOD_ELF ) ? );
client . upload_img ( & image_id , METHOD_ELF . to_vec ()) ? ;
let input_id = client . upload_input ( input_data ) ? ;
// Create proving session
let session = client . create_session (
image_id ,
input_id ,
vec! [], // assumptions
false // execute_only
) ? ;
// Wait for STARK proof to complete
let stark_session_id = loop {
let status = session . status ( & client ) ? ;
match status . status . as_str () {
"SUCCEEDED" => break session . uuid . clone (),
"RUNNING" => {
std :: thread :: sleep ( Duration :: from_secs ( 15 ));
continue ;
}
_ => panic! ( "Session failed: {:?}" , status . error_msg),
}
};
// Convert STARK to SNARK (Groth16)
let snark_session = client . create_snark ( stark_session_id ) ? ;
// Wait for Groth16 proof
loop {
let status = snark_session . status ( & client ) ? ;
match status . status . as_str () {
"SUCCEEDED" => {
let snark_url = status . output . unwrap ();
let snark_buf = client . download ( & snark_url ) ? ;
let receipt : Receipt = bincode :: deserialize ( & snark_buf ) ? ;
return Ok ( receipt );
}
"RUNNING" => {
eprintln! ( "SNARK proving in progress..." );
std :: thread :: sleep ( Duration :: from_secs ( 15 ));
}
_ => panic! ( "SNARK failed: {:?}" , status . error_msg),
}
}
}
See Bonsai SDK for more details on remote proving.
Proving Pipeline
The complete proving pipeline for generating a Groth16 proof:
Run the zkVM to generate execution segments:
let session = ExecutorImpl :: from_elf ( env , elf ) ?. run () ? ;
Generate STARK proofs for each execution segment:
for segment in session . segments () {
let segment_receipt = prove_segment ( & segment ) ? ;
segments . push ( segment_receipt );
}
Lift to Recursion Circuit
Convert each segment proof to use the recursion circuit:
let succinct_receipt = lift ( segment_receipt ) ? ;
Recursively join all segment proofs into a single proof:
let mut current = succinct_receipt_1 ;
for receipt in remaining_receipts {
current = join ( current , receipt ) ? ;
}
Prepare the proof for Groth16 conversion:
let identity_receipt = identity_p254 ( final_succinct_receipt ) ? ;
Generate the final Groth16 SNARK:
let groth16_receipt = compress ( identity_receipt ) ? ;
When using ProverOpts::groth16(), all these steps are handled automatically.
Receipt Types Comparison
Property Composite Succinct Groth16 Size Linear (~100kB-MB) Constant (~200kB) Constant (~300 bytes) Generation Time Fast Medium Slow Verification Time Linear Constant (off-chain) Constant (on-chain) Blockchain Ready No No Yes Supports Composition Yes Yes Yes Use Case Development Off-chain verification On-chain verification
Low-Level API
For advanced use cases, you can use the low-level risc0-groth16 crate:
Shrink Wrap Function
use risc0_groth16 :: prove :: shrink_wrap;
// Convert an identity_p254 seal to Groth16
let identity_p254_seal_bytes = /* ... */ ;
let groth16_seal = shrink_wrap ( identity_p254_seal_bytes ) ? ;
Verification
use risc0_groth16 :: { Verifier , ProofJson , PublicInputsJson , VerifyingKeyJson };
// Load verification data
let verifying_key : VerifyingKeyJson = serde_json :: from_str ( vk_json ) ? ;
let proof : ProofJson = serde_json :: from_str ( proof_json ) ? ;
let public_inputs = PublicInputsJson {
values : serde_json :: from_str ( inputs_json ) ? ,
};
// Verify the Groth16 proof
let verifier = Verifier :: from_json ( proof , public_inputs , verifying_key ) ? ;
verifier . verify () ? ;
Docker vs CUDA
The Groth16 prover can use either Docker or CUDA:
Docker (Default)
CUDA (GPU)
# Uses Docker for Groth16 proving
cargo run --release
CUDA-based Groth16 proving requires:
NVIDIA GPU with CUDA support
CUDA toolkit installed
cuda feature flag enabled
On-Chain Verification
Once you have a Groth16 receipt, you can verify it on Ethereum:
// Solidity contract using RiscZeroVerifier
import { IRiscZeroVerifier } from "risc0/IRiscZeroVerifier.sol" ;
contract MyContract {
IRiscZeroVerifier public verifier;
bytes32 public imageId;
function verifyProof (
bytes calldata seal ,
bytes32 journalHash
) public view returns ( bool ) {
// Verify the Groth16 proof on-chain
verifier. verify (seal, imageId, journalHash);
return true ;
}
}
See the RISC Zero Verifier Contract documentation for integration details.
Proving Time
Composite → Succinct : Depends on execution length (recursion overhead)
Succinct → Groth16 : Fixed time (~5-10 minutes on GPU, longer on CPU)
When to Use Groth16
✅ Use Groth16 when:
Verifying proofs on Ethereum or other EVM chains
Minimizing verification cost is critical
Proof size must be minimized (e.g., for storage)
❌ Avoid Groth16 when:
Only verifying off-chain (use Succinct instead)
Proving time is critical (use Composite for development)
Running on non-x86 architecture
Troubleshooting
Docker Issues
# Ensure Docker is running
sudo systemctl start docker
# Verify Docker installation
docker --version
CUDA Issues
# Check CUDA installation
nvcc --version
# Verify GPU is detected
nvidia-smi
If you encounter “platform not supported” errors on Apple Silicon, you must use an x86 machine for Groth16 proving. Consider using Bonsai remote proving instead.
Examples
Groth16 Proof Directory Installation and setup instructions
Blockchain Integration Deploy and verify on Ethereum
risc0-groth16 Crate Low-level Groth16 API documentation
Next Steps
Bonsai SDK Generate Groth16 proofs remotely
Proof Composition Compose multiple proofs efficiently
Local Proving Set up local proving infrastructure