Skip to content

feat(interop): snarkjs-compatible JSON export and bidirectional verification CI (#31) - #58

Merged
satran004 merged 1 commit into
mainfrom
feat/31-snarkjs-bidirectional-ci
Sep 6, 2026
Merged

feat(interop): snarkjs-compatible JSON export and bidirectional verification CI (#31)#58
satran004 merged 1 commit into
mainfrom
feat/31-snarkjs-bidirectional-ci

Conversation

@satran004

@satran004 satran004 commented Sep 6, 2026

Copy link
Copy Markdown
Member

Closes #31 (Phase 1 of #54). Design and evidence: ADR-0047.

What

  • Exporters (zeroj-crypto, new package crypto.snarkjs): SnarkjsGroth16Json and SnarkjsPlonkJson write proof.json, verification_key.json, public.json byte-identical to snarkjs 0.7.6, failing closed on any non-canonical value (infinity, off-curve, off-subgroup, scalar outside [0, r), non-canonical PlonK w). No new module dependencies; hand-written writer, no Jackson.
  • KATs (zeroj-crypto): byte-identical re-export of every snarkjs-written BLS12-381 artifact in the repo, the vk_alphabeta_12 relation, JVM round trips through the snarkjs codec + ZeroJ verifiers, egress rejections.
  • Live interop suites (zeroj-integration-tests, it.snarkjs.*, default test task, skip without snarkjs): ZeroJ Groth16/PlonK proofs and ZeroJ-native keys verified by snarkjs groth16/plonk verify with ten tamper negatives; live snarkjs proofs verified by pure-Java and blst Groth16 verifiers and the PlonK verifier; an offline differential test pins the exporter's selector-infinity policy against PlonkBLS12381Verifier.
  • CI: snarkjs-interop job in assurance.yml (Node 22, snarkjs@0.7.6, -PrequireSnarkjs makes a missing or wrong-version oracle a failure, 45 min timeout). ci.yml stays pure Java.
  • Shared vector: the snarkjs PlonK vector moves from zeroj-verifier-plonk test resources to zeroj-test-vectors (single copy; .gitattributes pins LF on the vectors).
  • Runner hardening (reviewer follow-up): SnarkjsProver redirects output before its timed wait so a hung child cannot bypass the timeout, with descendant cleanup and two process regression tests.

Findings worth a reviewer's attention

  1. snarkjs' vk_alphabeta_12 equals e_ZeroJ(alpha, beta)^3: wasmcurves uses the cyclotomic final exponentiation (3x the hard part), ZeroJ the exact (p^12-1)/r. Pinned by KAT on three snarkjs keys, a live key, and the generator; the KAT also asserts the uncubed value does not match.
  2. snarkjs writes artifacts with bfj, not JSON.stringify: an empty public.json is [\n].
  3. snarkjs plonk verify ignores vk.w and derives Fr.w[power]; ZeroJ's verifier reads it. The exporter requires the canonical root, and ZeroJ's root-of-unity chain is pinned against ffjavascript at powers 3, 8, 16, 24, 32.
  4. The snarkjs CLI exits 1 for both a rejection and an internal error; the suites classify verdicts by output (OK! vs the pinned rejection strings), so a negative test cannot pass on a crash.
  5. snarkjs accepts a ZeroJ-native PlonK setup and proof (planned as an experiment; it passed).

Risk / scope

R2 for the exporter (egress serialization boundary), R1 for CI and docs. Additive: no prover, verifier, transcript or maturity claim changes. Known gap: a ZeroJ proof under a snarkjs PlonK zkey is not covered because PlonKZkeyImporterBLS381 does not expose the A/B/C wire maps.

Verification

Check Result
./gradlew build verifyDefaultModuleSurface 4111 tests, 0 failures
Exporter KATs 22 pass
it.snarkjs.* + process regressions 16 pass (6 need snarkjs)
Oracle contract: present/absent/wrong version x required/not all four branches as specified
zeroj-usecases against 0.1.0-pre12-7bf569d-SNAPSHOT 13 projects build and test clean

The assurance job has run only as a local emulation of its exact command line; this PR is its first run on GitHub Actions.

🤖 Generated with Claude Code

…ication CI (#31)

ADR-0047. Adds SnarkjsGroth16Json / SnarkjsPlonkJson in zeroj-crypto (package
crypto.snarkjs) that write proof.json, verification_key.json and public.json
byte-identical to snarkjs 0.7.6 (bfj writer), failing closed on any
non-canonical value; KATs pin the format against every snarkjs-written
BLS12-381 artifact in the repo. Live interop suites in zeroj-integration-tests
(it.snarkjs.*) verify both directions for Groth16 and PlonK against the pinned
snarkjs CLI with tamper negatives, and a new snarkjs-interop assurance job
runs them with -PrequireSnarkjs so a missing or wrong-version oracle fails.

Findings pinned by tests and documented in the ADR: snarkjs' vk_alphabeta_12
is the cube of ZeroJ's pairing (wasmcurves cyclotomic final exponentiation);
snarkjs writes an empty public.json as "[\n]" (bfj, not JSON.stringify);
snarkjs plonk verify ignores vk.w, so the exporter requires the canonical
root and ZeroJ's root-of-unity chain is pinned against ffjavascript; the
snarkjs CLI exits 1 for both a rejection and a crash, so verdicts are
classified by output. The shared SnarkjsProver runner now redirects output
before its timed wait so a hung child cannot bypass the timeout. The snarkjs
PlonK vector moves to zeroj-test-vectors as the single shared copy.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WRtUQPK45PkawH22huhT1K
@satran004
satran004 merged commit 56f5f10 into main Sep 6, 2026
6 checks passed
@satran004
satran004 deleted the feat/31-snarkjs-bidirectional-ci branch September 6, 2026 13:34
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.

[CI/Interop] Add continuous bidirectional proof verification CI against snarkjs CLI

1 participant