ZKF: 59,000 Lines of Rust, 9 Proof Systems, and the End of ZK Lock-In
ZKF is a universal zero-knowledge proof framework -- 59,000 lines of production Rust -- that compiles any frontend language to any backend proving system. 9 backends, 7 frontends, 11 gadgets, STARK-to-SNARK wrapping, and Solidity verifier generation. Open-sourcing crate by crate starting April 2026.
The Problem Everyone Pretends Doesn't Exist
Zero-knowledge proof systems do not talk to each other.
Noir compiles to ACIR. Circom emits R1CS. Cairo targets Sierra. Plonky3 speaks AIR. Halo2 demands PLONKish gates. Each proof system has its own circuit language, its own constraint format, its own finite field, its own witness pipeline, and its own toolchain. If you pick one, you are locked in. If you want to compare two systems on the same circuit, you rewrite everything from scratch.
This is not a minor inconvenience. It is a structural tax on the entire ZK ecosystem. Teams cannot benchmark proof systems against each other without months of porting work. A Noir developer cannot deploy a Groth16 proof on Ethereum without leaving the Noir ecosystem entirely. A protocol that starts with Halo2 cannot test whether Plonky3 would give them 10x faster proving without rebuilding their circuit from the ground up. Researchers comparing proof systems across papers are comparing apples to submarines.
We built the bridge. And then we stress-tested it until it broke, fixed what broke, and proved it works.
ZKF is a universal zero-knowledge proof framework -- 59,000 lines of production Rust across 9 interconnected crates -- that compiles any frontend language to any backend proving system through a shared intermediate representation. One circuit definition. Nine proof systems. Seven input languages. No rewrites. No lock-in. No compromise.
What ZKF Actually Is
ZKF is a three-layer compiler and proving infrastructure:
Layer 1: Frontends (7 input languages)
──────────────────────────────────────
Noir (ACIR 0.46) | Circom (R1CS) | Cairo (Sierra)
Midnight Compact | Halo2-Rust | Plonky3-AIR | zkVM (SP1/RISC Zero)
│
│ compile_to_ir()
▼
Layer 2: Universal IR + Tooling
──────────────────────────────────────
IR v2 (minimal) ←→ ZIR v1 (rich constraints)
Optimizer | Debugger | Solver | Witness Generator
11 Built-in Gadgets | Package System
│
│ compile() → prove() → verify()
▼
Layer 3: Backends (9 proof systems)
──────────────────────────────────────
Groth16 (BN254) | Halo2 (Pasta) | Halo2 (BLS12-381)
Plonky3 (Goldilocks/BabyBear/Mersenne31)
Nova (IVC) | HyperNova (Multifolding)
SP1 | RISC Zero | Midnight
│
▼
STARK→SNARK Wrapping | Solidity Verifier Generation
Proof Composition | Aggregation | FoldingEvery frontend implements one trait. Every backend implements one trait. The IR sits between them as the contract. If your frontend can emit it and your backend can consume it, they compose. No coordination required.
Nine Backends, Five Finite Fields
ZKF does not abstract away the differences between proof systems. It respects them. Each backend has different strengths, and ZKF lets you choose the right one for your use case while keeping the same circuit definition.
1. Arkworks Groth16 (BN254)
The gold standard for on-chain verification. 128-byte proofs. Sub-millisecond verification. ZKF's Groth16 backend uses deterministic setup -- the trusted setup seed is derived from SHA-256 of the program digest, making the ceremony reproducible across runs. Setup artifacts are cached by program digest to avoid redundant computation. This is the backend you use when your proof needs to verify inside an Ethereum smart contract at ~200K gas.
2. Halo2 (IPA + Pasta)
Transparent setup -- no trusted ceremony. PLONKish constraint system with advice columns, instance columns, selectors, and lookup tables up to 12-bit range checks. ZKF handles automatic k parameter estimation, VK fingerprint caching, and Blake2b transcript generation. Recursion-ready.
3. Halo2 (BLS12-381)
Same PLONKish constraint system, different curve. For applications that need BLS-compatible proofs or interoperability with protocols on the BLS12-381 scalar field.
4. Plonky3 (STARK + FRI)
The fastest prover in production. Fully transparent, post-quantum secure. ZKF supports three field profiles:
- Goldilocks -- 64-bit prime (2^64 - 2^32 + 1), optimal for recursive STARKs, native Poseidon2 hashing
- BabyBear -- 32-bit prime, fastest field arithmetic on modern CPUs
- Mersenne31 -- 31-bit prime, optimal for Circle PCS
Plonky3 proofs are larger than Groth16 (kilobytes vs. bytes) but require zero trust assumptions. And if you need on-chain verification, the STARK-to-SNARK wrapper handles that.
5. Nova (IVC -- Incremental Verifiable Computation)
For computations that unfold over steps. Nova folds R1CS instances incrementally using the Pallas-Vesta curve cycle, producing a single proof that an entire chain of computations was executed correctly. ZKF includes a vendored, patched Nova library (31,635 lines) with CCS support for HyperNova multifolding.
6. HyperNova (Multifolding over CCS)
Nova's successor. Instead of R1CS, HyperNova folds over Customizable Constraint Systems -- a generalization that subsumes R1CS, PLONKish, and AIR. This means lookup tables and custom gates survive the folding process. ZKF is one of the first frameworks to integrate HyperNova with a production IR.
7. SP1 (Succinct Labs)
Write your computation in Rust, compile to RISC-V, prove execution in zero knowledge. ZKF's SP1 backend handles the full guest ELF compilation and caching pipeline, with CPU and mock proving modes, deterministic seed generation, and external prover fallback. Solidity verifier generation included.
8. RISC Zero
RISC Zero's zkVM integration with receipt verification and Bonsai remote proving fallback. Proof delegation for heavy computations.
9. Midnight Network
Native proof-server runtime and client SDK. ZKF can generate Compact source code from any circuit's IR, meaning any circuit written in any frontend can be deployed on Midnight Network. Bidirectional: import Compact circuits in, or generate Compact code out. 1,200 lines of Midnight integration across native runtime, client SDK, and transaction routing.
The IR: Two Levels of Abstraction
Different backends need different things. An R1CS-based system like Groth16 only needs equations of the form A * B = C. A PLONKish system like Halo2 needs lookup tables, custom gates, and permutation arguments. Forcing everything into the lowest common denominator wastes the power of advanced backends. Exposing everything to simple backends creates impossible lowering problems.
ZKF solves this with two IR levels:
IR v2 is minimal. Four constraint types: Equal, Boolean, Range, and BlackBox. Every frontend can target it. Every backend can consume it. This is the universal denominator.
ZIR v1 is rich. It adds lookup tables with polynomial constraints, custom gate definitions, memory regions (read-only and read-write), permutation arguments, and copy constraints. Backends like Halo2 and Plonky3 that support these features consume ZIR directly through compile_zir(), avoiding the lossy downcast to IR v2.
The key insight: ZIR-native programs compiled through Halo2 keep their lookup tables. The same programs compiled through Groth16 get automatically lowered to R1CS equivalents. No information is lost that the target backend could have used.
Seven Frontends
Noir (ACIR)
The deepest integration. ZKF supports four Noir version families -- beta.9, beta.10, v1 program JSON, and v1 stable -- with automatic version detection via FrontendProbe. The ACIR importer handles bytecode deserialization, opcode translation, constant-lifting for beta.9 compatibility, recursive aggregation markers, and BN254-native blackbox solving (Poseidon, Pedersen, Schnorr, ECDSA, SHA-256, Keccak, Blake2s). The import output maps original parameter names to witness indices, so developers use {"x": "3", "y": "5"} instead of {"w0": "3", "w1": "5"}.
Circom (R1CS)
Circom's R1CS JSON format from snarkjs maps directly to IR v2 constraints. External witness runner integration for deterministic witness generation.
Cairo (Sierra)
Cairo's Sierra intermediate language gets descriptor-driven execution, mapping Sierra libfuncs to IR operations.
Midnight Compact
Bidirectional. Import Compact circuits in. Generate Compact source code out. The codegen handles type inference (Field, Boolean, Bytes, Vector), ledger state declarations, and circuit body generation. Any circuit, any frontend, deployed on Midnight.
Halo2-Rust and Plonky3-AIR Exports
JSON-serialized circuit descriptions from native Halo2 and Plonky3 projects enter the ZKF pipeline without rewriting. Existing circuits gain access to all nine backends.
ZKF DSL
Rust procedural macros for writing circuits directly in Rust with type safety. Private/Public type wrappers, automatic ZIR constraint synthesis, and built-in range/equality assertions.
The STARK-to-SNARK Wrapping Pipeline
This is the hardest piece of engineering in the framework.
Plonky3 produces transparent STARK proofs -- no trusted setup, post-quantum secure. But STARK proofs are large and expensive to verify on-chain. Groth16 produces tiny proofs that verify cheaply on Ethereum, but requires a trusted setup and is not post-quantum secure.
ZKF bridges both worlds. It takes a Plonky3 STARK proof and wraps it inside a Groth16 SNARK. The result is a proof that says "I verified this STARK, and it checked out" -- but the verification itself is a tiny Groth16 proof that costs ~200K gas on Ethereum.
The wrapping pipeline is 3,500+ lines of code across six modules:
- stark_to_groth16.rs (1,755 lines) -- Deserializes Plonky3 STARK proofs, extracts FRI commitments and query openings, builds the FRI verifier as a BN254 R1CS circuit, runs Groth16 circuit-specific setup with caching by program digest
- fri_verifier_circuit.rs (1,074 lines) -- The complete FRI polynomial commitment verification expressed as R1CS constraints. Merkle path verification via Poseidon2, FRI folding consistency checks with division-free encoding, AIR constraint evaluation via Horner folding, Fiat-Shamir transcript replay, quotient polynomial identity checks
- poseidon2_goldilocks.rs (766 lines) -- The exact Poseidon2 permutation implemented as a BN254 gadget. Width 16, round schedule matching Plonky3 bit-for-bit. MDS matrix constants for Goldilocks. Multi-absorption sponge (rate=8, capacity=8) for hashing traces with more than 16 columns
- nonnative_goldilocks.rs (336 lines) -- Non-native field arithmetic: Goldilocks (64-bit prime) operations performed inside BN254 (256-bit prime). Addition, subtraction, and multiplication with quotient-based modular reduction and range checks via bit decomposition
- fri_gadgets.rs (820 lines) -- FRI folding gadgets, Merkle tree verification, challenge generation via DuplexChallenger, query witness extraction
- air_eval_circuit.rs (314 lines) -- AIR constraint evaluation over execution traces with Horner folding to combine constraints and quotient polynomial recomposition
Building a STARK verifier inside a SNARK circuit requires implementing an entirely different field's arithmetic (Goldilocks, 2^64 - 2^32 + 1) using the native field's constraints (BN254, a 254-bit prime). Every Goldilocks multiplication becomes a quotient proof: a * b = q * p + r, where q and r are auxiliary witness values with range checks proving they fit in the right number of bits. The Poseidon2 hash must match Plonky3's implementation exactly -- same MDS matrix, same round constants, same sponge mode -- or the wrapped proof rejects.
This is not something you find in other frameworks. Most projects that claim STARK-to-SNARK wrapping use off-the-shelf recursive verifiers. ZKF's implementation is purpose-built, circuit-level, and verified against live Plonky3 proofs.
The Tooling Layer
Optimizer
Two optimization passes -- one for IR v2, one for ZIR -- that do not lose information. Constant folding on arithmetic expressions. Tautology detection (x = x, 0 + x, x * 1). Duplicate constraint deduplication via BTreeSet. Dead private signal elimination with reference tracking. The ZIR optimizer preserves lookup tables, custom gates, memory regions, and permutation arguments -- no lossy round-trip through IR v2.
Debugger
Step-through constraint debugging with six analysis modes:
- DebugReport: per-constraint trace with detailed failure diagnostics
- SymbolicConstraint: expression structure, dependencies, degree estimates, nonlinearity detection
- SymbolicSignal: origin tracking (Input/Constant/Assignment/Hint), dependency chains, resolution status
- WitnessFlowGraph: DAG of signal dependencies showing how values propagate
- UnderconstrainedAnalysis: detects private signals that are not sufficiently constrained -- a common source of soundness bugs
- ExprTrace: step-by-step expression evaluation for root-cause analysis of constraint failures
Witness Solver
Three solver modes: built-in (iterative dependency resolution with loop detection), ACVM (Noir-native intermediate witness computation), and external command hooks (for Circom's snarkjs). The solver handles constants first, then inputs, then assignments, then hints, with completeness checks ensuring every non-constant signal is resolved before proving.
11 Built-in Gadgets
Reusable circuit components that emit ZIR signals, constraints, and assignments:
- BooleanGadget -- bit-constraint enforcement: x * (x - 1) = 0
- RangeGadget -- bit-range constraints via decomposition
- ComparisonGadget -- less-than, greater-than via bit decomposition
- PoseidonGadget -- 2-to-1 and 3-to-1 hash modes plus full permutation
- MerkleGadget -- membership proofs with tree traversal and hash chain verification
- SHA-256Gadget -- bit-level constraint circuit for SHA-256
- ECDSAGadget -- NIST curve signature verification
- SchnorrGadget -- Schnorr signature verification
- Secp256k1Gadget -- Secp256k1 curve operations
- NonNativeGadget -- field extension arithmetic for cross-field operations
- LookupGadget -- precomputed table constraints for efficient range checks
Every gadget plugs into the GadgetRegistry with builtins pre-loaded. Backends that support lookup tables get efficient table-based implementations. Backends that don't get equivalent constraint expansions. Same gadget, different compilation strategy, same correctness.
20+ CLI Commands
Every operation in ZKF is exposed through the CLI and the library API. The complete command set:
zkf import Import from Noir/Circom/Cairo → ZKF IR
zkf inspect Analyze frontend program structure before import
zkf compile IR → compiled backend artifacts (setup + keys)
zkf witness Generate witness from inputs (with solver selection)
zkf prove Full pipeline: compile + witness + proof generation
zkf verify Verify proof against program
zkf wrap Wrap Plonky3 STARK in Groth16 SNARK
zkf deploy Generate Solidity verifier contract
zkf debug Constraint-level trace + underconstrained analysis
zkf optimize Run optimization passes with impact report
zkf run Execute program with run reports
zkf benchmark Cross-backend apples-to-apples benchmarking
zkf estimate-gas Gas cost estimates for on-chain verification
zkf capabilities List all frontend/backend capabilities
zkf frontends Show frontend support matrix
zkf doctor Check tool requirements (nargo, circom versions)
zkf emit-example Generate canonical test circuits
zkf test-vectors Run test vectors across multiple backends
Package subcommands:
zkf package compile Multi-backend manifest compilation
zkf package prove Backend-specific proving
zkf package verify Packaged proof verification
zkf package compose Recursive proof composition
zkf package aggregate Batch multiple proofs into one
zkf package fold Nova/HyperNova incremental foldingSolidity Verifier Generation
ZKF generates production Solidity contracts for on-chain verification. The Groth16 verifier uses EVM precompiles for elliptic curve operations -- ecAdd (0x06), ecMul (0x07), and the pairing check (0x08). The verification key is hardcoded into the contract with alpha, beta, gamma, and delta curve points plus the input commitment array. A single public function -- verifyProof() -- accepts the proof and public inputs, runs the pairing check, and returns true or false.
The SP1 verifier uses a bound verifier pattern: a lightweight contract that delegates to a deployed SP1 verifier oracle with program-specific verification key binding.
Both verifiers are generated directly from proof artifacts. No manual contract writing. No copy-paste of curve points.
Stress-Tested: 27+ Prove-Verify Cycles, 6 Bugs Found and Fixed
We did not release ZKF and hope it works. We ran five comprehensive end-to-end test suites across the entire framework and published every result, including the bugs.
Test 1: Noir Import + Three-Backend Proving
Program: x * x + y * x + 5 == expected. Compiled with nargo 1.0.0-beta.9 (ACIR 0.46). Imported into ZKF IR: 3 signals (2 private, 1 public), 1 constraint. Proved and verified on Groth16 (BN254), Halo2 (Pasta-Fp), and Plonky3 (Goldilocks). All pass.
Bug found: BN254's representation of -1 (the field prime minus one, a 77-digit number) was passed unchanged to Pasta-Fp during cross-field compilation. Pasta has a larger prime, so the value was interpreted as a large positive number instead of negative one. The fix: acir_field_to_ir() now interprets BN254 values greater than p/2 as negative, then re-encodes for the target field.
Test 2: Circom R1CS Import + Debugger
Pythagorean triple verifier: a^2 + b^2 == c^2. Imported from snarkjs R1CS JSON. The debugger correctly catches wrong inputs -- feeding (3, 4, 6) produces "debug: FAILED at constraint 0" because 9 + 16 = 25, not 36. With correct inputs (3, 4, 5), all three backends pass.
Test 3: MiMC Hash Chain -- Source to On-Chain
The complete pipeline. A 5-round MiMC x^5 S-box hash chain: 20 signals (19 private, 1 public), 16 constraints. Noir source code compiled with nargo, imported as ACIR, proved on all three backends, the Plonky3 STARK wrapped into a Groth16 SNARK, and a Solidity verifier contract generated. Ten pipeline stages. All passed.
Two bugs found in the wrapping pipeline:
extract_fri_params()was guessing FRI parameters from proof byte size instead of deserializing the actual Plonky3 proof. It assumedlog_degree=10; the real proof haddegree_bits=0.- The Poseidon2
hash_leaffunction panicked on circuits with more than 16 trace columns. MiMC has 20. The sponge was hard-coded for single absorption. The fix: implement Plonky3's exactPaddingFreeSponge<16, 8, 8>multi-absorption pattern, where partial final chunks do not zero remaining rate positions.
Test 4: Cross-Backend Consistency -- 12/12
Program: expected = a * b. Four test vectors: small (3 * 7 = 21), zero (0 * 99 = 0), one (1 * 1 = 1), large (9,999,999 * 9,999,999 = 99,999,980,000,001). Three backends. Twelve prove-verify cycles in a single command. The test-vectors command auto-adapted the program's field per backend: BN254 for Groth16, Pasta-Fp for Halo2, Goldilocks for Plonky3. All twelve passed. The 14-digit product verified correctly across three different finite fields.
Test 5: Unit Test Suite
Full cargo test across all workspace crates. zkf-core: optimizer, witness 7/7, debugger, solver, ACVM beta.9 adapter, package serialization -- all passed. zkf-frontends: 25/25 import tests plus Plonky3 AIR export, Halo2 export, and descriptor frontend tests -- all passed. zkf-backends: all passed (31 compiler warnings, 0 errors). zkf-cli and zkf-examples: all passed.
Three additional bugs found and fixed during development: a duplicate struct update syntax from a batch-fix script, 80+ struct literal compilation errors from adding two new fields to core IR types (fixed systematically across 25+ files), and the witness command ignoring the auto-solver metadata.
Total: 27+ prove-verify cycles. 12/12 cross-backend consistency. 6 bugs found and fixed. Every test green.
By the Numbers
| Metric | Count |
|---|---|
| Lines of Rust | 59,000+ |
| Workspace crates | 9 |
| Proof system backends | 9 |
| Frontend importers | 7 |
| Supported finite fields | 7 (BN254, BLS12-381, Pasta Fp/Fq, Goldilocks, BabyBear, Mersenne31) |
| Built-in gadgets | 11 |
| CLI commands | 20+ |
| Backend-specific lowering modules | 6 |
| STARK-to-SNARK wrapping pipeline | 3,500+ lines |
| Vendored Nova library (patched) | 31,635 lines |
| Test files | 19 |
| Prove-verify cycles tested | 27+ |
| Cross-backend consistency | 12/12 |
| Bugs found during stress testing | 6 (all fixed) |
Open-Source Roadmap: 12 Months, Crate by Crate
Starting in April 2026, we are open-sourcing ZKF incrementally over 12 months. Each month, one crate goes public with full documentation, examples, and test coverage. The schedule:
| Month | Crate | What You Get |
|---|---|---|
| April | zkf-core | IR definitions, field math, witness generation, optimizer, debugger |
| May | zkf-frontends (Noir) | ACIR import for all Noir versions, blackbox solving |
| June | zkf-frontends (Circom) | R1CS import, external witness runner |
| July | zkf-gadgets | 11 reusable circuit components |
| August | zkf-backends (Groth16) | Arkworks backend, deterministic setup, Solidity verifier gen |
| September | zkf-backends (Halo2) | Pasta + BLS12-381 backends, PLONKish lowering |
| October | zkf-backends (Plonky3) | STARK backend, three field profiles, AIR lowering |
| November | zkf-backends (wrapping) | STARK-to-SNARK pipeline, Poseidon2 gadget, non-native arithmetic |
| December | zkf-backends (Nova) | IVC, HyperNova multifolding, patched Nova library |
| January | zkf-cli | Full CLI, package system, benchmarking |
| February | zkf-dsl + zkf-registry | Rust DSL macros, gadget registry |
| March | Full framework | Integration tests, examples, CI pipeline, documentation site |
Free subscribers get each release when it goes public. Pro subscribers get early access -- each crate is available to Pro members one month before public release, with deep-dive articles explaining the architecture, the design decisions, and the proof strategies behind each module. Institutional subscribers get private repository access from day one -- the full 59K-line codebase, all nine backends, updated in real-time as development continues.
Why This Matters
The zero-knowledge ecosystem is converging. Groth16 is not going away -- it is the cheapest on-chain verifier. But STARKs are getting faster (Plonky3 proves in milliseconds), folding schemes are enabling new computation models (Nova, HyperNova), and zkVMs are making general-purpose ZK accessible (SP1, RISC Zero). The question is not which proof system wins. The question is how you use the right one for each job without rebuilding your stack every time.
ZKF is the answer. Write your circuit once. Compile to Groth16 for on-chain deployment, Plonky3 for fast proving, Halo2 for recursion, Nova for incremental computation. Wrap your STARK in a SNARK when you need both transparency and cheap verification. Generate Solidity verifiers without touching Solidity. Benchmark all nine systems against each other on the same circuit with one command.
59,000 lines of Rust. Nine proof systems. Seven frontends. Eleven gadgets. Stress-tested with 27+ prove-verify cycles across three backends, two external languages, and a STARK-to-SNARK wrapping pipeline. Six bugs found, six bugs fixed, twelve out of twelve cross-backend consistency.
We built this because nobody else would. And we are giving it away because the ecosystem needs it.
The first crate drops in April. Subscribe to get it first.