Privacy Patterns for Midnight
Design and implement privacy-preserving applications using zero-knowledge proofs.
Core Privacy Model
Concept Description Visibility
Public Ledger state Everyone
Private Circuit inputs Only prover
Witness Prover-provided data Only prover
Disclosed Explicitly revealed Everyone
Reference Files
Topic Resource
Zero-Knowledge Basics references/zk-fundamentals.md
Commitment Schemes references/commitments.md
Nullifier Patterns references/nullifiers.md
Selective Disclosure references/selective-disclosure.md
Pattern Overview
- Commitment Scheme
Hide a value while binding to it:
commitment = persistentCommit(value, randomness); // Later: prove you know the opening
- Nullifier Pattern
Prevent double-use without revealing identity:
nullifier = transientHash(secret, commitment); // Can only be generated once per commitment
- Selective Disclosure
Prove properties without revealing data:
// Prove over 18 without revealing actual age disclose(age >= 18); // Only boolean is public
- Merkle Membership
Prove membership in a set without revealing position:
// Verify path from leaf to root assert verifyMerklePath(leaf, proof, root);
Quick Examples
Private Balance Check
// Only reveal if balance is sufficient, not actual amount export circuit checkFunds(balance: Uint<64>, required: Uint<64>): Boolean { return disclose(balance >= required); }
Anonymous Voting
export circuit vote(voter: Bytes<32>, choice: Boolean): [] { // Voter identity disclosed (prevents double voting) hasVoted.insert(voter); // Choice remains private, only totals change if (choice) { yesCount = yesCount + 1; } }
Commitment-Reveal
witness randomness: Field;
// Phase 1: Commit export circuit commit(value: Uint<64>): Field { return persistentCommit(value, randomness); }
// Phase 2: Reveal export circuit reveal(value: Uint<64>, commitment: Field): [] { assert persistentCommit(value, randomness) == commitment; disclose(value); }
Privacy Best Practices
-
✅ Use witness for data that should never appear on-chain
-
✅ Use persistentCommit (with randomness) to hide values
-
✅ Use nullifiers to prevent double-actions
-
✅ Disclose only what's necessary (prefer booleans)
-
❌ Don't store unhashed sensitive data on ledger
-
❌ Don't use predictable randomness in commitments
-
❌ Don't reveal intermediate values unnecessarily
Privacy Levels
┌────────────────────────────────────────────────┐ │ Level 0: Fully Public │ │ - All data visible on-chain │ ├────────────────────────────────────────────────┤ │ Level 1: Hidden Values │ │ - Commitments on-chain, values private │ ├────────────────────────────────────────────────┤ │ Level 2: Unlinkable Actions │ │ - Nullifiers prevent linking actions │ ├────────────────────────────────────────────────┤ │ Level 3: Anonymous Membership │ │ - Merkle proofs hide set position │ └────────────────────────────────────────────────┘
When to Use Each Pattern
Pattern Use Case
Commitment Sealed bids, hidden votes before reveal
Nullifier Preventing double-spend, one-time tokens
Merkle Proof Membership in allowlist without revealing identity
Selective Disclosure Age verification, credential proofs
Resources
-
Midnight Privacy Model
-
ZK-SNARK Fundamentals