
Sp1
- 2 installs
- 4 repo stars
- Updated February 25, 2026
- hairyf/blockchain-skills
Reference the SP1 zkVM - prove arbitrary Rust/RISC-V programs with the SDK and CLI, using recursion, precompiles, and Plonk/Groth16 proofs.
About
A reference skill for SP1, a zero-knowledge VM that proves correct execution of RISC-V programs written in Rust, covering the SDK, CLI, recursion, and precompiles. A developer uses it when building provable Rust programs and verifying their execution.
- Execute, prove, and verify RISC-V programs via SDK
- Recursion, precompiles, and Plonk/Groth16 proofs
Sp1 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 sp1Add 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 the SP1 zkVM - prove arbitrary Rust/RISC-V programs with the SDK and CLI, using recursion, precompiles, and Plonk/Groth16 proofs.
Files
Skill based on SP1 (zkVM v6 Hypercube), generated fromsources/sp1. Doc path:sources/sp1/(README, DEVELOPMENT.md, crates/*/README.md, examples), plus https://docs.succinct.xyz/docs/sp1 (introduction, quickstart, recommended-workflow).
SP1 is a zero-knowledge virtual machine that proves correct execution of RISC-V programs. Write provable logic in Rust (or other LLVM→RISC-V languages), build an ELF with the succinct toolchain, and use the SDK to execute, prove, and verify. No custom circuits; use Plonk or Groth16 proofs, recursion for aggregation, and precompiles to extend the zkVM.
Core References
| Topic | Description | Reference |
|---|---|---|
| Overview | Program vs script, ELF, entrypoint, project layout | core-overview |
| Program I/O | read, commit, SP1Stdin, public values | core-program-io |
| CLI and build | cargo prove new/build, project structure | core-cli-and-build |
| Proving and verification | ProverClient, execute, setup, prove, verify | core-proving-and-verification |
Features
Proof formats and verification
| Topic | Description | Reference |
|---|---|---|
| Proof formats | Plonk, Groth16, compressed; bytes for Solidity | features-proof-formats |
| Recursion | Verifying proofs in the zkVM, aggregation | features-recursion |
Extending the zkVM
| Topic | Description | Reference |
|---|---|---|
| Precompiles | Adding custom chips (syscall, MachineAir, AIR) | features-precompiles |
Best Practices
| Topic | Description | Reference |
|---|---|---|
| Workflow | Execute-only during dev, reuse client, crate layout, prover network | best-practices-workflow |
Generation Info
- Source:
sources/sp1 - Git SHA:
7a89e135bf65d11eb1673d67a06e5cd829973c3d - Generated: 2026-02-24
- Doc path used:
sources/sp1/(README.md, DEVELOPMENT.md, CONTRIBUTING.md; crates/cli, build, verifier, recursion, core/machine precompiles READMEs; examples: fibonacci, io, aggregation, groth16; patch-testing). Online: https://docs.succinct.xyz/docs/sp1 (introduction, quickstart, recommended-workflow).
SP1 Best Practices and Workflow
Develop with execute-only first
While iterating on the program, run only execution (no proof):
let (public_values, report) = client.execute(ELF, &stdin).run().unwrap();Check that public_values and the execution report (cycle counts, etc.) match expectations. Proof generation will only succeed if execution is correct; proving is slower and more resource-heavy. For programs over ~1M cycles, prefer execute-only during day-to-day dev and run full proving in CI or before release.
Reuse ProverClient
Create ProverClient once and reuse it. Initialization loads proving parameters and can be slow. For concurrent or repeated use, wrap in Arc<ProverClient>.
Crate layout
Keep the program crate (the one with sp1_zkvm::entrypoint!(main)) minimal. Put business logic in a separate crate that the program depends on. Benefits:
- Unit test the logic without the zkVM target.
- Share types between program and script.
- Clear separation between “proved entrypoint” and “reusable logic.”
Prover network for production
For non-trivial or production workloads, use the Succinct Prover Network instead of local CPU proving. Set the appropriate env (e.g. SP1_PROVER) and use the same ProverClient API; proofs are generated in the cloud with better latency and cost. See the prover network docs for setup.
Cycle count and cost
Use the execution report’s cycle (and syscall) counts to estimate proving time and cost. Small programs have a large fixed overhead; per-cycle efficiency improves for larger programs.
Debugging
- Constraint failures: run tests with
RUST_LOG=info RUST_BACKTRACE=1and the--features debugfeature to get clearer failure info. - Recursion panics: use
RUST_BACKTRACE=1 RUSTFLAGS="-g" SP1_DEBUG=truewhen running recursion tests.
<!-- Source references:
- https://docs.succinct.xyz/docs/sp1/getting-started/recommended-workflow
- sources/sp1/DEVELOPMENT.md
- sources/sp1/crates/recursion/README.md
-->
SP1 CLI and Build
The cargo prove CLI is the main way to create projects, build programs, and run traces. Install via sp1up or from source with cargo install --path crates/cli.
Create a project
cargo prove new --bare my-project
cd my-project--bare: script + program only. Use --evm to also generate Solidity contracts for on-chain verification (requires Foundry; run forge install in contracts/ after creation).
Project layout
.
├── program/ # zkVM program (RISC-V ELF)
│ ├── Cargo.toml
│ └── src/main.rs
├── rust-toolchain # succinct toolchain for program
└── script/
├── Cargo.toml
├── build.rs # builds program via sp1_build::build_program
└── src/
├── main.rs
└── bin/ # optional binaries (execute, prove, groth16, etc.)The script’s build.rs typically calls:
fn main() {
sp1_build::build_program("../program");
}So the program ELF is built automatically when you build or run the script.
Build the program
From the program directory:
cd program && cargo prove buildELF is produced under target/elf-compilation. The script crate’s build.rs usually runs this so you don’t need to run it manually when developing.
CLI development usage
# Run CLI from repo
cargo run --bin cargo-prove -- prove --help
# Run a subcommand (e.g. trace)
cargo run --bin cargo-prove -- prove trace --elf <path> --trace <path>Key points
cargo prove new --bareor--evmcreates the standard program/script layout.- Program is built with the succinct toolchain; script uses
sp1_build::build_programinbuild.rs. - In monorepos, add the program’s
Cargo.tomltorust-analyzer.linkedProjectsfor full IDE support.
<!-- Source references:
- https://docs.succinct.xyz/docs/sp1/getting-started/quickstart
- sources/sp1/crates/cli/README.md
- sources/sp1/crates/build/README.md
- sources/sp1/examples/fibonacci/script/build.rs
-->
SP1 Introduction
SP1 is a zero-knowledge virtual machine (zkVM) that proves correct execution of programs compiled for RISC-V. Programs can be written in Rust, C++, C, or any language that targets RISC-V. No custom circuits or crypto expertise are required: write normal code, compile, and generate a proof.
Why Use SP1
- Maintainability: Standard Rust (with
std), no custom DSLs. Easier to audit and evolve. - Faster development: Avoid low-level ZK engineering; go from idea to mainnet sooner.
- Performance: State-of-the-art proving speed; production-ready and audited.
SP1 is open source (MIT/Apache 2.0) with both prover and verifier implementations. V6 introduces Hypercube, a multilinear proof system with improved performance and recursion.
Key Concepts
- zkVM: The program runs in a RISC-V environment; the prover produces a ZK proof of that execution.
- ELF: The proven artifact is a RISC-V executable (ELF). Build with the
succinctRust toolchain viacargo prove build. - Program vs script: The program crate runs inside the zkVM; the script crate runs on the host and performs setup, proving, and verification.
<!-- Source references:
- https://docs.succinct.xyz/docs/sp1/introduction
- https://github.com/succinctlabs/sp1 (README.md)
-->
SP1 Core Overview
SP1 is a zero-knowledge virtual machine (zkVM) that proves correct execution of RISC-V programs. You write provable logic in Rust (or other LLVM→RISC-V languages); the same code runs in the zkVM and produces a proof. No custom circuits or ZK DSLs.
Program vs script
- Program: The code that runs inside the zkVM and is proven. Lives in the
program/crate. Compiled to a RISC-V ELF with thesuccinctRust toolchain. - Script: Host-side code that builds the program, feeds inputs, runs the prover/verifier, and reads public values. Lives in the
script/crate. Usessp1_sdkandinclude_elf!to load the program ELF.
Program and script communicate via stdin (host → program) and public values (program → host, committed with sp1_zkvm::io::commit).
Entrypoint
Programs must use the zkVM entrypoint macro and have no standard main:
#![no_main]
sp1_zkvm::entrypoint!(main);
pub fn main() {
// provable logic here
}The macro wraps main so it runs correctly inside the zkVM. Keep the program crate minimal; put most logic in a separate crate so you can unit test without the zkVM target.
ELF and build
The program is built with cargo prove build (or via script/build.rs calling sp1_build::build_program). Output is an ELF in target/elf-compilation. The script loads it with:
const ELF: &[u8] = include_elf!("your-program-name");Program crate name (in its Cargo.toml) must match the name passed to include_elf!.
Key points
- SP1 proves arbitrary Rust (and other RISC-V-compilable) code; V6 uses the Hypercube proof system.
- MSRV: Rust 1.79.
- Prover and verifier are open source (MIT/Apache 2.0).
<!-- Source references:
- https://docs.succinct.xyz/docs/sp1/introduction
- https://docs.succinct.xyz/docs/sp1/getting-started/quickstart
- sources/sp1/README.md
- sources/sp1/crates/build/README.md
-->
SP1 Program I/O
Programs running in the zkVM read inputs from the prover and expose outputs as public values. Types must match between script (writer) and program (reader).
Reading input in the program
Use sp1_zkvm::io::read and sp1_zkvm::io::read_vec for typed input:
#![no_main]
sp1_zkvm::entrypoint!(main);
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
struct MyPoint {
pub x: usize,
pub y: usize,
}
pub fn main() {
let n: u32 = sp1_zkvm::io::read();
let point: MyPoint = sp1_zkvm::io::read();
let bytes: Vec<u8> = sp1_zkvm::io::read_vec();
// use n, point, bytes...
}Any type that implements Deserialize (e.g. via Serde) can be read. Order and types must match what the script writes to SP1Stdin.
Committing public values
Public values are the program’s attested output. Commit them so the verifier (and script) can read them:
sp1_zkvm::io::commit(&value); // single value (Serialize)
sp1_zkvm::io::commit_slice(&[u8]); // raw bytesOnly committed data is part of the public input to the proof; the rest is private.
Writing input from the script
Build SP1Stdin and write in the same order the program reads:
use sp1_sdk::{SP1Stdin, ProverClient, include_elf};
let mut stdin = SP1Stdin::new();
stdin.write(&1000u32);
stdin.write(&my_struct);
stdin.write_vec(&byte_vec);
let client = ProverClient::from_env();
let (_, report) = client.execute(ELF, &stdin).run().unwrap();Reading public values in the script
After execution or after proving, read public values in the same order they were committed:
let (mut public_values, _) = client.execute(ELF, &stdin).run().unwrap();
// or from proof:
// let public_values = &proof.public_values;
let _ = public_values.read::<u32>(); // skip first (e.g. n)
let a = public_values.read::<u32>();
let b = public_values.read::<u32>();Key points
read/read_vecin program ↔write/write_vecin script; use the same types and order.commit/commit_slicedefine the public output; verifier checks these.- Use Serde-compatible types for structured I/O; keep the program’s main thin and delegate to a library crate for testability.
<!-- Source references:
- https://docs.succinct.xyz/docs/sp1/getting-started/quickstart
- sources/sp1/examples/fibonacci/program/src/main.rs
- sources/sp1/examples/io/program/src/main.rs
- sources/sp1/examples/fibonacci/script/src/main.rs
-->
SP1 Proving and Verification
Use the sp1_sdk crate and ProverClient to execute programs (no proof), generate proofs, and verify them. Prefer executing during development; prove when you need a proof or are testing the full pipeline.
Client and execute (no proof)
use sp1_sdk::{include_elf, utils, ProverClient, SP1Stdin};
const ELF: &[u8] = include_elf!("fibonacci-program");
fn main() {
utils::setup_logger();
let mut stdin = SP1Stdin::new();
stdin.write(&1000u32);
let client = ProverClient::from_env();
let (mut public_values, report) = client.execute(ELF, &stdin).run().unwrap();
println!("cycles: {}", report.total_instruction_count());
let a = public_values.read::<u32>();
let b = public_values.read::<u32>();
}client.execute(ELF, &stdin).run() runs the program in the RISC-V runtime and returns public values and an execution report (cycle counts, etc.). No proof is generated—use this to iterate quickly.
Setup and prove
let (pk, vk) = client.setup(ELF);
let proof = client.prove(&pk, &stdin).plonk().run().unwrap();
// or: .groth16().run().unwrap()setup(elf): produce proving keypkand verification keyvkfor that ELF. Call once per program (or cache keys).prove(&pk, &stdin): returns a builder; choose proof format (e.g..plonk()or.groth16()) then.run().
Verify
client.verify(&proof, &vk).expect("verification failed");Verification checks the proof and that the committed public values match. Read outputs from proof.public_values in the same order as commit in the program.
Proof serialization
proof.save("proof-with-pis.bin").expect("saving proof failed");
let loaded = SP1ProofWithPublicValues::load("proof-with-pis.bin").unwrap();
client.verify(&loaded, &vk).expect("verification failed");Key points
- Use
ProverClient::from_env(); it respectsSP1_PROVER(e.g. for prover network or CPU). - Initialize the client once and reuse it; setup and loading can be slow.
- For large programs, run with
--executeonly during dev to avoid proving every run; prove only when needed or in CI.
<!-- Source references:
- https://docs.succinct.xyz/docs/sp1/getting-started/quickstart
- sources/sp1/examples/fibonacci/script/src/main.rs
- sources/sp1/examples/fibonacci/script/bin/execute.rs
- sources/sp1/crates/sdk/src/cpu/mod.rs
- sources/sp1/crates/sdk/src/env/mod.rs
-->
SP1 Precompiles
Precompiles are custom chips that extend the zkVM with efficient, constrained logic (e.g. crypto or big-int ops). They run as syscalls from the program and are implemented as part of the core machine.
When to add a precompile
Use a precompile when an operation is expensive in vanilla RISC-V but can be expressed as a small, fixed constraint system. Examples in SP1: BN254, BLS12-381, secp256k1, SHA, uint256 mul. Adding one requires implementing execution, trace generation, and AIR evaluation.
Implementation outline
1. Chip struct and columns In core/src/syscall/precompiles/, define a struct and column layout (e.g. inputs, memory reads/writes, output).
2. Syscall trait Implement Syscall: num_extra_cycles, and execute(rt, syscall, arg1, arg2) to run the op in the runtime (e.g. read pointers, compute, write result).
3. MachineAir trait Implement MachineAir<F>: name, generate_trace (from execution record to trace matrix), and included (when this chip has work). Register the chip’s events in PrecompileEvent and get_local_mem_events in core/executor/src/events/precompiles/mod.rs.
4. Air and BaseAir Implement BaseAir::width and Air::eval so the constraint system matches execution. Mismatches here cause proof failures.
5. Syscall code Add a new SyscallCode variant, update from_u32, insert the chip in default_syscall_map, and update get_chips_and_costs and estimate_area.
6. Expose to programs In zkvm/entrypoint/src/syscalls/, add an extern "C" function that issues the ecall with the new syscall code, and export the constant in syscalls/mod.rs.
7. Tests Add a test program under crates/test-artifacts/programs that calls the precompile, build with cargo prove build, include the ELF in test-artifacts, and add a test that runs the program (e.g. run_test_io::<CpuProver<_, _>>(program, SP1Stdin::new())).
Key points
- Execution (
execute) and constraints (eval) must agree; otherwise proofs fail or are unsound. - Register the precompile in the executor’s
PrecompileEventand syscall map so traces and costs are correct. - Test with a real zkVM program and
cargo test --releasein the core crate.
<!-- Source references:
- sources/sp1/crates/core/machine/src/syscall/precompiles/README.md
-->
SP1 Proof Formats
SP1 supports multiple proof backends. Choose based on verification context: on-chain (Groth16 often preferred for size/speed), in-SDK verification, or compressed/recursive proofs.
Plonk (default)
let proof = client.prove(&pk, &stdin).plonk().run().unwrap();
client.verify(&proof, &vk).expect("verification failed");Plonk proofs are verified natively in the SDK. Good for development and when you don’t need an on-chain verifier.
Groth16 (EVM-friendly)
let proof = client.prove(&pk, &stdin).groth16().run().unwrap();
// On-chain: use proof bytes and vk
let solidity_proof = proof.bytes();
let public_values_bytes = proof.public_values.as_slice();
// Pass to Solidity verifier contract
// SDK verify
client.verify(&proof, &vk).expect("verification failed");
proof.save("proof.bin").expect("save failed");Groth16 proofs are short and fast to verify on EVM. Use proof.bytes() and the verifying key (e.g. vk.bytes32()) with the contract. The sp1-verifier crate and pre-generated BN254 verification keys are used for both Groth16 and Plonk.
Compressed / recursion
For aggregation or recursion, proofs can be compressed or used as subproofs inside another program. The recursion program runs in the zkVM and verifies SP1 proofs via sp1_zkvm::lib::verify::verify_sp1_proof; see the recursion/aggregation examples and the recursion crate README.
Key points
- Plonk: default, SDK verify only.
- Groth16: use for EVM verification;
proof.bytes()and public values for the contract. - Verification keys for Groth16/Plonk live in
sp1-verifier(bn254-vk) and in~/.sp1/circuits/after setup.
<!-- Source references:
- sources/sp1/examples/fibonacci/script/bin/groth16_bn254.rs
- sources/sp1/examples/fibonacci/script/src/main.rs
- sources/sp1/crates/verifier/README.md
- sources/sp1/crates/sdk/CHANGELOG.md
-->
SP1 Recursion
You can verify SP1 proofs inside another zkVM program (recursion). That allows aggregating many proofs into one, or building proof chains.
Verifying a proof in the zkVM
Inside the zkVM program, use the verifier from sp1_zkvm::lib::verify:
#![no_main]
sp1_zkvm::entrypoint!(main);
use sha2::{Digest, Sha256};
pub fn main() {
let vkey = sp1_zkvm::io::read::<[u32; 8]>();
let public_values = sp1_zkvm::io::read::<Vec<u8>>();
let digest = Sha256::digest(&public_values);
sp1_zkvm::lib::verify::verify_sp1_proof(vkey, &digest.into());
// If we get here, the proof was valid; commit aggregated result, etc.
sp1_zkvm::io::commit_slice(&digest);
}The recursion program receives the verification key and the committed public values (or their digest) and verifies the proof. The vkey and public values must match what the outer prover produced.
Aggregation pattern
A typical aggregation program:
1. Reads a list of (vkey, public_values) pairs (from previous proofs). 2. Verifies each with verify_sp1_proof(vkey, &public_values_digest). 3. Combines the results (e.g. Merkle root of (vkey, public_values)) and commits that.
See examples/aggregation/program: it reads vkeys and public values, verifies each proof, builds a Merkle tree over (vkey, public_value) leaves, and commits the root.
Debugging recursion
Recursion runs in a separate runtime. On panic you may see a TRAP error. For a backtrace:
RUST_BACKTRACE=1 RUSTFLAGS="-g" SP1_DEBUG=true cargo test ...Key points
- Use
verify_sp1_proof(vkey, &digest)in the zkVM; the digest is over the public values the proof commits to. - Aggregation = many proofs → one recursion program that verifies each and commits a single aggregate (e.g. root).
- Recursion crate and tests live under
crates/recursion; see README for recursion-specific build/test.
<!-- Source references:
- sources/sp1/examples/aggregation/program/src/main.rs
- sources/sp1/crates/recursion/README.md
- sources/sp1/crates/zkvm (verify API)
-->