Skip to content

Groth16, PlonK & BBS

View Markdown

ZeroJ implements three cryptographic systems. Two of them, Groth16 and PlonK, prove statements about circuits. The third, BBS, is a signature scheme for credentials. They solve different problems, and in ZeroJ they have very different maturity. This page explains each one, compares them side by side, and explains why everything runs on the BLS12-381 curve.

Short version: use Groth16 on BLS12-381 for circuits. It’s the focus of the current release and the default throughout these docs. Use BBS when you need issuer-signed credentials with selective disclosure. Treat ZeroJ’s PlonK support as experimental.

Groth16 (Jens Groth, 2016) is the most widely deployed zk-SNARK. It turns an R1CS circuit into proofs that are tiny and cheap to verify.

  • Proof: three elliptic-curve points: two in the group G1 and one in G2. On BLS12-381 that’s 48 + 96 + 48 = 192 bytes in compressed form, whatever the size of the circuit. (The same proof as snarkjs-style JSON text is about 1 KB.)
  • Verification: a single pairing check, plus one elliptic-curve multiplication per public input. On Cardano, ZeroJ’s reusable verifier does four Miller loops and one final check using Plutus V3 builtins. In ZeroJ’s JuLC VM tests, a proof with two public inputs used about 2.8 billion CPU units, against a per-transaction limit of 10 billion.
  • Setup: Groth16 needs a trusted setup for each circuit. A universal “powers of tau” phase is reused, and a circuit-specific phase 2 runs for every circuit and every change to it. See Trusted setup, explained.

The per-circuit setup is Groth16’s main cost. In exchange you get the smallest proofs and the cheapest verification of the three, which is exactly what you want when a blockchain validator pays for every CPU cycle.

In ZeroJ, Groth16 on BLS12-381 is Beta off-chain and Beta, testnet only on-chain. You get a pure-Java prover, a pure-Java verifier, a reusable Plutus V3 verifier, import of snarkjs .zkey ceremony keys, and export to snarkjs-compatible JSON.

PlonK: universal setup (experimental in ZeroJ)

Section titled “PlonK: universal setup (experimental in ZeroJ)”

PlonK (Gabizon, Williamson, and Ciobotaru, 2019) uses a different circuit format, built from gates and wiring permutations instead of R1CS, and a polynomial commitment scheme called KZG.

  • Setup: one universal trusted setup (a KZG structured reference string, or SRS) serves every circuit up to a maximum size. Each circuit still needs preprocessing, but that step is public and deterministic, so changing a circuit doesn’t need a new ceremony.
  • Proof: larger than Groth16, about 650 bytes (roughly 3.5 times as large).
  • Verification: two pairings plus more scalar multiplications than Groth16. ZeroJ’s experimental on-chain verifier measured about 4.8 billion CPU units for one public input.
  • Non-interactivity comes from Fiat–Shamir: the verifier’s challenges are derived by hashing the proof transcript, so transcript encoding details are security-critical.

BBS works differently from the other two. It isn’t a circuit proof system; it’s a signature scheme with a built-in zero-knowledge proof. The workflow has three parties:

Issuer Holder Verifier
│ signs attributes │ │
│ [name, birth date, │ │
│ country, KYC level, …] │ │
├─────── signature ───────►│ │
│ │ derives a proof revealing only │
│ │ [country, KYC level] │
│ ├──────── presentation ─────────────►│
│ │ │ checks against the
│ │ │ issuer's public key
  • The issuer signs a list of messages (attributes) with one signature.
  • The holder derives a presentation: a zero-knowledge proof that they hold a valid signature over all the attributes, revealing only the ones they choose.
  • The verifier checks the presentation against the issuer’s public key. Hidden attributes stay hidden, and each presentation is freshly randomized, so two presentations of the same credential aren’t linkable by their bytes.

No trusted setup. The issuer just generates a key pair.

Proof size grows with what you hide: a presentation proof is 272 bytes plus 32 bytes per hidden attribute.

Limitation: BBS can reveal or hide attributes, but it can’t prove a predicate about a hidden attribute, such as “birth date is before 2007”. For predicates, use a circuit (Groth16), possibly alongside BBS.

ZeroJ implements the IRTF CFRG draft draft-irtf-cfrg-bbs-signatures-10 with both of its BLS12-381 ciphersuites (SHA-256 and SHAKE-256), and tests against the draft’s official fixtures. The draft isn’t an RFC yet, so the scheme itself may still change. In ZeroJ, BBS verification is Beta, and issuance and proof generation are Beta with a caveat: the default pure-Java provider isn’t constant-time, so prefer the blst provider for issuer keys. There is also a fixed-profile on-chain presentation verifier. See BBS credentials.

Groth16PlonKBBS
What it provesAny statement you express as an R1CS circuitAny statement you express as a PlonK circuit“I hold an issuer-signed credential; here are the attributes I choose to reveal”
SetupTrusted, per circuit (universal phase 1 + circuit-specific phase 2)Trusted, universal SRS, reused across circuitsNone beyond the issuer’s key pair
Proof size (BLS12-381)192 bytesabout 650 bytes272 bytes + 32 per hidden attribute
Verification costLowest: one pairing check + one scalar multiplication per public inputHigher: two pairings + more scalar multiplicationsPairing-based; grows with the number of attributes
Changing the circuitNew phase-2 ceremonyRecompute public preprocessingNot applicable
Status in ZeroJBeta off-chain; Beta, testnet only on-chainExperimental, off-chain and on-chainVerification Beta; issuance Beta with caveat; on-chain verifier is fixed-profile
When to useDefault for every circuit, especially on CardanoEvaluation and research onlyCredentials with selective disclosure

Pairing-based proof systems need a pairing-friendly elliptic curve. Two curves dominate the ecosystem:

  • BN254 (also called alt_bn128) is what Ethereum’s precompiles support, so much existing tooling, including default circom and snarkjs setups, targets it.
  • BLS12-381 was designed later with a higher security margin. It’s used by Zcash, Ethereum’s consensus layer, and many signature schemes.

For Cardano the choice is simple. Plutus V3, introduced in the Conway era, added native BLS12-381 builtins, specified in CIP-0381: curve point operations, Miller loops, and a final pairing check. There are no BN254 builtins, so a BN254 proof can’t be verified on-chain at any reasonable cost.

That’s why ZeroJ uses BLS12-381 everywhere by default. Circuits compile over the BLS12-381 scalar field (CurveId.BLS12_381), and hash gadgets must use BLS12-381 parameters, such as Poseidon with PoseidonParamsBLS12_381T3.INSTANCE. ZeroJ still contains legacy BN254 code, but it’s disabled by default and needs -Dzeroj.allowLegacyBn254=true. Use it only for off-chain experiments.

  • Proving a statement about private data? Groth16 on BLS12-381. Plan for a proper ceremony before anything real depends on it.
  • Issuing credentials that holders reveal piece by piece? BBS.
  • Need a predicate over a credential attribute? A Groth16 circuit, possibly combined with BBS or another way of binding the attribute to an issuer.
  • Curious about universal setups? Try PlonK for research, and keep it away from anything that matters.