Skip to content

[ZIP TBD] Private Information Retrieval for Nullifier Exclusion Proofs#1198

Open
p0mvn wants to merge 202 commits into
zcash:mainfrom
valargroup:roman/gov-pir2
Open

[ZIP TBD] Private Information Retrieval for Nullifier Exclusion Proofs#1198
p0mvn wants to merge 202 commits into
zcash:mainfrom
valargroup:roman/gov-pir2

Conversation

@p0mvn

@p0mvn p0mvn commented Mar 3, 2026

Copy link
Copy Markdown

ZIP specifying a private information retrieval (PIR) scheme for Zcash Nullifier Exclusion Proofs. Participants prove that nullifiers are unspent by retrieving nullifier exclusion proofs without revealing which nullifier is being checked.

The construction uses YPIR+SP (a single-server, hintless PIR protocol built on SimplePIR with RLWE packing), making it suitable for cold-start mobile wallets with no client-side preprocessing. The nullifier exclusion tree is partitioned into three tiers — a plaintext broadcast (192 KB), a small PIR database (24 MB), and a large PIR database (6 GB) — requiring two sequential PIR queries and approximately 3.3 MB total bandwidth.

The document also surveys the PIR design space, explains the choice of YPIR+SP over alternatives such as InsPIRe, and specifies row layouts, serialization formats, and client procedures for each tier.

Status: Draft
Category: Standards / Wallet

Still to come (before Proposed):

  • Full reference implementation of the governance-specific layers
  • Deployment section

p0mvn and others added 30 commits March 2, 2026 22:35
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
p0mvn and others added 7 commits March 24, 2026 20:10
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
PIR versioning + v0 full download scheme
Co-authored-by: Claude <noreply@anthropic.com>
Comment thread zips/draft-valargroup-nullifier-pir.md Outdated
Comment on lines +84 to +85
exclusion tree data (the Tier 0 plaintext, all Tier 1 rows, and all
leaf records) and locally computes Tier 2 internal node hashes on

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still ties the "full download" option to the choices of Tier 1 and Tier 2 depths, which I think are unlikely to remain as currently specified.

@daira daira Mar 30, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the parameters given at https://github.com/zcash/zips/pull/1198/changes/4dfe8d0b99e97ba20733781db3ac5df42367c6ad..2a9802bdafb24149f7a49b0072992b4908a38777#r3006956703, the download need not be any more than 2.0625 GiB ($32 \times (2^{26} + 2^{21})$ bytes) even if all of the intermediate nodes up to depth 20 inclusive are given. Actually it only has to include the number of depth-21 leaves corresponding to the real number of nullifiers (32 at each leaf), so that's ~1.52 GiB ($32 \times (50000000 + 2^{21})$ bytes) for 50 million nullifiers.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replied to the tier amendment proposal here:
#1198 (comment)

This thread is contingent on that decision, so I am keeping it open for now

p0mvn and others added 5 commits March 30, 2026 16:02
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Czar <adam@osmosis.team>

Co-authored-by: Claude <noreply@anthropic.com>
The packing-key security analysis previously used m=33 (ring-level
sample count) with n=2048 (scalar dimension), producing an artificial
∞ result. This was inconsistent with the selector analysis, which
correctly expands ring samples to scalar LWE via negacyclic extraction.

Apply the same expansion to the packing key: 33 ring elements × 2048
coefficients = 67,584 scalar LWE samples. The lattice estimator now
reports finite hardness (104.0 Core-SVP / 132.6 Matzov for uSVP),
essentially identical to the Tier 2 selector — as expected when m >> n.

Co-authored-by: Claude <noreply@anthropic.com>
Made-with: Cursor
security analysis

Co-authored-by: Claude <noreply@anthropic.com>
Comment thread zips/draft-valargroup-nullifier-pir.md Outdated
Comment thread zips/draft-valargroup-nullifier-pir.md Outdated
p0mvn and others added 5 commits March 31, 2026 15:10
Update the nullifier exclusion tree specification to use K=2 punctured
ranges with the 9+6+10 depth split (depth 25), replacing the earlier
depth-26 layout. Key changes:

- Rename "Interval Merkle tree" to "Punctured interval Merkle tree"
  and update terminology for punctured ranges throughout
- Update tree construction for K=2: overlapping triples, odd-count
  enforcement, sentinel spacing (33 sentinels at 2^249 intervals)
- Revise tier depths (0-9, 9-15, 15-25) and all derived constants
  (512 Tier 1 rows, 32,768 Tier 2 rows, 4,096 / 98,304 byte rows)
