Skip to content

fflonk 4/5: An fflonk verifier, with a Keccak or a SHA-256 transcript - #19

Merged
mjthatch merged 1 commit into
mainfrom
research/fflonk
Oct 23, 2026
Merged

mjthatch merged 1 commit into
mainfrom
research/fflonk

Conversation

@mjthatch

Copy link
Copy Markdown
Collaborator

The fourth of five stacked PRs for the fflonk verifier. The base is PR 3, the MSM.

groth/fflonk.py compiles a snarkjs 0.7.6 fflonk verification key into a Simplicity program that checks what snarkjs's fflonkVerify checks. fflonk's setup is universal: a circuit needs a powers-of-tau file and no ceremony of its own. docs/fflonk.md explains the design.

Two transcript modes

  • keccak checks proofs from snarkjs as it is (PR 2's Keccak-256).
  • sha256 checks proofs from tools/snarkjs_sha256.mjs, which is snarkjs with the transcript's hash swapped for SHA-256 (a copy of its sources, patched at run time; it refuses a snarkjs other than 0.7.6). Such proofs do not verify with snarkjs's own verifier.

Cost

On the toy circuit circuits/multiplier_fflonk.circom (3 public inputs), in hand. The generator predicts each cost exactly.

SHA-256 Keccak
ranges and on-curve checks 0.5 M 0.5 M
transcript 0.3 M 49.1 M
opening (F_r) 6.0 M 6.0 M
MSM 73.4 M 73.4 M
pairing check 42.9 M 42.9 M
binding 0.1 M 0.1 M
total, before padding 123.2 M mWU 172.0 M mWU
pruned program / witness 65.6 / 44.7 kB 74.7 / 44.7 kB

Groth16 costs 48.6 M for comparison. A spend pads its witness to raise the budget (below).

What is in it

  • fflonk_ref.py. A line-by-line Python port of fflonkVerify. The tests compare the program with it on every intermediate value. Its loaders refuse points off the curves, X_2 outside the order-r subgroup, and points at infinity.
  • The opening (frprog.py). F_r arithmetic as straight-line code: the prover runs it on integers, and the generator records it and lays it out level by level. The formulas are simplified to need 22 inverse hints plus one per public input:
    • y^8 - xi, y^4 - xi and (y^3 - xi)(y^3 - xi w) for the products over the roots;
    • one hint for every inverse of a power of xiSeed.
  • The pairing check. pairing.py's polynomial identity, unchanged:
    • B is the generator [1]₂, which the program checks;
    • δ is [x]₂;
    • e(-C0, [1]₂) is the constant.
  • Binding. The circuit's first two public inputs are the sighash as two 128-bit limbs, which the program compares with sig_all_hash. fflonk pays little per input, so there is no Merkle root.
  • Padding.
    • The verifier costs more than its program and witness weigh. So a spend adds 512-byte zero blocks, each earning 0.51 M of budget and costing 0.05 to 0.12 M to check.
    • The program offers groups of 1, 2, ... 256 blocks, each behind a case on a bit of a witness word, so the spend picks the count and the groups not taken are pruned.
    • Fflonk.pad_for counts the budget low, as Groth16.budget_ok does.
  • Generator checks.
    • Each witness node is read once in the tree unfolding, as for Groth16.
    • Every hint word is below 2^254.
    • Challenge words, evaluations and public inputs must be canonical, which is stricter than snarkjs.

Tests

make test-fflonk_keccak and make test-fflonk_sha256 (tests/test_fflonk.py), on the proofs in examples/fflonk:

  • The reference. Each proof verifies in its own mode only, and the port rejects a tampered evaluation and public input.
  • The honest witness. It passes at the predicted cost, and also padded as if the program were 50 kB, where the budget covers the cost.
  • Rejections. All must fail on a jet (harness.expect_rejected):
    • a tampered evaluation, challenge, public input, inverse, column word, fixed-base scalar or MSM hint;
    • a forged G1 slot or G2 slot;
    • in Keccak mode, a valid proof for another sighash.
  • The witness. No two witness nodes hold the same value.

The fixtures were made with the demo of PR 5.

🤖 Generated with Claude Code

@mjthatch
mjthatch added this pull request to stack #21 October 23, 2026 13:55
@mjthatch
mjthatch force-pushed the research/fflonk branch 2 times, most recently from aa83fce to 3dd6b2b Compare October 23, 2026 14:13
Base automatically changed from research/msm to main October 23, 2026 14:26
groth/fflonk.py compiles a snarkjs 0.7.6 fflonk verification key into a
Simplicity program that checks what fflonkVerify checks. fflonk's setup is
universal: a circuit needs a powers-of-tau file and no ceremony.

- Transcript modes: keccak checks proofs from snarkjs as it is; sha256
  checks proofs from tools/snarkjs_sha256.mjs, snarkjs with the
  transcript's hash swapped for SHA-256.
- fflonk_ref.py: a line-by-line Python port of fflonkVerify, whose loaders
  refuse points off the curves, X_2 outside the subgroup, and infinity.
- frprog.py: F_r arithmetic as straight-line code, recorded from the
  prover's run and laid out level by level. The opening needs 22 inverse
  hints plus one per public input.
- The MSM of PR 3 and pairing.py's check: B is the generator, delta is
  [x]_2, e(-C0, [1]_2) is the constant.
- Binding: the first two public inputs are the sighash limbs, compared
  with sig_all_hash.
- Padding: groups of 1 to 256 zero blocks behind cases on a witness word,
  so a spend can raise its budget to cover the cost; pad_for counts the
  budget low, as Groth16.budget_ok does.
- Each witness node is read once in the tree unfolding.

Cost on circuits/multiplier_fflonk.circom (3 public inputs), before
padding: 123.2 M mWU with SHA-256, 172.0 M with Keccak (49.1 M of it the
transcript).

tests/test_fflonk.py runs both modes on the proofs in examples/fflonk: the
honest witness at the predicted cost, padded so the budget covers it, and
tampered evaluations, challenges, inputs, hints, slots and a proof for
another sighash, each rejected by a failed jet. docs/fflonk.md explains
the design.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mjthatch
mjthatch merged commit a55fe57 into main Oct 23, 2026
18 checks passed
@mjthatch
mjthatch deleted the research/fflonk branch October 27, 2026 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants