Skip to content

v0.7.0 — Circuit synthesis from Petri net schema

Choose a tag to compare

@stackdump stackdump released this 16 Apr 09:51
· 6 commits to main since this release
b9ba30a

Headline: Hand-written gnark circuits are gone. Every ZK circuit for every ERC template is now generated from metamodel.Schema, closing the "Petri net as single source of truth" loop for ZK proofs alongside the existing Solidity + Foundry pipelines. See #1.

Highlights

Phase 2 — Circuit synthesis (gh#1)

  • prover/synth/ package: emits Go source files containing gnark circuit struct + Define() methods directly from metamodel.Schema.

  • All 7 circuits synthesized with byte-identical constraint shapes (parity verified via gnark compile):

    Circuit Constraints Public Private
    mint 662 6 2
    approve 662 6 2
    burn 14,647 5 41
    voteCast 15,261 6 43
    transfer 15,967 6 42
    vestClaim 15,968 6 43
    transferFrom 18,012 7 42
  • prover/circuits.go dropped — the hand-written version (478 lines) retired; prover/*_gen.go are the sole source of circuit definitions.

  • Schema metadata extensions (metamodel.Schema): State.MerkleDepth, State.HashFunc, Action.Roles, Action.ZKOps. All omitempty — existing schema CIDs unchanged.

  • New ZKOp primitives: NullifierBind, CommitmentBind, RangeCheck. Express cryptographic obligations that aren't arc-derivable (nullifier hashes, vote-commitment binding).

  • Guard expression compiler: <state>[<key>] >= <amount> patterns automatically become api.ToBinary range checks; non-ZK conjuncts (zero-address checks etc.) are correctly ignored.

  • Deterministic output: make gen-circuits && git diff --exit-code is a CI gate.

Phase 1 — Vote security + UX

  • Fixed registration-bypass vulnerability: unregistered voters could previously cast votes because if poll.RegistryRoot == "" guards skipped validation. Server now rejects votes with an empty registry (400) and counts registration slots (409 when exhausted).
  • Runtime rt.Enabled() gate (slice 2.7): the vote handler now replays events through the Petri net Runtime and checks castVote enablement, replacing the earlier manual event-count counter. Introduced registrySlots TokenState so registerVoter → registrySlots → castVote is a proper token flow.
  • Sparse Merkle tree in public/merkle.js: fixes a ~2-minute main-thread hang on vote. Depth-20 tree build drops from ~2M mimcHash ops to O(N·D) (~6ms for a single-voter tree).
  • modInverse sign bug in public/dev-wallet.js secp256k1: JS BigInt division truncates toward zero, breaking extended Euclidean for negative inputs. This caused wrong public keys → invalid signatures rejected by the backend. One-line fix.
  • EIP-6963 provider discovery in public/poll.js: Trust Wallet and MetaMask coexisting no longer collide on window.ethereum last-writer-wins.
  • Client-side signing test fixture: e2e/wallet-fixture.js injects a window.ethereum mock that performs real secp256k1 via the pure-JS path. 26 Playwright tests (up from 19).

Tooling & infrastructure

  • bitwrap -synthesize <template> -output <path>: new CLI for regenerating circuits.
  • make gen-circuits: regenerates all ERC templates.
  • make test-e2e-wallet: headless e2e with the real-signing fixture.
  • WASM prover moved to Web Worker (no main-thread freeze during proving).

Known gaps (follow-up work)

  • .btw DSL → synthesizer naming convention (gh#1 residue): the DSL produces uppercase state names (BALANCES), the generators expect lowercase (balances). Running examples/erc20.btw through -synthesize currently returns "schema has no synthesizable actions". Wiring a case-insensitive lookup (or normalizing IDs at dsl.Build) is a small follow-up.
  • Slice 2.6 synth parity tests are now self-comparisons (no hand-written twin to compare against). Harmless but could be simplified to pure smoke tests.

Upgrade notes

  • No config / migration changes. Deploy is git pull && make build && restart.
  • Circuit names are stable (mint, transfer, etc.) so no prover-service client changes needed.
  • prover/circuits.go is gone; if external consumers imported MintCircuit etc. from that file, they now come from prover/erc020_gen.go and friends — same package, same names.

Full commit history: 55 commits from v0.6.3..v0.7.0. git log v0.6.3..v0.7.0 --oneline for the full list.

🤖 Generated with Claude Code