- Add full download retrieval scheme with itemized bandwidth breakdown
- Update PIR bandwidth summary for the new tier sizes
- Add leaf encoding, authentication path, and tree padding sections
- Add detailed row serialization formats for all three tiers

Co-authored-by: Claude <noreply@anthropic.com>
Made-with: Cursor
The security and correctness analysis sections contained stale values
from an earlier tier layout. Update to match the depth-25 (9+6+10)
parameters specified in the Nullifier Instantiation Values table.

Security analysis:
- Tier 2 selector samples: 262,144 → 32,768 (= m_pad from Database Shape)
- Ring blocks: B = 128 → 16 (= ceil(32768/2048))
- Matrix dimensions: 2048×262,144 → 2048×32,768
- Instance Summary and Sensitivity Analysis tables updated accordingly
- Lattice estimator rerun confirms all hardness numbers unchanged
  (once m >> n=2048, additional samples give no attacker advantage)

Correctness analysis:
- Scan noise: V_scan from 2^51.4 → 2^48.4 (fewer rows in product)
- Total noise: 2^74.9 → 2^74.8 (key-switching still dominates)
- Packing chunks: 7 → 28 (= ceil(56174/2048) = ceil(W_value/d))
- Total slots: 14,336 → 57,344
- Per-slot failure: 2^-80 → 2^-90 (improved margin)
- Union bound: 2^-66 → 2^-74 (still well below 2^-40 target)

Estimator script (nullifier_pir_analysis.py):
- M_TIER2: 262_144 → 32_768
- Tier 2 label: "128 ring elems" → "16 ring elems"

Co-authored-by: Claude <noreply@anthropic.com>
Made-with: Cursor
Switch full-download spec from pre-computed 96-byte leaf records to
the sorted nullifier list (32 bytes per element) with client-side
leaf triple reconstruction, reducing download from ~3.2 GB to ~1.48 GiB
at 50M nullifiers.

Co-Authored-By: Claude <noreply@anthropic.com>
Made-with: Cursor

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Lowers the Claim circuit Merkle path depth from 29 to 26, reducing
empty-subtree padding from 4 levels to 1. This saves ~990 circuit
constraints while still supporting ~67M leaves (~134M nullifiers),
roughly 2.7× the current ~50M nullifier count.

Co-authored-by: Claude <noreply@anthropic.com>
Made-with: Cursor
@str4d

str4d commented Mar 31, 2026

Copy link
Copy Markdown
Collaborator

As I already noted in private to the authors of this set of ZIPs, any earlier commits in the PR that were created with AI assistance will need to have their commit messages amended (via a force-push to the branch) to declare that assistance, per the CONTRIBUTING for this repo. If fixing each individual commit is too difficult, then I recommend rebasing the PR to squash the commits down into a smaller set that can be correctly attributed.

@str4d
str4d dismissed their stale review March 31, 2026 21:47

Only comment left open is to be decided by Daira-Emma.

@str4d
str4d removed their request for review March 31, 2026 21:48
@p0mvn

p0mvn commented Apr 1, 2026

Copy link
Copy Markdown
Author

As I already noted in private to the authors of this set of ZIPs, any earlier commits in the PR that were created with AI assistance will need to have their commit messages amended (via a force-push to the branch) to declare that assistance, per the CONTRIBUTING for this repo. If fixing each individual commit is too difficult, then I recommend rebasing the PR to squash the commits down into a smaller set that can be correctly attributed.

Thanks for the reminder. Addressed

@p0mvn
p0mvn requested review from daira and str4d April 1, 2026 17:53
@p0mvn
p0mvn marked this pull request as ready for review April 1, 2026 17:53
greg0x added a commit to valargroup/zips that referenced this pull request Apr 10, 2026
…ver in Validator

The draft talked about a "nullifier service operator" in Privacy
Implications and described a Nullifier Service in Deployment
Architecture, but no role was defined to say who actually runs it.
Add a Nullifier Service Operator role: a separate operator who
runs the nf-server PIR binary from valargroup/vote-nullifier-pir,
with its own ingest pipeline and HTTP query endpoint, distributed
and deployed independently of the vote chain node.

Submission server operation lives in svoted itself and is therefore
the Validator's responsibility. Extend the Validator role section
to make that explicit and cross-reference the submission server
draft, so a reader doesn't have to infer the assignment.

Also fix a stale filename: the [^draft-pir] reference pointed at
draft-valargroup-gov-pir.md, but the canonical filename in
zcash#1198 is draft-valargroup-nullifier-pir.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

5 participants