协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
blockchain-zk【开发与工程】Zero-knowledge proofs, zk-rollup, zkEVM, Circom, Noir, Halo2, proof systems, Groth16, PLONK, STARK, recursive proofs, circuit optimization, zkSync, StarkNet, Scroll, Polygon zkEVM, and ZK application patterns. Covers proof system selection, circuit programming, prover infrastructure, and ZK rollup architecture. Do NOT use for: general cryptography (u…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Guide zero-knowledge proof integration in blockchain systems covering proof system selection, circuit programming, prover/verifier infrastructure, ZK rollup architecture, and application patterns. Enables building privacy-preserving and scalability-enhancing ZK solutions.
"zero-knowledge", "zk", "zkp", "groth16", "plonk", "stark", "plonkish", "circom", "noir", "halo2", "bellman", "arkworks", "snarkjs", "zk-rollup", "zkrollup", "zkevm", "recursive proof", "aggregation", "ivc", "nifs", "pcd", "circuit", "constraint", "r1cs", "acir", "ssa", "witness", "sequencer", "prover", "verifier", "zksync", "starknet", "scroll", "polygon zkevm", "taiko", "trusted setup", "ceremony", "toxic waste", "crs", "merkle proof", "nullifier", "commitment", "babyjubjub", "zkapp", "zk-application"
ZK architecture specification: proof system selection, circuit design, prover infrastructure, verifier deployment, and integration plan.
## [Topic]
- Proof System Category: <Groth16 / PLONK / STARK / PLONKish / Custom>
- DSL: <Circom / Noir / Halo2 / Leo / Zinc>
- Constraints: <number of constraints>
- Proving Time: <time estimate>
- Verification Gas: <gas cost on target chain>
- Security: <trusted setup? audit?>
- Recommendation: <best approach for this use case>
5000 tokens
ZK use case:
├── On-chain verification (EVM)?
│ ├── Fixed circuit → Groth16 (most gas-efficient)
│ │ ├── Pros: ~200K gas verify, smallest proof (~130 bytes)
│ │ ├── Cons: Trusted setup per circuit, no recursion naively
│ │ └── Best: Token privacy, identity, one-shot proofs
│ ├── Variable circuit → PLONK
│ │ ├── Pros: Universal trusted setup, flexible circuits
│ │ ├── Cons: Larger proof (~250 bytes), ~300K gas verify
│ │ └── Best: Multi-circuit systems, upgradable circuits
│ └── No trusted setup → STARK (via Ethereum validium)
│ ├── Pros: Transparent, quantum-resistant
│ ├── Cons: Large proofs (~100KB), expensive on-chain verify
│ └── Best: L2 rollups, high-value systems
├── Off-chain verification?
│ ├── Fast proving → STARK (FRI-based)
│ ├── Small proofs → Groth16
│ └── Recursive → IVC (Nova, Cyclefold)
└── ZK-rollup?
├── EVM-equivalent → Type 2/3 zkEVM (Scroll, Linea)
│ ├── Pros: Full EVM compatibility
│ └── Cons: Complex circuits (millions of constraints)
├── Custom VM → Type 4 zkEVM (ZKsync Era, StarkNet)
│ ├── Pros: Simpler circuits, faster proving
│ └── Cons: Not EVM-equivalent (compilation needed)
└── Specific use → Custom circuit (zk-application)
Circuit programming DSL:
├── EVM-focused Groth16?
│ ├── Circom 2 — largest ecosystem, best tooling
│ ├── Good: snarkjs, ffjavascript, quick integration
│ ├── Bad: Limited to R1CS, steep learning curve
│ └── Use: Token mixers, identity, airdrop proofs
├── Multi-backend abstraction?
│ ├── Noir — Aztec's DSL, compiles to multiple backends
│ ├── Good: Abstracted backend, ACIR IR, easy syntax
│ ├── Bad: Newer ecosystem, fewer libraries
│ └── Use: General ZK, cross-backend portability
├── Custom PLONKish gates?
│ ├── Halo2 — Zcash's framework, highest flexibility
│ ├── Good: Custom gates, lookup tables, ultra-PLONK
│ ├── Bad: Complex, Rust-only, steep learning curve
│ └── Use: High-performance circuits, custom arithmetization
└── Research / academic?
├── Arkworks — Rust ZK library ecosystem
└── Bellman — Rust ZK-SNARK library (old, replaced by halo2)
Groth16 PLONK STARK Halo2
Proof size 128B 250B 100KB 2-4KB
Verify time 5ms 10ms 50ms 15ms
Prove time 10s 30s 5min 2min
Trusted setup Per circ Universal None None
Recursion Hard Hard Native Possible
Quantum risk Yes Yes No Yes
Gas (EVM) 200K 300K 1M+ 400K+
pragma circom 2.1.0;
// Merkle tree inclusion proof circuit
template MerkleTreeInclusion(nLevels) {
signal input leaf;
signal input root;
signal input pathIndices[nLevels];
signal input siblings[nLevels];
signal output isIncluded;
// Compute Merkle root from leaf up
component hashes[nLevels];
signal computed[nLevels+1];
computed[0] <== leaf;
for (var i = 0; i < nLevels; i++) {
hashes[i] = Poseidon(2);
hashes[i].inputs[0] <== pathIndices[i] == 0
? computed[i]
: siblings[i];
hashes[i].inputs[1] <== pathIndices[i] == 0
? siblings[i]
: computed[i];
computed[i+1] <== hashes[i].out;
}
// Check computed root matches provided root
isIncluded <== computed[nLevels] == root;
}
component main = MerkleTreeInclusion(20);
pragma circom 2.1.0;
include "poseidon2.circom";
template Withdrawal(nLevels) {
signal input root; // Merkle root
signal input nullifierHash; // Unique spending key hash
signal input recipient; // Recipient address
signal input relayer; // Relayer address
signal input fee; // Relayer fee
signal input refund; // Change
// Private inputs
signal private input nullifier;
signal private input secret;
signal private input pathElements[nLevels];
signal private input pathIndices[nLevels];
// Compute commitment = hash(nullifier, secret)
component commitmentHasher = Poseidon(2);
commitmentHasher.inputs[0] <== nullifier;
commitmentHasher.inputs[1] <== secret;
signal commitment;
commitment <== commitmentHasher.out;
// Verify Merkle inclusion
component merkleProof = MerkleTreeInclusion(nLevels);
merkleProof.leaf <== commitment;
merkleProof.root <== root;
merkleProof.pathIndices <== pathIndices;
merkleProof.siblings <== pathElements;
merkleProof.isIncluded === 1;
// Compute nullifierHash
component nullifierHasher = Poseidon(1);
nullifierHasher.inputs[0] <== nullifier;
nullifierHash === nullifierHasher.out;
}
component main { public [root, nullifierHash, recipient, relayer, fee, refund] } = Withdrawal(20);
// Noir — Aztec's ZK DSL
// Merkle tree membership proof
use dep::std;
fn main(
leaf: Field,
root: Field,
path_indices: [Field; 20],
siblings: [Field; 20],
) -> pub bool {
let mut computed = leaf;
for i in 0..20 {
let left: Field;
let right: Field;
if path_indices[i] == 0 {
left = computed;
right = siblings[i];
} else {
left = siblings[i];
right = computed;
}
computed = std::hash::pedersen([left, right])[0];
}
computed == root
}
use halo2_proofs::{plonk::*, arithmetic::*, poly::*};
use pasta_curves::pallas;
// Custom chip for range check
#[derive(Debug, Clone)]
struct RangeCheckConfig {
value: Column<Advice>,
q_range: Selector,
}
impl<F: FieldExt> Chip<F> for RangeCheckChip {
type Config = RangeCheckConfig;
type Loaded = ();
fn configure(
meta: &mut ConstraintSystem<F>,
config: Self::Config,
) -> Self::Config {
// Create gate: value * (1 - value) = 0 (binary constraint)
meta.create_gate("range check", |meta| {
let q = meta.query_selector(config.q_range);
let value = meta.query_advice(config.value, Rotation::cur());
vec![q * value.clone() * (Expression::Constant(F::ONE) - value)]
});
config
}
}
interface ZKRollup {
// Sequencer: orders transactions, creates batches
sequencer: {
collect(): UserOperation[]
order(ops: UserOperation[]): OrderedBatch
submitBatch(batch: OrderedBatch): void
}
// Prover: generates validity proofs for batches
prover: {
generateProof(batch: OrderedBatch): ZKProof
// Can be: Groth16, PLONK, STARK, or recursive proof
}
// Verifier contract on L1
verifierContract: {
address: `0x${string}`
verifyProof(proof: ZKProof, stateRoot: bytes32): boolean
// Called by relayer after batch submission
}
// State: L2 state committed to L1 via state root updates
state: {
lastFinalizedStateRoot: bytes32
pendingBatches: Batch[]
}
}
| Type | EVM Equivalence | Proving Time | Compatibility |
|---|---|---|---|
| Type 1 | Full equivalence | Slow (hours) | 100% compatible |
| Type 2 | Full equivalence, optimized | Medium (minutes) | ~99% compatible |
| Type 2.5 | Partial equivalence | Medium | ~95% compatible |
| Type 3 | Near equivalence | Fast (minutes) | ~85% compatible |
| Type 4 | High-level equivalence | Fastest | Requires recompilation |
| Project | Type | Prover | Proving Time | Status |
|---|---|---|---|---|
| Scroll | Type 2/3 | Custom | Minutes | Mainnet |
| Linea | Type 2/3 | Custom | Minutes | Mainnet |
| ZKsync Era | Type 4 | Custom (Boojum) | Seconds | Mainnet |
| StarkNet | Type 4 | STARK prover | Seconds | Mainnet |
| Polygon zkEVM | Type 2 | Custom | Minutes | Mainnet |
| Taiko | Type 1 | Multiple (SGX, ZK) | Hours | Testnet |
| RISC Zero | zkVM | Custom | Minutes | Testnet |
// Recursive proof aggregation
// 1. Prove each batch individually → individual proofs
// 2. Prover aggregates N proofs into 1 recursive proof
// 3. L1 verifies single proof for N batches
// Benefits:
// - Verification cost: O(1) instead of O(N)
// - Parallel proving: each batch proven independently
// - Compression: N proofs → 1 proof
// Implementations:
// - IVC (Incrementally Verifiable Computation): Nova, Cyclefold
// - PCD (Proof-Carrying Data): continuous computation with proofs
// - Aggregation: combine N independent proofs (Halo2, plonk-verifier)
// Gas savings (EVM):
// N batches verified individually: N × 200K gas
// N batches via recursive proof: 200K gas (regardless of N)
| Vulnerability | Description | Prevention |
|---|---|---|
| Underconstrained | Circuit allows invalid witnesses | Formal verification of constraints |
| Missing range check | Integer overflow in circuit | Range check gates on all inputs |
| Non-deterministic witness | Multiple constraints don't pin value | Unique constraint per signal |
| Toxic waste exposure | Trusted setup data leaked | Secure multi-party computation |
| Hash function mismatch | Using prover-unfriendly hash | Poseidon, MiMC, or SHA-256 with optimizations |
| Frontrunning on proofs | Third-party submits proof first | Commit-reveal, nonce in public inputs |
| Weak FRI parameters | Insufficient query rounds | Follow ethSTARK parameter recommendations |
| Underpowered field | Field too small for security target | Use 256-bit+ field for 128-bit security |
1. Ceremony initiation: Define circuit, generate parameters
2. Multi-party computation: N participants contribute randomness
3. Each participant: generates local randomness, transforms parameters
4. Destroy toxic waste: all but last participant's randomness destroyed
5. Final parameters: last participant must destroy their randomness
6. Verification: verify final CRS against transcript
Risks:
- If any participant retains their randomness, fake proofs are possible
- Malicious participant can bias parameter generation
- Ceremony must be audited by independent third party
Current ceremonies:
- Groth16: Per-circuit ceremony required
- PLONK: Universal ceremony (one for all circuits, e.g., Perpetual Powers of Tau)
- STARK: No ceremony (transparent, hash-based)
Circuit size Constraints RAM GPU Memory Time
Small 1K-10K 8GB 4GB 1-10s
Medium 10K-100K 32GB 8GB 10-60s
Large 100K-1M 128GB 16GB 1-10min
zkEVM batch 1M-10M 512GB 48GB 10-60min
Recommended hardware:
├── GPU: RTX 4090 (24GB), A100 (40/80GB), H100 (80GB)
├── CPU: AMD EPYC 64-core, Intel Xeon 56-core
├── RAM: 256GB+ for zkEVM proving
├── Storage: NVMe SSD for circuit data
└── Network: 10Gbps for distributed proving
| Application | Proof System | Circuit Size | Best DSL |
|---|---|---|---|
| Private transfer (Tornado) | Groth16 | ~1K constraints | Circom |
| Identity (Semaphore) | Groth16 | ~500 constraints | Circom |
| zkAirdrop | Groth16 | ~1K constraints | Circom |
| zkDID (Polygon ID) | Groth16 | ~2K constraints | Circom |
| zkOracle (Axiom) | Halo2 | Variable | Halo2/EVM |
| ZK coprocessor | Halo2/PLONK | Variable | Halo2 |
| On-chain ZKML | PLONKish | 100K-1M+ | Custom |
ZK System Selection
├── Use case?
│ ├── Rollup (L2 scaling) → zkEVM (zkSync, Scroll, Linea)
│ ├── Privacy → Tornado-style mixer / Aztec (private DeFi)
│ ├── Identity → zk-SNARKs for verifiable credentials
│ └── Scaling (validium) → StarkEx / zk-Porter (data off-chain)
├── Proof system?
│ ├── SNARK (small proofs) → Groth16 (fast verification, trusted setup)
│ ├── STARK (transparent) → StarkWare / Plonky2 (no trusted setup, larger proofs)
│ └── Recursive → Recursive SNARKs (Nova, IVC)
├── Development framework?
│ ├── Circom → Low-level circuit DSL (mature, large ecosystem)
│ ├── Noir → Rust-like ZK language (Aztec)
│ └── zkSync Era → Solidity-like (EVM compatible)
└── Prover infrastructure?
├── Cloud → AWS/GCP GPU instances (NVIDIA A100)
├── Decentralized → EigenLayer AVS (decentralized proving)
└── Embedded → Client-side proving (mobile, browser)
Decision criteria: Evaluate proof size vs verification cost, trusted setup tolerance, developer experience, and proving time requirements.
// blockchain-zk/circuits/merkle_tree.circom
pragma circom 2.1.0;
include "circomlib/merkle_tree/MerkleTree.circom";
template MerkleProofCheck(nLevels) {
signal input leaf;
signal input root;
signal input proof[nLevels];
signal input indices[nLevels];
component tree = MerkleTree(nLevels);
tree.leaf <== leaf;
tree.root <== root;
for (var i = 0; i < nLevels; i++) {
tree.siblings[i] <== proof[i];
tree.indices[i] <== indices[i];
}
root === tree.root;
}
component main = MerkleProofCheck(10);
// blockchain-zk/contracts/Verifier.sol
pragma solidity ^0.8.20;
contract Groth16Verifier {
struct Proof {
uint256[2] a;
uint256[2][2] b;
uint256[2] c;
}
function verifyProof(Proof memory proof, uint256[1] memory input) public view returns (bool) {
uint256[14] memory verifyingKey = [
0x2c0a, 0x0e09, 0x1a1b, 0x2c2d, 0x3e3f,
0x1234, 0x5678, 0x9abc, 0xdef0, 0xabcd,
0xef01, 0x2345, 0x6789, 0x0123
];
return _verify(proof, verifyingKey, input);
}
}
| Anti-Pattern | Consequence | Solution |
|---|---|---|
| Using Groth16 without trusted setup ceremony | Security risk from toxic waste | Complete MPC ceremony with verification |
| Over-constrained circuits | Slow proving, high gas | Minimize constraints; optimize bottlenecks |
| Ignoring witness generation time | Block time violations | Parallelize witness gen; optimize circuit |
| No circuit fuzzing | Undetected soundness bugs | Fuzz circuit inputs against reference impl |
| Single prover | Centralization, censorship risk | Decentralize proving (multiple provers) |