Documentation Index Fetch the complete documentation index at: https://mintlify.com/provablehq/snarkvm/llms.txt
Use this file to discover all available pages before exploring further.
This example demonstrates how to generate and verify zero-knowledge proofs using SnarkVM’s SNARK implementation.
Overview
When you execute a transaction in SnarkVM, a zero-knowledge proof is automatically generated to prove the computation was performed correctly. This example shows how to:
Execute a program and generate a proof
Extract the proof from the transaction
Verify the proof independently
Complete example
use snarkvm :: {
prelude ::* ,
ledger :: store :: ConsensusStore ,
};
use rand :: thread_rng;
fn main () -> Result <()> {
// Setup: Create a simple program
let program_string = r#"
program verify_example.aleo;
function compute:
input r0 as u64.private;
input r1 as u64.private;
add r0 r1 into r2;
mul r2 r2 into r3;
output r3 as u64.private;
"# ;
let program = Program :: < Testnet3 > :: from_str ( program_string ) ? ;
// Initialize VM
let store = ConsensusStore :: < Testnet3 , ConsensusMemory < Testnet3 >> :: open (
Some ( aleo_std :: StorageMode :: Development ( 0 ))
) ? ;
let vm = VM :: from ( store ) ? ;
// Generate account
let private_key = PrivateKey :: < Testnet3 > :: new ( & mut thread_rng ()) ? ;
// Deploy program
println! ( "Deploying program..." );
let deployment = vm . deploy (
& private_key ,
& program ,
None ,
0 ,
None ,
& mut thread_rng ()
) ? ;
println! ( " \n --- Proof Generation ---" );
// Execute the function (generates proof)
let inputs = [
Value :: from_str ( "5u64" ) ? ,
Value :: from_str ( "3u64" ) ? ,
];
println! ( "Computing: (5 + 3)² = 64" );
println! ( "Generating zero-knowledge proof..." );
let transaction = vm . execute (
& private_key ,
( "verify_example.aleo" , "compute" ),
inputs . iter (),
None ,
0 ,
None ,
& mut thread_rng ()
) ? ;
println! ( "✓ Proof generated successfully" );
println! ( "Transaction ID: {}" , transaction . id ());
println! ( " \n --- Proof Verification ---" );
// Verify the transaction
let verification_result = vm . check_transaction (
& transaction ,
None ,
& mut thread_rng ()
);
match verification_result {
Ok ( _ ) => {
println! ( "✓ Proof verified successfully!" );
println! ( "The computation is correct without revealing inputs." );
}
Err ( e ) => {
println! ( "✗ Proof verification failed: {}" , e );
}
}
println! ( " \n --- Proof Properties ---" );
// Extract execution from transaction
if let Transaction :: Execute ( _ , execution , _ ) = & transaction {
println! ( "Number of transitions: {}" , execution . len ());
for ( i , transition ) in execution . transitions () . enumerate () {
println! ( " \n Transition {}:" , i );
println! ( " Program: {}" , transition . program_id ());
println! ( " Function: {}" , transition . function_name ());
println! ( " Inputs: {}" , transition . inputs () . len ());
println! ( " Outputs: {}" , transition . outputs () . len ());
// The proof is attached to the transition
if let Some ( proof ) = transition . proof () {
println! ( " Proof: {} bytes" , proof . to_string () . len ());
}
}
}
Ok (())
}
Step by step
Create and deploy a program
let program = Program :: < Testnet3 > :: from_str ( program_string ) ? ;
let deployment = vm . deploy ( & private_key , & program , None , 0 , None , & mut thread_rng ()) ? ;
Deploy a simple program that adds two numbers and squares the result.
Execute with proof generation
let transaction = vm . execute (
& private_key ,
( "verify_example.aleo" , "compute" ),
inputs . iter (),
None ,
0 ,
None ,
& mut thread_rng ()
) ? ;
When you call vm.execute, SnarkVM automatically:
Compiles the program to an R1CS constraint system
Generates a witness (the intermediate values)
Creates a SNARK proof using Varuna
Verify the proof
let verification_result = vm . check_transaction (
& transaction ,
None ,
& mut thread_rng ()
);
Verification checks that:
The proof is valid
The program exists and matches the claimed ID
All constraints are satisfied
The computation was performed correctly
Extract proof details
if let Transaction :: Execute ( _ , execution , _ ) = & transaction {
for transition in execution . transitions () {
if let Some ( proof ) = transition . proof () {
println! ( "Proof: {} bytes" , proof . to_string () . len ());
}
}
}
Each transition in the execution contains a proof that can be extracted and inspected.
What is proven?
The zero-knowledge proof demonstrates:
Correctness : The output was computed correctly from the inputs
Program execution : The specified program was executed
Input knowledge : The prover knows private inputs that satisfy the constraints
All without revealing:
The private input values (5 and 3)
The intermediate computation steps
The final output value (64)
Proof properties
Succinctness Proofs are small (~1-2 KB) regardless of computation complexity
Fast verification Verification takes milliseconds even for complex computations
Zero-knowledge No information about private inputs is revealed
Non-interactive No back-and-forth communication required
Manual proof generation
For advanced use cases, you can generate proofs manually:
use snarkvm :: prelude ::* ;
// Get the proving key for the function
let process = vm . process ();
let stack = process . get_stack ( program . id ()) ? ;
let proving_key = stack . get_proving_key ( "compute" ) ? ;
// Generate proof manually
let proof = proving_key . prove (
& inputs ,
& mut thread_rng ()
) ? ;
// Verify with verifying key
let verifying_key = stack . get_verifying_key ( "compute" ) ? ;
let is_valid = verifying_key . verify ( & inputs , & proof ) ? ;
assert! ( is_valid );
Typical proving and verification times on modern hardware:
Proof generation : 100-500ms for simple programs
Proof verification : 10-50ms
Proof size : 1-2 KB regardless of computation
Complex programs with many constraints take longer but remain practical.
Common verification errors
The proof doesn’t satisfy the constraints. This usually indicates:
Tampered proof data
Incorrect program execution
Mismatched verifying key
The program ID in the transaction doesn’t match any deployed program: // Make sure the program is deployed first
vm . deploy ( & private_key , & program , None , 0 , None , & mut thread_rng ()) ? ;
The witness doesn’t satisfy all constraints. This shouldn’t happen with properly generated proofs but can occur if:
The circuit is under-constrained
There’s a bug in the program logic
Advanced: Batch verification
Verify multiple proofs efficiently:
let transactions = vec! [ tx1 , tx2 , tx3 ];
for transaction in & transactions {
vm . check_transaction ( transaction , None , & mut thread_rng ()) ? ;
}
println! ( "All {} proofs verified!" , transactions . len ());
SnarkVM’s Varuna SNARK supports native batch verification for improved performance when verifying many proofs.
Next steps
Zero-knowledge proofs Deep dive into how ZK proofs work in SnarkVM
SNARK algorithms API reference for Varuna SNARK implementation