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
aandb.
Non-membership proof statement
Given the claimant’s private nullifier , the proof shows:
- Gap inclusion: opens to the published root via a standard Merkle authentication path.
- 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.