Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Concepts: Non-Membership Proofs

To claim an airdrop, a claimant must show their note existed at the snapshot and was unspent at the snapshot height. For shielded pools, "spent" is represented by a pool-specific nullifier set. We prove unspentness with a gap-based Merkle non-membership proof: prove the claimant’s (private) nullifier lies strictly between two consecutive spent nullifiers from the organizer’s snapshot.

Setup: spent-nullifier set and gaps

Let be the spent nullifiers observed on-chain up to the snapshot height (inclusive), sorted and de-duplicated by their canonical pool-specific ordering.

Here and are fixed sentinel bounds for the pool’s nullifier domain (Sapling uses and ; Orchard uses and the maximum valid field element ).

Define the open-interval gaps:

Here if and only if .

Merkle commitment to gaps

The gap tree commits to each adjacent pair by hashing a Merkle leaf using a pool-specific leaf hash with explicit domain separation (via a fixed "leaf level" distinct from internal-node levels).

The Merkle root of these leaves is published as the snapshot’s gap-root (one per pool).

In the current implementation, domain separation is provided by the hash personalization/level:

  • Sapling: is the Sapling Pedersen hash of the 512-bit string a || b (concatenation of two 32-byte nullifiers) using Merkle personalization level 62.
  • Orchard: is computed as a level-62 Orchard Merkle combine of the two canonical Orchard nullifier nodes corresponding to a and b.

Non-membership proof statement

Given the claimant’s private nullifier , the proof shows:

  1. Gap inclusion: opens to the published root via a standard Merkle authentication path.
  2. Strict interval: for the corresponding bounds , the circuit enforces .

Together this implies : if were equal to any spent nullifier in , it could not satisfy a strict inequality with the adjacent bounds.

Security notes

  • Edge cases: Since the outer gaps use fixed values and , nullifiers equal to either value cannot be used by this construction. We decided to accept this edge case given nullifiers are random and accepting them would imply further circuit constraints.
  • Field validity (Orchard): In addition to the gap argument, Orchard nullifiers must be checked to be valid encodings of field elements (e.g. ) separately from above.