Skip to content

research: Poseidon vs Poseidon2 at Veil's real hash arities (REJECT) — duplicates prior unmerged work, see note - #34

Open
alexandre-mrt wants to merge 1 commit into
mainfrom
research/2026-08-15-poseidon2-benchmark
Open

research: Poseidon vs Poseidon2 at Veil's real hash arities (REJECT) — duplicates prior unmerged work, see note#34
alexandre-mrt wants to merge 1 commit into
mainfrom
research/2026-08-15-poseidon2-benchmark

Conversation

@alexandre-mrt

Copy link
Copy Markdown
Owner

⚠️ Before reviewing this: the nightly loop's ledger is stale, and this PR is a duplicate

main hasn't merged a research PR since #17. docs/research/LEDGER.md on main only has one row
(2026-07-22 baseline), so every night since has been branching from that same stale state and
re-discovering the same experiment. I only found this by checking open PRs after finishing tonight's
work — PRs #18, #19, #24, #25, #29, #30, and #32 are all "Poseidon2 vs Poseidon" attempts that
already reached REJECT/PARK with numbers consistent with mine
(e.g. #29 and #32 found the same
t=5→8/t=6→8 padding tax this PR found, down to matching raw constraint counts like 605 and 835 for
native Poseidon(3)/Poseidon(5)). #22 and #31 already re-attempted the on-chain-gas blocker too.

I'm opening this anyway because the work is complete, correct, and touches nothing in production —
but merging any research PR here without first deciding what to do with the ~16-PR backlog just adds
a 7th duplicate. My actual recommendation: pick one Poseidon2 PR to merge (probably #32 — it
additionally cross-checks the vendored circuit against @taceo/poseidon2's independent JS reference
implementation) or write a synthesis, close the rest, and figure out why nothing has merged since
#17 before the next nightly run
— otherwise this keeps happening.

Summary (tonight's actual run)

Queue item #2: does swapping Veil's Poseidon(2/3/4/5) calls for Poseidon2 — via a real, off-the-shelf
circom implementation (@taceo/circom-lib@0.6.0) rather than hand-derived round constants — reduce
R1CS constraint count at the arities Veil's circuits actually use?

No. Measured via 8 standalone bench circuits (circuits/bench/), Poseidon2 costs 12–126% more
constraints than circomlib's Poseidon at every arity Veil uses, worst at n=4/n=5 (Veil's
highest-volume calls — commitments, nullifiers, credential leaves) because @taceo/circom-lib has
no native t=5/t=6 round constants and pads to t=8.

Also re-attempted queue item #1 (on-chain gas) first, per EXPERIMENTS.md's note to spend an early
part of the run on it: now confirmed BLOCKED by session network policy specifically (403 from
the egress proxy on any Sui fullnode RPC and on static.crates.io; GitHub access scoped to
alexandre-mrt/veil only, denies MystenLabs/sui) — concrete proxy-level evidence, not a
tool-approval-layer denial like prior nights.

No production circuit, Move module, or frontend code changed.

What's included

  • circuits/bench/ — 8 single-hash-call circuits (Poseidon vs Poseidon2 at n=2,3,4,5), the
    Poseidon2Hash sponge wrapper, compile.sh, and negative-test.mjs (real R1CS-level proof that
    a forged Poseidon2 hash output is rejected, via witness tampering + snarkjs wtns check).
  • scripts/bench/poseidon-hash-latency.mjs — reusable proving-time benchmark.
  • docs/research/2026-08-15-poseidon2-benchmark.md — full report: hypothesis, threat/privacy model,
    soundness argument, leakage analysis, results with raw command output, verdict, open questions
    (including the t=5/t=6 custom-constants follow-up and a scoped linear-layer-codegen optimization
    that could plausibly flip this REJECT into a KEEP).
  • docs/research/LEDGER.md, EXPERIMENTS.md — new row, re-ranked queue.

Test plan

  • transfer.circom, compliance.circom, withdraw.circom: 108/108 pass (real Groth16 proofs,
    freshly recompiled)
  • Proof converter: 109/109 pass
  • Compliance utils (credential leaf, Merkle builder, depth-20 tree): 67/67 pass
  • Property-based fuzz: 6/6 properties × 500 cases pass
  • Frontend (vitest): 19/19 pass
  • circuits/bench/negative-test.mjs: forged hash output correctly rejected by the R1CS
  • contracts: sui move testNOT RUN, sui CLI unavailable in this sandbox (queue item
    feat: Veil privacy payment protocol — full stack MVP #1, see report); no contract code touched by this PR

Generated by Claude Code

Queue item #2. Built 8 standalone bench circuits (circuits/bench/) comparing
circomlib's Poseidon(n) against @taceo/circom-lib's Poseidon2(t) (wrapped in a
matching capacity-then-absorb sponge) at n = 2,3,4,5 — the exact arities
transfer.circom, compliance.circom, and withdraw.circom call.

Measured result: Poseidon2 costs 12-126% more R1CS constraints at every arity
Veil uses, worst at n=4/n=5 (the highest-volume calls) because the library has
no native t=5/t=6 round constants and pads to t=8. REJECT for this specific
implementation — no production circuit touched.

Also re-attempted on-chain gas (queue item #1) first: now confirmed BLOCKED by
session network policy specifically (proxy-level 403 on Sui fullnode RPC and
static.crates.io, GitHub access scoped away from MystenLabs/sui), not
toolchain difficulty.

Includes a negative test (circuits/bench/negative-test.mjs) proving a forged
Poseidon2 hash output is rejected by the R1CS, and a reusable proving-time
benchmark (scripts/bench/poseidon-hash-latency.mjs). Full production test
suite re-verified green (108/108 circuit + 109/109 converter + 67/67
compliance-utils + 6/6 fuzz + 19/19 frontend); Move tests still blocked on the
sui CLI.

Full writeup: docs/research/2026-08-15-poseidon2-benchmark.md

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYKTU1M4Kd92HmcrHQ6qdq
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