Proof Aggregation
Overview
SP1 supports proof aggregation and recursion, which allows you to verify an SP1 proof within SP1. Use cases include:
- Reducing on-chain verification costs by aggregating multiple SP1 proofs into a single SP1 proof.
- Proving logic that is split into multiple proofs, such as proving a statement about a rollup's state transition function by proving each block individually and aggregating these proofs to produce a final proof of a range of blocks.
For an example of how to use proof aggregation and recursion in SP1, refer to the aggregation example.
Note that to verify an SP1 proof inside SP1, you must generate a "compressed" SP1 proof (see Proof Types for more details).
When should SP1 proof aggregation be used?
Note that by itself, SP1 can already prove arbitrarily large programs by chunking the program's execution into multiple "shards" (contiguous batches of cycles) and generating proofs for each shard in parallel, and then recursively aggregating the proofs. Thus, aggregation is generally not necessary for most use-cases, as SP1's proving for large programs is already parallelized.
However, aggregation can be useful in two specific cases:
- When your computation requires more than the zkVM's limited (~2GB) memory.
- When you want to combine multiple SP1 proofs from different parties into a single proof to reduce on-chain verification costs.
Verifying Proofs inside the zkVM
To verify a proof inside the zkVM, you can use the sp1_zkvm::lib::verify::verify_sp1_proof
function.
sp1_zkvm::lib::verify::verify_sp1_proof(vkey, public_values_digest);
You do not need to pass in the proof as input into the syscall, as the proof will automatically be read for the proof input stream by the prover.
Note that you must include the verify
feature in your Cargo.toml
for sp1-zkvm
to be able to use the verify_proof
function (like this).
Generating Proofs with Aggregation
To provide an existing proof as input to the SP1 zkVM, you can write a proof and verifying key to a SP1Stdin
object, which is already used for all inputs to the zkVM.
# Generating proving key and verifying key.
let (input_pk, input_vk) = client.setup(PROOF_INPUT_ELF);
let (aggregation_pk, aggregation_vk) = client.setup(AGGREGATION_ELF);
// Generate a proof that will be recursively verified / aggregated. Note that we use the "compressed"
// proof type, which is necessary for aggregation.
let mut stdin = SP1Stdin::new();
let input_proof = client
.prove(&input_pk, stdin)
.compressed()
.run()
.expect("proving failed");
// Create a new stdin object to write the proof and the corresponding verifying key to.
let mut stdin = SP1Stdin::new();
stdin.write_proof(input_proof, input_vk);
// Generate a proof that will recursively verify / aggregate the input proof.
let aggregation_proof = client
.prove(&aggregation_pk, stdin)
.compressed()
.run()
.expect("proving failed");