这四种语言位于 zk 技术栈(zk stack)的不同层次:
最大的实际差异在于:
这个差异在你构建“先承诺(commit now)、后揭示(reveal later)”流程时尤其重要,因为在 Compact/Leo/Cairo 中,持久化(persistence)和状态迁移(state transitions)是一等概念;而在 Noir 中,你通常会把问题拆成:
Compact 是一种合约语言(contract language),而不只是电路 DSL(domain-specific language)。官方示例使用如下语法:
export circuit owner(): Either<ZswapCoinPublicKey, ContractAddress> { ... }
export ledger owner: Either<ZswapCoinPublicKey, ContractAddress>;
这会立刻告诉你两件重要的事:
export ledger 声明。export circuit 声明。其类型系统相当紧凑,并且面向合约。从官方示例和库中可以看到:
u64Bytes<32>Either<A, B>ZswapCoinPublicKey 和 ContractAddressCompact 围绕 Midnight 上的隐私保护执行(privacy-preserving execution)而设计,因此“哪些是公开状态(public state),哪些是私下证明(proved privately)的内容”比普通 EVM 语言更核心。
作为初学者,你需要关心的主要原语是:
哈希辅助函数的语法部分,是我在交付代码前会根据精确的 2026 标准库(stdlib)版本再确认的内容。
下面是一个 Compact 风格的草图,使用了官方示例中的真实结构语法(export ledger、export circuit、类型化存储)。我将哈希辅助函数标记为 TODO,因为其确切函数名/签名对版本较敏感。
pragma language_version >= 0.16.0;
// TODO: verify exact import path for hashing helpers in current Midnight Compact stdlib.
import CompactStandardLibrary;
export ledger commitment: Bytes<32>;
export ledger revealed: bool;
constructor(initial_commitment: Bytes<32>) {
commitment = initial_commitment;
revealed = false;
}
export circuit get_commitment(): Bytes<32> {
return commitment;
}
export circuit reveal(secret: Field, nonce: Field): [] {
// TODO: verify exact hash helper name and return type in current Compact reference.
// Example intent: hash(secret, nonce) -> Bytes<32>
let recomputed: Bytes<32> = persistentHash(secret, nonce);
assert(recomputed == commitment);
assert(revealed == false);
revealed = true;
}
语义如下:
H(secret, nonce)。reveal(secret, nonce) 在电路内部重新计算哈希值。这正是理解 Compact 的正确心智模型:合约状态 + 具备证明感知(proof-aware)的执行,而不是“我先写一个纯电路,再在别处想办法处理状态”。
Compact 的成熟度,很大程度上取决于 Midnight 生态的成熟度。
通常较好的方面:
与当前 Cairo 相比通常较弱的方面:
在调试方面,你应预期其工具表面(tool surface)会比主流 EVM 或 Starknet 更窄。在隐私保护语言中,调试通常是以下方法的组合:
测试质量将取决于你所使用版本中的 Midnight SDK 和本地开发工具(local dev tooling)。我预期,到 2026 这一决策时间点,关键问题不再是“我能不能写测试?”,而是“我的团队能多快诊断证明失败(proof failures)和状态迁移缺陷(state-transition bugs)?”
截至 2024 年中之前的当前公开状态(public state),Midnight 尚未处于与 Starknet 相同的生产部署(production-deployment)类别。你应将 Compact 视为一种生态特定语言(ecosystem-specific language):它有很有前景的隐私目标,但对于需要最深厚现有生产历史的团队来说,它还不是默认答案。
在以下情况下选择 Compact:
在以下情况下不要选择它:你的真实需求是“今天把一个 zk 应用部署到最经受实战检验的在线证明型 L2 上”。那会更倾向于 Cairo/Starknet。
Leo 是面向 Aleo 程序(programs)的一种高级语言(high-level language)。官方语法如下:
program hello.aleo;
function main:
input r0 as u32.public;
add r0 r0 into r1;
output r1 as u32.public;
而在更高层的 Leo 示例中:
transition main(a: u32, b: u32) -> u32 {
return a + b;
}
其类型系统包括:
u8、u16、u32、u64 等field关键原语包括:
program name.aleo;Leo 比 Noir 更具意见化(opinionated)。你不只是写一个电路;你是在为 Aleo 的执行模型(execution model)编程。
一个干净的 Aleo 设计是:
H(secret, nonce) == commitment由于精确的 async/finalize 细节在不同版本中发生过变化,我会在存储交互处加上 TODO 注释。
program commit_reveal.aleo;
// TODO: verify exact mapping syntax in current Leo reference.
mapping commitments: field => bool;
transition make_commitment(secret: field, nonce: field) -> field {
let commitment: field = BHP256::hash_to_field(secret, nonce);
return commitment;
}
// User can submit the computed commitment for storage.
// TODO: verify whether this should be `async transition ... -> Future` in current Leo/Aleo version.
async transition commit(commitment: field) -> Future {
return finalize_commit(commitment);
}
async function finalize_commit(commitment: field) {
commitments.set(commitment, true);
}
// Reveal proves correctness privately, then updates state publicly.
// TODO: verify exact mapping read helper and finalize syntax.
async transition reveal(secret: field, nonce: field, commitment: field) -> Future {
let recomputed: field = BHP256::hash_to_field(secret, nonce);
assert_eq(recomputed, commitment);
return finalize_reveal(commitment);
}
async function finalize_reveal(commitment: field) {
let exists: bool = commitments.get_or_use(commitment, false);
assert_eq(exists, true);
commitments.set(commitment, false);
}
这里在语法上真实存在的部分包括:
program name.aleo;transition ...fieldBHP256::hash_to_field(...) 这样的 Aleo 风格哈希命名你需要针对具体编译器版本核对的内容包括:
但在概念上,这非常符合 Leo 的惯用风格:在 transition 中完成私有计算(private computation),然后在 finalize 中完成持久状态变更(persistent state mutation)。
Leo 最大的优势是纵向集成(vertical integration)。如果你是在为 Aleo 构建应用,那么语言、证明器(prover)和执行环境(execution environment)是协同设计的。
这会带来:
相较主流智能合约开发,仍然更难的点在于:
对于纯 transition 逻辑,测试通常相对直接。对于有状态程序,你需要更谨慎,因为你测试的是:
Aleo 工具链已经达到具有实际可用性的程度,但其社区与运维生态(ops ecosystem)仍小于 Starknet。
Aleo 已上线,并且拥有真实应用和开发者活动。但如果你问“这四者中,哪一个拥有最深厚、可见的生产级智能合约部署历史?”,Leo 在公开 DeFi/应用体量和运行历史上,仍然落后于 Cairo/Starknet。
在以下情况下选择 Leo:
在以下情况下不要选择 Leo:
如果你熟悉 Rust 风格语法,那么 Noir 是这四者中最容易读懂的。官方 Noir 语法如下:
fn main(x: Field, y: pub Field) {
assert(x == y);
}
这一行已经展示了三个核心概念:
fn main(...)Field 作为默认算术类型(default arithmetic type)pub 用于公共输入(public inputs)其类型系统包括:
Fieldu8、u16、u32、u64bool最重要的实际原语是:Noir 从根本上说是一种电路语言(circuit language)。这意味着它非常擅长表达如下证明约束(proof constraints):
仅靠它本身,并不会提供合约存储语义(contract storage semantics)。在 Aztec 技术栈中,Noir 需要与 Aztec 的合约/运行时模型(contract/runtime model)配合使用。
这个模式中,纯 Noir 部分很简单:验证被揭示的秘密(secret)和随机数(nonce)确实能打开(open)一个先前已发布的承诺值(commitment)。
use dep::std::hash::poseidon;
fn main(secret: Field, nonce: Field, commitment: pub Field) {
let recomputed = poseidon::hash_2([secret, nonce]);
assert(recomputed == commitment);
}
Poseidon 的精确 import 路径可能因 Noir 标准库版本而变化;如有需要:
// TODO: verify exact poseidon module path for the target Noir release.
这是四者中最干净的电路。它精确表达了“必须被证明的内容”。
如果你使用的是 Aztec,那么“先承诺、后揭示”流程通常会被拆分为:
(secret, nonce) 可以打开该承诺值一个合约层级的 Aztec 草图在结构上大致如下,但我会在用于文档前核对精确的 2026 Aztec 合约语法:
// TODO: verify exact Aztec contract syntax, storage declarations, and hash helper names.
#[aztec]
contract CommitReveal {
#[storage]
struct Storage {
// pseudostructure: map commitment => used flag
}
#[private]
fn reveal(secret: Field, nonce: Field, commitment: Field) {
let recomputed = poseidon::hash_2([secret, nonce]);
assert(recomputed == commitment);
// read commitment from storage / note set
// mark consumed
}
}
因此,正确的比较点应是:
Noir 的工具链是其最强卖点之一。
初学者喜欢它的原因:
难点不在 Noir 本身,而在其下层或周边:
在测试方面,Noir 非常适合电路单元测试(circuit-unit tests)。对于完整 Aztec 应用,其成熟度更多取决于 Aztec 的合约框架与 devnet 工具,而不是语言本身。
在这里,你必须区分 Noir 与 Aztec:
因此,如果问题是“作为一种电路语言,Noir 是否已经过生产验证(production-proven)?”,答案是:在更广泛的 zk 工程意义上,是的。
如果问题是“今天的 Aztec 合约部署是否像 Starknet 一样成熟且经过充分实战检验(battle-tested)?”,答案是否定的。
在以下情况下选择 Noir:
如果你的真实需求是“一个完整、成熟、有状态的链语言,并且拥有大量生产部署”,那么不要单独选择 Noir。此时通常 Cairo 更合适。
Cairo 是这里最偏生产导向(production-heavy)的选择。现代 Cairo 1 语法类似 Rust。官方合约语法如下:
#[starknet::contract]
mod Counter {
#[storage]
struct Storage {
value: u128,
}
#[external(v0)]
fn increment(ref self: ContractState) {
let value = self.value.read();
self.value.write(value + 1);
}
}
类型系统中的关键组成包括:
felt252 作为基础域元素(base field element)u8、u16、u32、u64、u128、u256对 zk 应用开发者而言,关键原语是:
Cairo 比 Leo 或 Noir 更底层,因为你离被证明的机器(machine that gets proved)更近。
下面是一个较为真实的 Cairo 1 Starknet 合约草图:
#[starknet::contract]
mod CommitReveal {
use starknet::storage::{Map, StorageMapReadAccess, StorageMapWriteAccess};
use poseidon::poseidon_hash_span;
#[storage]
struct Storage {
commitments: Map<felt252, bool>,
}
#[external(v0)]
fn commit(ref self: ContractState, commitment: felt252) {
self.commitments.write(commitment, true);
}
#[external(v0)]
fn reveal(ref self: ContractState, secret: felt252, nonce: felt252) {
let mut inputs = array![];
inputs.append(secret);
inputs.append(nonce);
let recomputed = poseidon_hash_span(inputs.span());
assert(self.commitments.read(recomputed), 'UNKNOWN_COMMITMENT');
self.commitments.write(recomputed, false);
}
#[view]
fn is_committed(self: @ContractState, commitment: felt252) -> bool {
self.commitments.read(commitment)
}
}
其行为是:
commit() 存储一个承诺值。reveal() 在链上(onchain)重新计算 Poseidon(secret, nonce)。这不是 Noir 意义上的独立 zkSNARK 电路(separate zkSNARK circuit)。在 Cairo 中,程序执行(program execution)本身就是在 Starknet/STARK 模型下被证明的对象。
对于面向生产的智能合约工程(production-oriented smart contract engineering)而言,Cairo/Starknet 是这里最强的选择。
优势:
弱点:
Cairo 的测试能力足以支持真实团队。与大多数 zk 系统相比,你会获得一种更标准的合约开发体验:写函数、测试存储效果、断言错误、模拟调用。
在调试方面,它也显著优于较年轻的隐私优先生态,因为已经有更多人踩过相同类型的坑。
这是四者中最明确的答案:
如果你的老板问:“这份列表中,哪种语言最有力地证明了当下的生产使用?”答案是 Cairo。
在以下情况下选择 Cairo:
如果你的最高优先级是“用最简单、最干净的语法来表达一个独立证明(standalone proof)”,那就不要选 Cairo。对此,Noir 通常更好。
| 语言 | 最适合被理解为 | 核心公开语法提示 | 类型系统风格 | 状态模型 | 证明模型 | 工具链成熟度 | 当下的生产部署 | 最佳适用场景 |
|---|---|---|---|---|---|---|---|---|
| Midnight Compact | 隐私优先合约语言 | export ledger, export circuit, Either<...> |
合约特化、链原生隐私类型 | 内建合约存储 | Midnight 上的可证明合约执行 | 新兴中 | 相比 Starknet 有限 | Midnight 原生私有应用 |
| Aleo Leo | Aleo 程序语言 | program x.aleo;, transition, field, mappings/finalize |
高层、面向应用 | Mappings + finalize 流程 | Aleo 模型内的私有执行 | 中等,纵向集成强 | 真实存在但生态较小 | 需要集成式隐私的 Aleo 应用 |
| Noir (Aztec) | 首先是电路语言,其次才是 Aztec 应用语言 | fn main(...), Field, pub, assert(...) |
干净、表达力强、以电路为中心 | 纯 Noir 中不原生提供;由 Aztec/运行时提供 | 显式电路约束 | 电路层强,完整应用栈中等 | Noir 是;Aztec 运行时不如 Starknet 成熟 | 电路编写、Aztec 私有应用 |
| StarkNet Cairo | 面向生产的智能合约/可证明执行语言 | #[starknet::contract], #[storage], felt252, read()/write() |
更底层、系统导向 | 原生合约存储 | 通过 STARK 证明执行轨迹 | 整体最强 | 显著最强 | 生产级 Starknet 应用 |
使用下面这个决策树。
如果我要把这些压缩成四条直接建议:
对于特定的“提交秘密、稍后揭示、证明正确性”模式:
如果你仍然不确定,可以按风险承受能力来默认选择:
读到这里很感谢。如果 “Compact / Leo / Noir / Cairo 对比” 这个话题正好契合你目前的工作,下面三条路径任选其一:
完整的 Midnight ZK Cookbook 索引 覆盖 Midnight、Aleo、Aztec、Noir、risc0 共 17 篇英文教程加 4 篇中文翻译,按生态分组列出。
Bounty Radar 聚合 Algora、GitHub labels、Drips Wave、Code4rena、Bountycaster 的开放 ZK bounty。Bounty Radar 主页 bounty-radar-data 或任一生态子 widget:https://battam1111.github.io/bounty-radar-data/widget.html?ecosystem=<aleo|aztec|cairo|midnight|noir|risc0>。免费版需轮询;$19/月 Hobbyist 档 会把一个 filter 实时推送到 Telegram。
zk-pipeline-doctor 是 MIT 开源的免费 CLI,对任意 ZK 项目按测试、CI、文档、安全、可复现性、语言工具链六个维度打分(支持 Compact、Leo、Noir、Cairo 和 7 个 Rust zkVM)。用 zk-doctor-action 接进 GitHub Action 可获得 PR diff 评论。$15/月 Pro 档 增加 4 个跨生态深度检测器(电路复杂度、证明系统坑点、verifier soundness、多文件一致性)。
由 AI 辅助起草,作者在发布前逐行审阅。完整流程见 DISCLOSURE。