Skip to main content

Zero-Knowledge Proofs

Orbinum uses Groth16 zero-knowledge proofs over the BN254 curve to let users prove ownership, transaction validity, and identity claims without revealing private data. Proofs are generated client-side, submitted on-chain, and verified in ~15ms by pallet-zk-verifier.

This page covers the cryptographic foundations: proof system, hash function, circuit designs, and the end-to-end proof flow.


Proof System: Groth16 over BN254

Why Groth16?

Constant proof size — Always 128 bytes, no matter how complex your transaction
Lightning-fast verification — Only ~15ms on-chain using 3 pairing operations
Battle-tested tooling — Used by Zcash, Tornado Cash, and many DeFi protocols
Native verification — Proofs are verified in the runtime by pallet-zk-verifier, cheaper than an in-EVM pairing check

Why BN254 Curve?

The BN254 (also called alt_bn128) is an elliptic curve specifically designed for efficient pairing operations:

254-bit prime field — Large enough for security, small enough for speed
~100 bits of security — Industry-standard security level
Efficient pairings — Critical for fast proof verification
Wide ecosystem support — Ethereum, Substrate, and most ZK frameworks

Cryptographic Primitives

Poseidon Hash Function

Poseidon is the primary hash function used by all Orbinum circuits. It is specifically designed for ZK arithmetic: where SHA-256 costs ~25,000 R1CS constraints, Poseidon costs ~300 — an 80× reduction in proving time.

// From primitives/zk-core
pub fn poseidon_hash_2(left: [u8; 32], right: [u8; 32]) -> [u8; 32]
pub fn poseidon_hash_4(inputs: [[u8; 32]; 4]) -> [u8; 32]

Constraint cost comparison

Hash FunctionR1CS ConstraintsZK-FriendlyStandardized
Poseidon~300✅ Yes✅ Yes
SHA-256~25,000❌ No✅ Yes
Pedersen~750✅ Yes⚠️ No

How We Use Poseidon

Poseidon appears in three places, all defined in Notes & the Merkle Forest:

commitment = Poseidon4(value, asset_id, owner_pk_ax, blinding)
nullifier = Poseidon(commitment, spending_key)
parent = Poseidon(left_child, right_child)

The first two make a note opaque and its spend unlinkable; the third builds the tree that proves membership without revealing which leaf.

Circuits

Each circuit defines the constraints that constitute a valid operation. The Groth16 prover runs these constraints offline; the on-chain verifier checks the resulting proof in ~15ms regardless of constraint count.

The Transfer Circuit

transfer.circom is the largest of the circuits. Its full signal list lives in the Transfer Circuit reference; what follows is what it actually enforces.

The 4 Critical Constraints

1
Input notes exist in the Merkle tree

merkle_verify(commitment_a, path_a, root) == 1 merkle_verify(commitment_b, path_b, root) == 1

2
Nullifiers are computed correctly

nullifier_a == poseidon(commitment_a, sk) nullifier_b == poseidon(commitment_b, sk)

3
Value is conserved (inputs cover outputs and fee)

input_values[0] + input_values[1] == output_values[0] + output_values[1] + fee

The fee is a public signal committed to at proof-generation time. It is credited as a private balance inside the shielded pool, to whoever the dispatch origin names — see who receives the fee.

4
Output commitments are valid

commitments[0] == Poseidon4(output_values[0], asset_id, output_owners[0], output_blindings[0]) commitments[1] == Poseidon4(output_values[1], asset_id, output_owners[1], output_blindings[1])

Circuit Complexity

Different operations require different circuit sizes:

CircuitR1CS ConstraintsProving TimeProof SizeUse Case
Transfer33,687~2–3s128 bytesPrivate transfer (2 in → 2 out) with gasless fee
Unshield16,903~750ms128 bytesWithdraw to public, total or partial, with gasless fee
Value Proof~300<50ms128 bytesValidator claiming relay fees

Proving times are measured on a local development machine and scale with the prover's hardware. Verification is ~15ms for transfer and unshield, <5ms for the value proof — independent of constraint count, which is the property that makes Groth16 practical on-chain.

Shield needs no proof

Depositing into the pool is a public operation — the amount and sender are visible on-chain anyway — so shield submits no ZK proof and has no verification key.

Understanding Constraints

Each constraint is a mathematical equation that must hold true. More constraints = larger circuit = longer proving time. But verification time stays constant (~15ms) thanks to Groth16!


Proof Flow

Proving happens entirely in your browser — the witness never leaves the device.

  1. Gather the witness. Your notes, their Merkle paths, and your spending key.
  2. Generate the proof against the circuit's proving key. A few seconds of browser CPU.
  3. Submit the proof plus its public signals. The runtime verifies in ~15 ms and records the nullifier and new commitments.

For the user-facing version of this, see Private Transfer. For the exact signals, see the Transfer Circuit.


Trusted Setup

Groth16 requires a trusted setup ceremony to generate the proving and verification keys. This is a one-time process per circuit. The output is:

  • A proving key (.zkey / .ark) — used client-side to generate proofs
  • A verification key — deployed on-chain into pallet-zk-verifier

Ceremony Phases

1
Phase 1: Powers of Tau

Multiple participants contribute randomness. Each adds their secret and passes it to the next person. This creates a large file of random values (~10-50 GB).

2
Phase 2: Circuit-Specific Setup

Using the Powers of Tau, we generate proving keys (.zkey) and verification keys for each circuit (transfer, unshield, value proof).

3
Destroy the "Toxic Waste"

All participants must permanently delete their random secrets. These secrets are called "toxic waste" because they could be used to create fake proofs.

The 1-of-N Trust Assumption

The beautiful property of trusted setup ceremonies:

As long as ONE participant is honest and destroys their secret, the setup is secure.

Even if 99 out of 100 participants collude or leak their secrets, if just ONE person follows the protocol correctly, the entire system remains secure.

Current Status

MVP Status

The current proving/verification keys in /artifacts are for testing only. They were generated in a local, non-production setup.

Before mainnet launch (Q4 2026), we will conduct a public multi-party ceremony with community participation and full transparency.


This page covers the cryptographic foundations. The following pages explain how this system is deployed and operated on Orbinum: