Onchain Verification: Setup
The best way to get started with verifying SP1 proofs on-chain is to refer to the SP1 Project Template.
- The template program shows how to write outputs that can be decoded in Solidity.
- The template script shows how to generate the proof using the SDK and save it to a file.
- The template contract shows how to verify the proof onchain using Solidity.
Refer to the section on Contract Addresses for the addresses of the deployed verifiers.
Generating SP1 Proofs for Onchain Verification
By default, the proofs generated by SP1 are not verifiable onchain, as they are non-constant size and STARK verification on Ethereum is very expensive. To generate a proof that can be verified onchain, we use performant STARK recursion to combine SP1 shard proofs into a single STARK proof and then wrap that in a SNARK proof. Our ProverClient
has a prover option for this called plonk
. Behind the scenes, this function will first generate a normal SP1 proof, then recursively combine all of them into a single proof using the STARK recursion protocol. Finally, the proof is wrapped in a SNARK proof using PLONK.
WARNING: The Groth16 and PLONK provers are only guaranteed to work on official releases of SP1. To use Groth16 or PLONK proving & verification locally, ensure that you have Docker installed and have at least 32GB of RAM. Note that you might need to increase the memory limit for docker desktop if you're running on Mac.
Example
use sp1_sdk::{include_elf, utils, HashableKey, ProverClient, SP1Stdin};
/// The ELF we want to execute inside the zkVM.
const ELF: &[u8] = include_elf!("fibonacci-program");
fn main() {
// Setup logging.
utils::setup_logger();
// Create an input stream and write '500' to it.
let n = 500u32;
let mut stdin = SP1Stdin::new();
stdin.write(&n);
// Set up the pk and vk.
let client = ProverClient::from_env();
let (pk, vk) = client.setup(ELF);
println!("vk: {:?}", vk.bytes32());
// Generate the Groth16 proof.
let proof = client.prove(&pk, stdin).groth16().run().unwrap();
println!("generated proof");
// Get the public values as bytes.
let public_values = proof.public_values.as_slice();
println!("public values: 0x{}", hex::encode(public_values));
// Get the proof as bytes.
let solidity_proof = proof.bytes();
println!("proof: 0x{}", hex::encode(solidity_proof));
// Verify proof and public values
client.verify(&proof, &vk).expect("verification failed");
// Save the proof.
proof.save("fibonacci-groth16.bin").expect("saving proof failed");
println!("successfully generated and verified proof for the program!")
}
You can run the above script with RUST_LOG=info cargo run --bin groth16_bn254 --release
in examples/fibonacci/script
.
Using Groth16 and PLONK without Docker (Advanced)
If you would like to run the Groth16 or PLONK prover directly without Docker, you must have Go 1.22 installed and enable the native-gnark
feature in sp1-sdk
. This path is not recommended and may require additional native dependencies.
sp1-sdk = { version = "2.0.0", features = ["native-gnark"] }