Skip to content

Commit 5d57494

Browse files
author
tilo-14
committed
Add ZK examples: shielded-pool, payments, zk-id, zk-vote, zk-airdrop-claim
- zk/shielded-pool: Private token pool with deposit/withdraw - zk/payments: Private transfers with balance commitments - zk/zk-id: Identity proofs with compressed account Merkle inclusion - zk/zk-vote: Anonymous voting with credential ownership proofs - zk/zk-airdrop-claim: ZK-proven airdrop claims Each example includes Circom circuits, Rust program with groth16 verification, and integration tests.
1 parent d30d82d commit 5d57494

79 files changed

Lines changed: 35750 additions & 40 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/rust-tests.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ jobs:
2929
- counter/native
3030
- counter/pinocchio
3131
- account-comparison
32-
- zk-id
32+
- zk/zk-id
33+
- zk/zk-vote
34+
- zk/payments
3335
- airdrop-implementations/simple-claim/program
3436
include:
3537
- example: basic-operations/native
@@ -51,10 +53,10 @@ jobs:
5153
example: ${{ matrix.example }}
5254
solana-cli-version: ${{ env.SOLANA_CLI_VERSION }}
5355
rust-toolchain: ${{ env.RUST_TOOLCHAIN }}
54-
install-circom: ${{ matrix.example == 'zk-id' }}
56+
install-circom: ${{ startsWith(matrix.example, 'zk/') }}
5557

5658
- name: Setup ZK circuits
57-
if: matrix.example == 'zk-id'
59+
if: startsWith(matrix.example, 'zk/')
5860
working-directory: ${{ matrix.example }}
5961
run: ./scripts/setup.sh
6062

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ test-ledger
88
.claude
99
build
1010
pot
11+
zk/zk-infra.md
File renamed without changes.

zk/payments/CLAUDE.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# ZK Private Payments
2+
3+
Private token transfers using ZK proofs to hide payment amounts.
4+
5+
## Summary
6+
7+
- Deposit: Public tokens → private balance commitment = `Poseidon(amount, blinding)`
8+
- Transfer: Send tokens privately (hidden amounts) with ZK proof
9+
- Withdraw: Private balance → public tokens
10+
- Nullifier prevents double-spend
11+
12+
## Quick Start
13+
14+
```bash
15+
./scripts/setup.sh
16+
cargo build-sbf
17+
cargo test-sbf
18+
```
19+
20+
## Source Structure
21+
22+
```
23+
src/
24+
├── lib.rs # Program entry, instructions, accounts
25+
└── verifying_key.rs # Groth16 verifying key (7 public inputs)
26+
27+
circuits/
28+
├── private_transfer.circom # Main transfer circuit
29+
├── compressed_account.circom # Account hash computation
30+
├── merkle_proof.circom # Merkle tree verification (26 levels)
31+
└── range_check.circom # Balance sufficiency checks
32+
33+
tests/
34+
└── test.rs # E2E test with proof generation
35+
```
36+
37+
## Instructions
38+
39+
| Instruction | Description |
40+
|-------------|-------------|
41+
| `initialize` | Create vault for token mint |
42+
| `deposit` | Deposit tokens, create balance commitment |
43+
| `create_balance_commitment` | Create commitment without token transfer (testing) |
44+
| `transfer` | Private transfer with ZK proof |
45+
| `withdraw` | Withdraw tokens by proving commitment ownership |
46+
47+
## Compressed Accounts
48+
49+
| Account | Seeds | Fields |
50+
|---------|-------|--------|
51+
| `BalanceCommitment` | `[b"balance", commitment]` | `mint_hashed`, `commitment` |
52+
| `NullifierAccount` | `[b"nullifier", nullifier]` | `nullifier` |
53+
54+
## ZK Circuit (PrivateTransfer)
55+
56+
**Public inputs** (7 signals):
57+
1. `owner_hashed` - Program ID hashed to BN254 field
58+
2. `merkle_tree_hashed` - State tree pubkey hashed
59+
3. `discriminator` - BalanceCommitment discriminator
60+
4. `mint_hashed` - Token mint hashed
61+
5. `expectedRoot` - Merkle tree root
62+
6. `nullifier` - Prevents double-spend
63+
7. `receiver_commitment` - Receiver's balance commitment
64+
65+
**Private inputs**:
66+
- `sender_amount`, `sender_blinding` - Sender's balance preimage
67+
- `transfer_amount` - Amount to transfer
68+
- `new_sender_blinding`, `receiver_blinding` - New blinding factors
69+
- `leaf_index`, `account_leaf_index`, `address` - Account position
70+
- `pathElements[26]` - Merkle proof
71+
72+
**Circuit constraints**:
73+
1. Verify sender knows preimage: `sender_commitment = Poseidon(sender_amount, sender_blinding)`
74+
2. Verify nullifier: `nullifier = Poseidon(sender_commitment, sender_blinding)`
75+
3. Range check: `sender_amount >= transfer_amount` (64-bit decomposition)
76+
4. Verify new sender commitment: `new_sender_commitment = Poseidon(sender_amount - transfer_amount, new_sender_blinding)`
77+
5. Verify receiver commitment: `receiver_commitment = Poseidon(transfer_amount, receiver_blinding)`
78+
6. Compute data_hash and account hash
79+
7. Verify Merkle proof against `expectedRoot`
80+
81+
## Errors
82+
83+
| Code | Name | Message |
84+
|------|------|---------|
85+
| 6000 | `ZeroAmount` | Zero amount |
86+
| 6001 | `Overflow` | Arithmetic overflow |
87+
| 6002 | `InsufficientFunds` | Insufficient funds in vault |
88+
| 6003 | `AccountNotEnoughKeys` | Not enough keys in remaining accounts |
89+
| 6004 | `InvalidProof` | Invalid proof |
90+
91+
## Dependencies
92+
93+
- Light Protocol SDK (compression, Merkle trees)
94+
- groth16-solana (on-chain proof verification)
95+
- circomlib (Poseidon, comparators)

0 commit comments

Comments
 (0)