One substrate, many proofs. Trait-based on-chain verifier library for Solana supporting Groth16, PLONK, FRI-STARK, and Nova.
Mosaic
Proof-system-agnostic on-chain verification for Solana.
One API. Multiple proving systems. No Groth16 wrapping required.
The Solana ecosystem has exactly one production-grade ZK verifier today (Light Protocol's groth16-solana). Every other proof system โ PLONK, HyperPlonk, Halo2-KZG, FRI-STARK, Risc0, Nova, ProtoStar โ either requires awkward Groth16 wrapping (see Bonsol/Anagram's Risc0-in-Circom workaround) or cannot be verified on Solana L1 at all.
Mosaic fixes that. Pick a proving system via a generic parameter; swap systems without touching program logic.
use mosaic_core::{ProofSystem, ProofSystemId};
use mosaic_groth16::Groth16Verifier;
// (Future: use mosaic_plonk::PlonkKzgBn254;)
let backend = mosaic_core::syscall::host::HostBackend::new(); let verifier = Groth16Verifier::<_, false>::new(&backend);
verifier.verify(&vkbytes, &proofbytes, &publicinputsbytes)?; // ^^ same call shape for every supported proving system
Status
Phase-3 bodies (this release): all four Phase-3 verifier pipelines now run end-to-end and return Ok(()) on structurally well-formed proofs. Scaffold caveats per family are documented for fixture-driven tightening in upcoming 0.4.x releases. Phase-2 production verifiers (Groth16, PLONK) remain unchanged at their frozen CU budgets.
| Component | Status | On-chain CU | |---|---|---| | mosaic-core (traits, errors, syscall abstraction) | โ
Production | โ | | mosaic-groth16 single verify | โ
Production | 83,574 | | mosaic-groth16 batch verify (N=5, Bowe-Gabizon) | โ
Production | 258,397 (52K/proof, -38%) | | mosaic-plonk KZG-PLONK BN254 verifier | โ
Production | 968,457 | | mosaic-hyperplonk KZG BN254 verifier | ๐ Phase-3 body (structural, scaffold caveats) | target ~505K | | mosaic-halo2 KZG BN254 verifier (PSE fork) | ๐ Phase-3 body (structural, scaffold caveats) | target ~580K | | mosaic-stark FRI-STARK (Goldilocks / BabyBear / Mersenne31) | ๐ Phase-3 body (structural, scaffold caveats) | target ~9.4M | | mosaic-nova Nova / HyperNova / ProtoStar folding | ๐ Phase-3 body (structural, scaffold caveats) | target ~885K | | mosaic-serde snarkjs adapter (Groth16 + PLONK) | โ
Production | โ | | mosaic-serde arkworks adapter | โ
Production | โ | | mosaic-serde gnark / halo2 / plonky3 / risc0 | ๐ง Stub (Phase 3) | โ | | mosaic-chunked data model | โ
Implemented | โ | | mosaic-chunked instruction handlers | โ
Production | โ | | Reference Solana program | โ
319 KB SBF ELF (30.4% of 1 MB cap; 12 cryptographic gates wired) | โ | | Differential test harness (arkworks + snarkjs fixture) | โ
Production (Groth16 + PLONK; Phase-3 extension tracked) | โ | | Property-test coverage (proptest, sessions 36-72) | โ
549 lib tests across 12 crates (+152 proptest + 9 shared primitives lifted in audit-coverage sweep) | โ | | Audit runbook | โ
docs/audit-coverage-runbook.md โ reproduce + extend recipes for external review firms | โ | | BPF CU regression bench (bpf-bench) | โ
7 systems: Groth16 (single + batch), KZG-PLONK, HyperPlonk, Halo2, Nova, FRI-STARK; Nova/FriStark dispatch byte mismatch fixed in v0.9.12 | โ | | Host criterion bench (wall-clock baseline) | โ
5 systems: Groth16, HyperPlonk, Halo2, Nova, FRI-STARK | โ | | Fuzz harnesses (sessions 54-59 / 109-114) | โ
37 targets including 10 compression harnesses (Phase-2: Halo2/Groth16/PLONK; Phase-3: HyperPlonk/Nova) | โ | | Compression infrastructure (session 114) | โ
Every BN254 verifier (5/5): proof + VK round-trip APIs, 59 round-trip tests, 4 fuzz + 4 criterion benches | โ | | On-chain compressed-verify (session 116) | โ
VerifyCompressedProof = 0x03 instruction โ 5/5 BN254 verifiers callable via compressed wire format; 50 % bandwidth saving for Groth16, 40-42 % for PLONK/Nova | โ | | SBF integration tests (sessions 113 + 116) | โ
13 tests across all 8 declared ProofSystemId bytes + 1 alias + 1 unknown-byte negative + 3 compressed-path tests | โ | | Audit-firm handoff doc (session 115) | โ
AUDIT-CHECKLIST.md โ crate-by-crate scope/non-scope + reproducibility recipe + open-questions list | โ | | External audit | ๐ด Not yet commissioned (AUDIT-CHECKLIST.md ready to send for scoping quote) | โ |
See AUDIT.md for audit history and SECURITY.md for the responsible-disclosure policy.
Quick start
Add to your program
[dependencies]
mosaic-core = { version = "0.1", features = ["solana"] }
mosaic-groth16 = { version = "0.1", features = ["solana"] }
solana-program = "2.1"
Verify a Groth16 proof on-chain
use mosaic_core::{
proof_system::{ProofSystem, ProofSystemId},
syscall::solana::SolanaSyscallBackend,
};
use mosaic_groth16::Groth16Verifier;
pub fn process_instruction( programid: &Pubkey, _accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { let backend = SolanaSyscallBackend::new(); let verifier = Groth16Verifier::<_, false>::new(&backend); let (vk, rest) = decodelp(instructiondata)?; let (proof, pibytes) = decodelp(rest)?; verifier.verify(vk, proof, pibytes).maperr(Into::into) }
Set the compute budget on the client
use solanasdk::computebudget::ComputeBudgetInstruction;
let cuix = ComputeBudgetInstruction::setcomputeunitlimit(200_000); let verifyix = mosaicsdk::buildverifyproof_ix(&request)?;
transaction.add(&cu_ix); transaction.add(&verify_ix);
See docs/compute-unit-budget.md for per-system targets.
Generate a proof off-chain (snarkjs โ Mosaic)
use mosaic_serde::snarkjs::SnarkjsCodec;
let bundle = SnarkjsCodec::decodebundle(&proofjson, &vkjson, &publicinputs_json)?; // bundle.vk, bundle.proof, bundle.public_inputs are now canonical bytes
Pre-flight verification (catch bugs before on-chain)
mosaic_sdk::preflight(&request)?;
// Runs the same verifier locally with arkworks. Fails fast.
Workspace topology
mosaic/
โโโ crates/
โ โโโ mosaic-core/ # ProofSystem, SyscallBackend, errors
โ โโโ mosaic-groth16/ # BN254 Groth16 verifier
โ โโโ mosaic-plonk/ # KZG-PLONK (Phase 2 stub)
โ โโโ mosaic-stark/ # FRI-STARK (Phase 3 stub)
โ โโโ mosaic-nova/ # Folding scheme (Phase 3 stub)
โ โโโ mosaic-serde/ # snarkjs / arkworks / gnark / halo2 / plonky3 / risc0 adapters
โ โโโ mosaic-chunked/ # Large-proof upload protocol
โ โโโ mosaic-program/ # Reference Solana on-chain program
โ โโโ mosaic-sdk/ # Client-side Rust SDK
โ โโโ mosaic-bench/ # Criterion + bpf-bench
โ โโโ mosaic-fuzz/ # libFuzzer harnesses
โโโ docs/
โ โโโ adr/ # Architecture decision records (5 ADRs)
โ โโโ threat-model.md
โ โโโ compute-unit-budget.md
โโโ tests/
โโโ differential/ # arkworks vs Mosaic oracle tests
โโโ integration/ # On-chain devnet tests (Phase 2)
โโโ fixtures/ # Sample proofs from each framework
Design principles
- Object-safe
ProofSystemtrait. Single byte-slice API; the on-chain
match, the SDK uses Box<dyn ProofSystem>.
- Two-layer error model.
OnChainError(deterministic, repr-u32) for
DiagnosticError (rich, feature-gated) for off-chain. See
ADR-0002 and SIMD-0129.
- Syscall abstraction.
SyscallBackendlets host tests use arkworks
- No hand-rolled cryptography. Verifier crates depend on
ark-bn254
- Forward-compatible byte layout.
LE_INPUTSconst generic +FormatTag
MSRV
Rust 1.85.0 โ host workspace (cargo, clippy, rustfmt, tests). Rust 1.89.0-dev โ Solana SBF target via cargo build-sbf --tools-version v1.52.
The default cargo-build-sbf (platform-tools v1.51, rustc 1.84.1) can't parse edition2024 in some transitive dependencies (constanttimeeq via blake3). We therefore pin --tools-version v1.52 in CI and recommend the same locally.
Security
- Phase-1 scope freeze:
v0.1.0-phase1.
- Phase-2 scope freeze:
v0.2.0-phase2.
- Phase-3 scaffold freeze:
v0.3.0-phase3-scaffolds.
- Phase-3 bodies freeze:
v0.4.0-phase3-bodies.
- Phase-3 soundness release:
v0.4.1-phase3-soundness.
opt-level = "z"; mosaic-zk-primitives crate
extracted.
- Phase-3 complete release:
v0.5.0-phase3-complete.
- Phase-3 extended release:
v0.6.0-phase3-extended.
(1, 2, 3) into the VK. CU baselines re-measured
under the opt-level = "z" profile (PLONK cap 800K โ 1.1M).
- Phase-3 primitives release:
v0.7.0-phase3-primitives.
mosaic-zk-primitives from
inlined duplication across Halo2 / HyperPlonk / Nova verifiers โ
frfrombebytesreduced, derivefrchallenge, frbefrom_u64,
verifytwopairpairing, commitmentminusscalarg1. Nova
proof canonical gains a dedicated w_eval slot (session 23),
replacing the scaffold reuse of public_inputs[0]. 14 new unit
tests cover the lifted primitives.
- Phase-3 polish release:
v0.8.0-phase3-polish.
computekzgopening_lhs consolidates the full
A = C โ yยทG1 + ฮพยทW pairing-LHS construction across every KZG
verifier. HyperPlonk univariate opening point now binds the
FULL sumcheck challenge vector (not just the trailing challenge).
ProofSystem::estimatedcomputeunits turns shape-aware for the
three Phase-3 bodies (halo2/hyperplonk/nova). mosaic-sdk gains
five opt-in Cargo features (plonk, hyperplonk, halo2,
nova, stark, plus all-phase3) so client preflight covers
every proof system. Cast-safe borrow-chain rewrite eliminates
four as u8 truncation warnings in fr::subr + msm::negateg1.
+27 proptest tests (zk-primitives 51โ64, stark 103โ117) plus
clippy cleanup + complete # Errors rustdoc for zk-primitives.
- Audit-coverage release:
v0.8.1-audit-coverage.
| Crate | ฮ | Total | |---|---:|---:| | mosaic-halo2 | +16 | 75 | | mosaic-hyperplonk | +17 | 82 | | mosaic-nova | +14 | 59 | | mosaic-plonk | +15 | 32 | | mosaic-groth16 | +15 | 26 | | mosaic-serde | +9 | 12 | | mosaic-chunked | +11 | 20 | | mosaic-sdk | +7 | 11 | | mosaic-program | +7 | 7 |
Property categories: canonical byte layout invariants, full Fiat-Shamir round-by-round avalanche (Halo2 4-round, HyperPlonk 3-round, Nova 3-round, PLONK 6-round including the snarkjs- compatibility "u-absorbs-only-Wxi-and-Wxiฯ" bit), single-byte tamper rejection over commit and opening regions, state-machine monotonicity (chunked upload session), Borsh wire-format round- trip (instruction payloads), BE-comparison + Fr arithmetic invariants, snarkjs adapter c1โc0 ordering and decimaltobe_32 range envelope, builder/setter independence, instruction-tag dispatch routing.
Three false positives surfaced and were documented inline: the Halo2 verifier random-byte-flip test required scope-narrowing away from selector slots that interact with the dummy fixture's trivially-zero wires; the HyperPlonk verifier's anchor + XOR tamper pattern was rewritten to direct proof[off] = new_val after bit_mask = anchor cancellation was caught by proptest shrinking; the same byte-anchor pattern was avoided across the rest of the sweep.
Only fixture-driven differential testing remains before external audit engagement (tracked: extending tests/differential from Groth16 + PLONK to all four Phase-3 bodies).
- Vulnerability reports: see SECURITY.md and the
- Audit history: see AUDIT.md.
- Threat model: see docs/threat-model.md.
- Supply chain:
cargo-deny+cargo-auditrun in CI;cargo-vet
supply-chain/. - Lint policy:
docs/lint-policy.mdis the
- Changelog: CHANGELOG.md.
License
Dual-licensed under either of:
at your option. SPDX:Apache-2.0 OR MIT.
Contributing
See CONTRIBUTING.md. All contributions are welcomed under the dual-license terms above.