groth16-solana
July 8, 2026 · View on GitHub
Groth16 zero-knowledge proof verification with Solana altbn254 syscalls.
Verification takes less than 200,000 compute units.
The syscalls are contained in Solana releases 1.18.x onwards and are active on Solana Mainnet-beta.
Inputs need to be in u8 arrays in big endian.
See functional test as an example how to use this library.
This crate is compatible with Groth16 proofs of circom circuits.
The verifier file can be generated with the JavaScript script from a verifyingkey.json file generated by snarkjs.
Usage:
let mut public_inputs_vec = Vec::new();
for input in PUBLIC_INPUTS.chunks(32) {
public_inputs_vec.push(input);
}
let proof_a: G1 =
<G1 as FromBytes>::read(&*[&change_endianness(&PROOF[0..64])[..], &[0u8][..]].concat())
.unwrap();
let mut proof_a_neg = [0u8; 65];
<G1 as ToBytes>::write(&proof_a.neg(), &mut proof_a_neg[..]).unwrap();
let proof_a = change_endianness(&proof_a_neg[..64]).try_into().unwrap();
let proof_b = PROOF[64..192].try_into().unwrap();
let proof_c = PROOF[192..256].try_into().unwrap();
let mut verifier = Groth16Verifier::new(
&proof_a,
&proof_b,
&proof_c,
public_inputs_vec.as_slice(),
&VERIFYING_KEY,
)
.unwrap();
verifier.verify().unwrap();
See functional test for a running example how to use this library.
Create Verifyingkey from snarkjs verifyingKey.json
Use snarkjs to export the verifyingkey as json.
In this repo:
- npm i
- npm run parse-vk
BSB22 commitments (gnark logderivlookup / api.Commit)
Enable the bsb22 feature to verify gnark Groth16 proofs that include a single
BSB22 (Bowe-Sankaranarayanan-Bonneau 2022) Pedersen commitment. This is the
shape every gnark circuit ends up with as soon as it touches
std/lookup/logderivlookup or any emulated-field range-check helper, because
gnark's multicommit.WithCommitment merges every deferred commit callback
into a single api.Commit() at finalization (see
std/multicommit/nativecommit.go:89).
[dependencies]
groth16-solana = { version = "0.2", features = ["bsb22"] }
The bsb22 feature adds:
Groth16Verifier::new_with_commitment(...)— alongside the existing standard-Groth16new. Same length checks, plusvk_ic.len() == nr_pubinputs + 2(gnark appends one extra K column for the commitment-derived hash wire).- A
vk_commitment: Option<CommitmentVerifyingKey>field onGroth16Verifyingkeyholding the Pedersen commitment key (g2,g_sigma_neg_g2). The field itself is not feature-gated; the standard constructornewrejects vks where it is set withGroth16Error::UnexpectedCommitmentKey. - Internal hash-to-field (RFC 9380
expand_message_xmdover SHA-256) used to derive the commitment challenge, byte-exact with gnark-crypto'secc/bn254/fr/element.go::Hashand validated against golden vectors. groth16_solana::vk::gnark::parse_gnark_vk_bytes— parser for gnark'sVerifyingKey.WriteRawTobinary, including the trailingPublicAndCommitmentCommittedandCommitmentKeyssections. Returns aGroth16VerifyingkeyOwnedwhich exposesas_borrowed()for use with the verifier API. Multi-commitment vks (rare; typical when a circuit calls rawapi.Commitmore than once) are rejected withGroth16Error::Bsb22UnsupportedMultiCommitment.
Cost on Solana: ~212k CU per BSB22 verify, measured end to end with
solana-bn254 v3, the sol_sha256 syscall for hash-to-field, and
stack-allocated expand_message_xmd buffers (no heap allocations on the
hot path). The source of truth for this figure is
tests/bsb22-program/tests/litesvm_cu.rs. Re-measure via:
cargo build-sbf --manifest-path tests/bsb22-program/Cargo.toml
cargo test --manifest-path tests/bsb22-program/Cargo.toml -- --nocapture
The test asserts the cost stays under 350k CU and logs the exact figure for each run. Rough breakdown of the BSB22-specific work on top of a standard Groth16 verify:
| Component |
|---|
expand_message_xmd SHA-256 (3 blocks, via sol_sha256 syscall) |
| 1 G1 scalar mul (BSB22 K column extension) |
| 2 G1 additions (MSM term + commitment) |
1 2-pair alt_bn128_pairing (Pedersen PoK) |
gnark fixture
A worked example lives at tests/bsb22/gnark-fixture/main.go: a single Go
file that defines three circuit variants (1, 2, and 3 logderivlookup
queries respectively, all merged into one BSB22 commitment by gnark) and
exposes Setup / Prove / NativeVerify via cgo. The Rust integration
test crate tests/bsb22/ compiles it to a C archive at build time, runs
bindgen, and exercises the verifier on real proofs from each variant. To
work with the fixture you need a Go toolchain installed (gnark v0.14).
cargo test -p groth16-solana # standard Groth16, default features
cargo test -p groth16-solana --features bsb22 # adds BSB22 unit tests
cargo test -p groth16-solana-tests-bsb22 # FFI integration tests (requires Go)
The verifier is a Rust port of gnark's backend/groth16/bn254/verify.go
(single-commitment case).
Audit
The groth16_solana release 0.0.1 has been audited during the Light Protocol v3 audit. Check out the report here.
Note: This open-source crate is provided "as-is" without warranties. Use at your own risk.
License
Licensed under Apache License, Version 2.0.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.