Skip to content

feat(zk-verifier): per-note circuit versioning with version retirement#110

Merged
nol4lej merged 9 commits into
mainfrom
feat/per-note-circuit-version
Jul 10, 2026
Merged

feat(zk-verifier): per-note circuit versioning with version retirement#110
nol4lej merged 9 commits into
mainfrom
feat/per-note-circuit-version

Conversation

@nol4lej

@nol4lej nol4lej commented Jul 9, 2026

Copy link
Copy Markdown
Member

Verify each spend against the circuit version its note was created under, not merely the active one, so rotating a verification key never bricks notes minted under the old key. Plus the security hardening the rotation audit surfaced.

Consensus-affecting: dispatch signatures change and new extrinsics are added → spec_version and transaction_version bump.

zk-verifier pallet:

  • verify() resolves the requested version's VK and refuses a retired one fail-closed; UnsupportedCircuitVersion distinguishes "no such version" from "no active version".
  • retire_version / unretire_version (Root only) flag a compromised or weak VK as unusable WITHOUT deleting it (audit + stats preserved). The active version cannot be retired; superseding it with set_active_version does not disable the old key, so retirement is the nuclear option that closes the downgrade-to-weak-key path.
  • store_vk helper caps versions per circuit at MAX_VERSIONS_PER_CIRCUIT=64 and caches blake2_256(key_data) in VkHashes for the SDK cross-check.
  • is_supported_version port method + runtime API filter both excluderetired versions, so the tx-pool and RPC agree with verify().
  • SECURITY INVARIANT documented on register_verification_key: a new version of an existing circuit must be a key rotation of a semantically identical circuit; a semantic change must use a new circuit id.
  • Dedicated benchmarks + WeightInfo for retire_version / unretire_version.

zk-verifier primitive:

  • TRANSFER_PUBLIC_INPUTS corrected 5 -> 7 (it counted declared signal names, but nullifiers[2]/commitments[2] are arrays). The value was dead until ensure_vk_arity began gating registration, which then rejected every valid transfer VK. Verify was unaffected (it reads arity from the VK).

shielded-pool pallet:

  • private_transfer, unshield, claim_shielded_fees take a required circuit_version: u32 and verify against that version's VK.
  • validate_unsigned rejects an unsupported version early (Custom(10)) via is_supported_version, keeping the tx-pool clean.
  • private_transfer now rejects unverified assets and is weighted per output.

EVM precompile:

  • unshield/private_transfer/claim_shielded_fees calldata gains a trailing uint32 circuitVersion (new selectors).
  • unshield memo-decode fails loudly on a malformed offset for partial unshields instead of silently dropping the change memo.

Runtime:

  • Dispatch signatures changed (circuit_version params) and new zk-verifier
    extrinsics (retire_version, unretire_version) were added, so both versions
    bump for the per-note circuit-version release.

nol4lej added 9 commits July 8, 2026 19:16
Real measured weights for account-mapping, relayer, shielded-pool and
zk-verifier. zk-verifier gains retire_version / unretire_version (retire
~20.96ms, unretire ~15.41ms — unretire only clears a flag); shielded-pool
covers the circuit_version-carrying private_transfer / unshield /
claim_shielded_fees.
Cut dated releases for the per-note circuit-version work:
- orbinum-zk-verifier 1.2.0 → 1.3.0 (transfer arity fix)
- pallet-zk-verifier 0.8.0 → 0.9.0 (version retirement, circuit-version
  verify, regenerated weights)
- pallet-shielded-pool 0.8.3 → 0.9.0 (circuit_version params, regenerated
  weights)
- pallet-evm-precompile-shielded-pool 0.2.0 → 0.3.0 (circuitVersion calldata)
- pallet-account-mapping 0.3.1 → 0.3.2 (weights regenerated)
- pallet-relayer 0.3.0 → 0.3.1 (weights regenerated)
@nol4lej nol4lej merged commit aafb598 into main Jul 10, 2026
10 of 11 checks passed
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.

1 participant