Explainer 03

How a proof is made — and checked

The previous explainer answered what a zero-knowledge proof is and what it guarantees. This one answers the mechanical question people ask next:

"OK, but how does my phone produce that little proof, and how does the blockchain decide it's valid — without ever seeing my passport?"

No heavy math is required to follow this. We build the intuition with an analogy first, then walk the real pipeline on the phone and on-chain.

The core idea: proving you know a secret without revealing it

Imagine a ring-shaped cave with a magic door at the back that only opens with a secret password. You want to prove to a friend that you know the password — without telling them the password.

You walk in and take either the left or right tunnel. Your friend waits at the entrance and cannot see which way you went. They shout "come out the left side!". If you really know the password you can open the door and emerge from whichever side they call, every single time. If you were bluffing, you only have a 50% chance per round. Repeat 40 rounds and the odds of faking it are about one in a trillion. Your friend becomes convinced you know the password — yet never learns what it is.

A zero-knowledge proof is the mathematical version of that cave. The "secret" is your passport data and your private key. Instead of walking left or right, the math forces you to answer unpredictable challenges that you could only answer correctly if your secret genuinely satisfies every rule.

Step 1 — Turn the rules into arithmetic (the circuit)

Before any proof can exist, the rules you want to prove are converted into a giant system of arithmetic equations. Each rule becomes a set of equations:

That whole collection of equations is the circuit. A single passport circuit has hundreds of thousands of them. The key property: the equations are written so they all balance to zero at the same time only if you plugged in genuine, valid data.

Think of it as a giant sudoku. There is exactly one consistent way to fill in every blank, and it only works if the starting numbers — your real passport bytes, your real key — are legitimate.

Step 2 — Fill in the blanks (the witness)

Your phone takes your actual private data (the bytes from the NFC scan, your secret key) and computes every intermediate value in those hundreds of thousands of equations — every multiplication, every hash step, every bit.

This complete set of filled-in values is the witness. It is the full solution to the sudoku. Because it contains your secrets, the witness never leaves your phone.

Step 3 — Compress the witness into a tiny proof

This is the clever part. The phone must convince the verifier it holds a valid witness — without sending the witness.

The trick is polynomials. All the equations get bundled into a small number of mathematical curves (polynomials). A deep theorem makes this work:

If two polynomials are different, they can only agree at a handful of points. So if you check that they match at one single random point and they do, they are almost certainly the same polynomial everywhere.

So the proving process is:

  1. The phone encodes the entire witness into a few polynomials.
  2. It commits to them — producing a few cryptographic fingerprints (points on an elliptic curve). A commitment is like sealing a value in an envelope: you cannot change it later, but it does not reveal what is inside.
  3. A random challenge point is needed to test the polynomials. There is no live verifier standing by, so the phone derives the "random" challenge by hashing its own commitments. This is the Fiat–Shamir trick: a hash of unpredictable data acts as an unriggable coin-flip the prover cannot game.
  4. The phone evaluates the polynomials at that challenge point and packages the answers plus a small "opening proof".

The result is a tiny blob — a few hundred bytes to a couple of kilobytes — no matter how huge the original computation was. That blob is the proof.

The two systems Jomhoor uses do this slightly differently:

SystemUsed forProof sizeSetup
Groth16RSA passports~192 bytes (3 elliptic-curve points)One ceremony per circuit
UltraPlonkINID cards, ECDSA documents~2 KBOne universal setup for all circuits

The native code doing the heavy lifting is rapidsnark (Groth16) or Barretenberg / Swoirenberg (UltraPlonk). This is the step that takes a few seconds on the phone — it is performing thousands of elliptic-curve multiplications.

Step 4 — What actually gets sent

Out of the phone comes only:

Your passport bytes, your secret key, and the full witness all stay on the device.

How the proof is verified

The verifier is the smart contract on the blockchain (for INID voting, that is IDCardVoting.sol). It does not re-run your computation — that is the whole point. Re-running would be impossibly expensive and it doesn't have your data. Instead it does two things.

1. Re-build the expected public signals

The contract independently assembles what the public signals should be. It takes the values you claimed (citizenship, nullifier, …) plus the values it already knows (the proposal's rules, the current date, the on-chain Merkle root) and lays them out into the fixed slots the proof must match. Why claiming a fake value here doesn't help you is covered in on-chain verification.

2. Run the pairing check

The contract feeds the proof blob and those public signals into a short cryptographic test built on a special operation on the BN254 elliptic curve called a pairing, written e(·, ·).

A pairing has a near-magical property: it lets you verify a multiplicative relationship between hidden values while only seeing their sealed-envelope commitments. It is like being able to confirm that "the number in envelope A times the number in envelope B equals the number in envelope C" — without ever opening the envelopes.

For Groth16 the entire validity of your proof comes down to checking that one equation balances:

e(A, B) = e(α, β) · e(public signals, γ) · e(C, δ)

If both sides come out equal, the proof is valid: your witness existed and satisfied every one of those hundreds of thousands of equations. If even one equation in the circuit didn't balance — you lied about citizenship, forged a signature, reused a nullifier — the two sides won't match and the contract reverts.

The Rarimo L2 (like Ethereum) even has built-in precompiles — special cheap operations at addresses 0x07 and 0x08 — specifically for these BN254 pairings, because they are expensive to compute but essential.

UltraPlonk verification is structurally similar: it also ends in a couple of pairing checks, but instead of one fixed equation it confirms that the committed polynomials genuinely open to the claimed values at the random challenge point.

The whole thing in one breath

  1. Rules → a giant system of equations (the circuit).
  2. The phone plugs in your real secret data and solves every equation (the witness).
  3. The witness is compressed into a tiny proof via polynomial commitments + a self-generated random challenge — secrets are never included.
  4. The phone sends the proof + public signals only.
  5. The contract rebuilds the expected public signals and runs a pairing equation. Balances → valid. Doesn't balance → rejected.

The deep guarantee: the math only works out if you actually possessed valid data — and the verifier learns that fact and nothing else.

Glossary

TermMeaning
CircuitThe fixed system of arithmetic equations that encodes the rules being proved.
WitnessEvery intermediate value computed while solving the circuit on your real inputs. Stays on-device.
CommitmentA cryptographic "sealed envelope": binds a value without revealing it.
Fiat–ShamirReplacing a live verifier's random challenge with a hash of the prover's own commitments, so the proof is non-interactive.
PairingA special elliptic-curve operation that lets a verifier check a relationship between hidden values from their commitments alone.
Verification keyThe small set of constants the contract holds to run the pairing check. The public residue of the trusted setup.
Public signalsThe handful of values the proof is about (nullifier, country code, hashes, proposal ID). Never raw personal data.

Next

Up next: on-chain verification — what the smart contract actually does with those bytes, why it is fast and cheap, and why a fake claim in the public signals cannot pass.