Skip to content

Cache circuit-side padded-note IMT proofs#32

Merged
p0mvn merged 3 commits into
mainfrom
greg/circuit-padded-nullifiers
May 1, 2026
Merged

Cache circuit-side padded-note IMT proofs#32
p0mvn merged 3 commits into
mainfrom
greg/circuit-padded-nullifiers

Conversation

@greg0x
Copy link
Copy Markdown
Contributor

@greg0x greg0x commented May 1, 2026

Summary

The delegation PIR precompute introduced in #31 was caching IMT non-membership proofs keyed by the wrong nullifier. Now that the submit-time PIR fallback is gone (fb31bb70), the cache miss surfaces directly as IMT error: missing precomputed IMT proof for nullifier <hex> and the proof never builds.

Root cause: build_governance_pczt populates dummy_nullifiers via make_dummy_note, which constructs each padded-slot note with NoteValue::from_raw(1) so Keystone renders the padded actions in the PCZT. The delegation circuit, however, fills its padded slots with NoteValue::ZERO (per voting-circuits/.../delegation/builder.rs:200-213+230). The two notes share rho/rseed/address but differ in value, so their nullifiers differ. Pre-#31 this didn't fire because the bundle builder's PirImtProvider fetched the (0-zat) proof on demand via pir_client, silently bridging the gap. After fb31bb70 removed that fallback, the only IMT data the provider sees is the precompute cache — keyed by the wrong nullifier.

Fix

New padded_nullifiers_for_circuit helper derives the 0-zat circuit-side nullifiers from the stored padded_note_secrets (rho, rseed) plus the FVK extracted from the notes' UFVK. Both precompute_delegation_pir and build_and_prove_delegation now use it instead of load_dummy_nullifiers. precompute_delegation_pir gains a network_id parameter (needed for UFVK decode); the FFI signature change is in valargroup/zcash-swift-wallet-sdk#TBD.

The dummy_nullifiers column is left intact — it remains part of the PCZT/Keystone metadata flow and may still be referenced by future Keystone-side work.

Verification

  • cargo check -p zcash_voting clean against local-wired tree.
  • End-to-end on mainnet (testnet) simulator: delegation TX A0DFB6105AFE8956E84818E644D43B612DEC25CF63C5B4C996A2A3EF562DC6B4 lands on chain. Precompute reports cached=0 fetched=5 for a 4-real-note bundle (4 real + 1 padded), then submit hits the cache (5 cached, 0 fetched) and the proof builds in ~2s.

Release

Bumps to 0.2.4 in the second commit. Once merged + published, valargroup/zcash-swift-wallet-sdk#TBD picks it up.

greg0x added 2 commits May 1, 2026 22:56
build_governance_pczt stores dummy_nullifiers via make_dummy_note, which
constructs each padded slot's note with NoteValue::from_raw(1) so Keystone
renders the padded actions in the PCZT. The delegation circuit, however,
constructs its padded slots with NoteValue::ZERO (voting-circuits' builder
fills empty inputs with zero-value notes so they don't perturb the ballot
weight math). The two notes share rho/rseed/address but differ in value, so
their Orchard nullifiers differ.

Pre-#31, the bundle builder's PirImtProvider could fall back to fetching from
PIR on demand, which silently bridged the gap — the IMT lookup was always
made for the 0-zat nullifier, the cache miss triggered a fresh fetch, and
the stale 1-zat dummy_nullifiers we'd cached were never consulted. After
#31 + fb31bb7 ("Remove submit-time delegation PIR fallback"), the
provider is initialised purely from the precompute cache, so the mismatch
surfaces as `IMT error: missing precomputed IMT proof for nullifier <hex>`
and the proof never builds.

Fix in precompute_delegation_pir + build_and_prove_delegation: derive the
0-zat circuit-side padded nullifiers from the stored padded_note_secrets
(rho, rseed) plus the FVK extracted from the notes' UFVK, exactly the way
voting-circuits/.../delegation/builder.rs:200-213+230 does. Both call sites
now key the IMT cache off these, so the bundle builder's lookup hits.

precompute_delegation_pir gains a network_id parameter so it can decode the
UFVK; build_and_prove_delegation already had it and threads it through.
The dummy_nullifiers column is left intact — it's still part of the PCZT/
Keystone metadata flow.
Bundles the circuit-side padded nullifier fix shipped in this branch.
Matches the team's release-commit pattern (cf. 461fa59 "Release
zcash_voting 0.2.2").
@p0mvn p0mvn merged commit 10bf87e into main May 1, 2026
5 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.

3 participants