A zero-knowledge allowlist system built on Midnight Network. Members prove they belong to a set without revealing who they are.
- How Sparse Merkle Trees enable privacy-preserving membership proofs
- Writing ZK circuits in Midnight's Compact language
- Domain-separated hashing for cryptographic safety
- Nullifier-based replay prevention
- The full lifecycle: setup → member registration → proof generation → on-chain verification
npm install
npm test # run the test suite
npm run cli # show CLI helpcontracts/
allowlist.compact — The ZK smart contract (Compact language)
src/
types.ts — Type definitions
poseidon.ts — Hash utilities (Poseidon shim)
merkle-tree.ts — Sparse Merkle Tree implementation
cli.ts — CLI tool for building trees, generating proofs
tests/
allowlist.test.ts — Comprehensive test suite (144 cases)
tutorial/
article.md — Full written tutorial (3,000+ words)
┌─────────────────────────────────────────────────────────────┐
│ OFF-CHAIN (Private) │
│ │
│ 1. Admin creates a Sparse Merkle Tree (depth 20) │
│ 2. Each member gets a secret → hashed to a leaf │
│ 3. Admin publishes the tree root on-chain │
│ 4. Member generates ZK proof locally (secret + path) │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ON-CHAIN (Public) │
│ │
│ 5. Contract verifies Merkle path inside ZK circuit │
│ 6. Nullifier recorded → prevents double-usage │
│ 7. ✅ Proof valid — member is anonymous but verified │
└─────────────────────────────────────────────────────────────┘
# Generate a membership secret
npx tsx src/cli.ts generate-secret
# → 0x3a7f2b8c1d4e5f6a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a
# Build a tree from secrets
echo '["0xaaa...", "0xbbb...", "0xccc..."]' > secrets.json
npx tsx src/cli.ts build-tree secrets.json
# Generate a proof
npx tsx src/cli.ts prove 0xaaa... tree.json
# Compute a nullifier
npx tsx src/cli.ts nullifier 0xaaa... "vote_v1"| Circuit | Who | What |
|---|---|---|
setup(commitment) |
Admin (once) | Pin the admin identity |
setRoot(root) |
Admin | Publish a new Merkle root |
verifyAndUse(nullifier) |
Member | Prove membership + record usage |
MIT