Skip to content

Commit c761176

Browse files
committed
stash third review
1 parent ed015c6 commit c761176

1 file changed

Lines changed: 37 additions & 30 deletions

File tree

zk-compression-blog.md

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,59 +24,47 @@ For example, in a private KYC program, the private input is your credential, the
2424

2525
### Poseidon Merkle Tree
2626

27-
A Poseidon Merkle tree is a binary tree where each node is the hash of its children.
27+
Merkle trees store zk application state.
28+
Specifically, a Poseidon Merkle tree is a binary tree where each node is the hash of its children.
2829
Poseidon is designed for ZK circuits - it uses fewer constraints (TODO: add reference) than SHA256, making proofs faster and cheaper to generate.
2930

30-
**Merkle tree: custom vs state trees**
31+
**Merkle trees on Solana:**
3132

32-
A custom sparse Merkle tree gives you full control. You design the leaf structure and proof format to match your circuit exactly. The tradeoff: you build and run your own indexer to track the tree and serve Merkle proofs.
33+
A **custom sparse Merkle tree** gives you full control. You design the leaf structure and proof format to match your circuit exactly. Create Solana accounts with your program to store a sparse Merkle tree and its roots.
34+
The tradeoff: you build and run your own indexer to track the tree and serve Merkle proofs.
3335

34-
State trees handle indexing for you. Standard Solana RPCs serve Merkle proofs. The tradeoff: your circuit must prove inclusion of your data inside the compressed account (accounts stored in Poseidon Merkle trees with Solana RPC support) structure. This adds constraints to your circuit but eliminates infrastructure overhead.
35-
36-
```
37-
Compressed Account Hash:
38-
+----------------------------------------------------------+
39-
| Poseidon( |
40-
| owner_hash, |
41-
| leaf_index, |
42-
| merkle_tree_pubkey, |
43-
| address, |
44-
| discriminator, |
45-
| data_hash <-- developer-defined, hash anything here |
46-
| ) |
47-
+----------------------------------------------------------+
48-
```
49-
50-
The `data_hash` is entirely yours. Hash whatever structure your application needs. The outer fields are protocol overhead, but they don't limit what you store inside.
36+
**Zk compression state Merkle trees** Solana RPCs handle indexing for you and serve Merkle proofs. The Light Protocol programs create and maintain Poseidon state Merkle trees for you in Solana accounts. Once a state Merkle tree fills up the protocol creates a new one.
37+
The tradeoff: your circuit must prove inclusion of your data inside the compressed account structure. Compressed accounts are stored as hashes in Poseidon Merkle trees with Solana RPC support. This adds constraints to your circuit but RPCs index the Merkle tree for you.
5138

5239
Note, for offchain privacy a user client should fetch a complete (sub)tree not Merkle proof from an indexer. If only onchain privacy is sufficient fetching Merkle proofs from an indexer is more efficient.
5340

5441

5542
### Nullifier
56-
A nullifier is a hash derived from your secret and the leaf the transaction is using.
43+
Nullifiers prevent double spending.
44+
In detail, a nullifier is a hash derived from your secret and the leaf the transaction is using.
5745
When you use private state (stored in a Merkle tree leaf), you publish the nullifier. The program stores it in a set.
5846
If anyone tries to spend the same leaf again, the nullifier would match one already stored, so the transaction fails.
5947
The nullifier reveals nothing about which leaf was spent.
6048
Different state produces different nullifiers, so observers can't link a nullifier back to its source leaf.
6149

62-
**Nullifier storage: PDAs vs compressed addresses**
50+
**Nullifiers on Solana:**
6351

64-
PDAs are the straightforward choice. Derive an address from the nullifier hash, create an account there. If the account exists, the nullifier was used. The cost is ~899k lamports per nullifier for rent exemption.
52+
**PDAs** are a straightforward choice. Derive an address from the nullifier hash, create an account there. If the account exists, the nullifier was used. The cost is ~899k lamports per nullifier for rent exemption.
6553

66-
Compressed addresses work the same way but cost ~10k lamports. The tradeoff: you need an additional ZK proof to create the account and a CPI to the Light system program. If you're already generating a ZK proof for your application logic, the marginal cost of the extra proof is low. If not, PDAs are simpler.
54+
**Compressed addresses** work the same way but cost ~10k lamports. The tradeoff: you need an additional ZK proof to create the account and a CPI to the Light system program. If you're already generating a ZK proof for your application logic, the marginal cost of the extra proof is low. If not, PDAs are simpler.
6755

6856

6957
## Zk Id example
7058

71-
zk-id is a proof of concept credential system built with zk compression and the following tools:
59+
[zk-id](https://github.com/Lightprotocol/program-examples/tree/main/zk-id) is a proof of concept credential system built with zk compression and the following tools:
7260

7361
| Component | Implementation |
7462
|-----------|----------------|
75-
| Merkle leaves | compressed accounts (light-sdk) |
76-
| Nullifiers | compressed addresses (light-sdk) |
77-
| Circuit | circom |
78-
| Proof generation | circom-prover (Rust) |
79-
| On-chain verification | groth16-solana |
63+
| Merkle leaves | [compressed accounts](https://github.com/Lightprotocol/program-examples/blob/main/zk-id/src/lib.rs#L141) (light-sdk) |
64+
| Nullifiers | [compressed addresses](https://github.com/Lightprotocol/program-examples/blob/main/zk-id/src/lib.rs#L192) (light-sdk) |
65+
| Circuit | [circom](https://github.com/Lightprotocol/program-examples/tree/main/zk-id/circuits) |
66+
| Proof generation | [circom-prover](https://github.com/Lightprotocol/program-examples/blob/main/zk-id/tests/test.rs#L575) (Rust) |
67+
| On-chain verification | [groth16-solana](https://github.com/Lightprotocol/program-examples/blob/main/zk-id/src/lib.rs#L269) |
8068

8169
### Creating a Credential
8270

@@ -185,3 +173,22 @@ The indexer watches the blockchain and maintains a local copy of the Merkle tree
185173
**Zk compression**
186174
- **light-hasher** - Poseidon/SHA256 implementations matching circuit behavior
187175
- **light-sdk** - Compressed accounts, state trees, address derivation
176+
177+
## Appendix
178+
179+
1. Compressed Account Hashing:
180+
```
181+
Compressed Account Hash:
182+
+----------------------------------------------------------+
183+
| Poseidon( |
184+
| owner_hash, |
185+
| leaf_index, |
186+
| merkle_tree_pubkey, |
187+
| address, |
188+
| discriminator, |
189+
| data_hash <-- developer-defined, hash anything here |
190+
| ) |
191+
+----------------------------------------------------------+
192+
```
193+
194+
The `data_hash` is entirely yours. Hash whatever structure your application needs. The outer fields are protocol overhead, but they don't limit what you store inside.

0 commit comments

Comments
 (0)