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-Groth16 new. Same length checks, plus vk_ic.len() == nr_pubinputs + 2 (gnark appends one extra K column for the commitment-derived hash wire).
  • A vk_commitment: Option<CommitmentVerifyingKey> field on Groth16Verifyingkey holding the Pedersen commitment key (g2, g_sigma_neg_g2). The field itself is not feature-gated; the standard constructor new rejects vks where it is set with Groth16Error::UnexpectedCommitmentKey.
  • Internal hash-to-field (RFC 9380 expand_message_xmd over SHA-256) used to derive the commitment challenge, byte-exact with gnark-crypto's ecc/bn254/fr/element.go::Hash and validated against golden vectors.
  • groth16_solana::vk::gnark::parse_gnark_vk_bytes — parser for gnark's VerifyingKey.WriteRawTo binary, including the trailing PublicAndCommitmentCommitted and CommitmentKeys sections. Returns a Groth16VerifyingkeyOwned which exposes as_borrowed() for use with the verifier API. Multi-commitment vks (rare; typical when a circuit calls raw api.Commit more than once) are rejected with Groth16Error::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.