跳转到主要内容

如何使用零知识证明在以太坊上构建隐私应用

零知识证明
隐私
菲利普·克劳斯
EF Builder Growth
2026年5月12日
12 分钟阅读

以太坊在设计上是极其公开的。任何拥有区块浏览器的人都可以看到每个地址、余额、交易、合约调用和事件。当你需要可验证性时,这种透明度是有用的。但当用户需要投票、申领、提款或证明成员身份,而不希望将每次操作都关联回同一个钱包时,这就成了一个问题。

匿名成员身份是一种可复用的模式,为以太坊上的大量隐私应用提供了动力。人们首先注册,然后证明他们属于该群体,而不透露他们具体是哪位成员。零知识证明是注册钱包和操作钱包之间的桥梁,这座桥梁不会透露是谁穿过了它。

周边的产品会发生变化,但隐私骨架保持不变。

通过匿名投票解释该模式

该模式包含三个部分。承诺用于注册每个成员。默克尔树将这些承诺汇聚成一个群体。证明和无效化器(nullifier)让一名成员可以操作一次,而不透露是哪位成员进行了操作。

第一步:注册

每个投票者在链下创建两个私有值:秘密值(secret)和无效化器(nullifier)。投票者将这些值哈希成一个公开的承诺,然后将该承诺注册到链上。

承诺是公开的注册记录。秘密值和无效化器是投票者稍后需要的私密票据(note)。如果丢失了该票据,投票者就无法证明其成员身份。如果泄露了它,其他人就可能代替该用户进行投票。

因为承诺是一个哈希,观察者无法恢复其中的私有值。承诺表明“有人注册了”,但不会透露稍后谁将使用该注册。

第二步:构建群体

随着越来越多投票者的注册,应用将他们的承诺收集到一棵默克尔树中。默克尔树将一长串值压缩成一个单一的哈希,称为根(root)。更改列表中的任何值,哈希就会改变,因此根充当了整个集合的防篡改摘要。

这棵树就是你的匿名集。如果树中有十个用户,观察者可以将随后的操作范围缩小到这十个人之一。如果树中有一万个用户,就很难将操作与某个人联系起来。一个匿名集很小的隐私应用通常不是非常私密,即使密码学是正确的。

第三步:匿名操作

当投票开始时,投票者不应使用注册承诺的同一个钱包进行投票。使用注册钱包投票会直接将投票与注册者联系起来,从而使隐私保护工作前功尽弃。相反,投票者会创建一个零知识证明。该声明被编码为一个电路,表示:“我知道能生成已注册承诺的私有值,并且我正在为这次投票揭示正确的无效化器哈希。”

该证明使验证者合约确信该声明是真实的。它不会透露秘密值、无效化器,也不会透露使用了哪个承诺。

无效化器是防止重复投票的机制。在提供证明的同时,投票者会发布一个无效化器哈希。投票合约在接受投票后会存储该哈希。如果同一私密票据再次用于同一次投票,它将生成相同的无效化器哈希,合约将拒绝第二次投票。结合证明,这使得合约只知道某个注册投票者操作了一次,而不知道具体是哪一个。

可复用的门控

同样的证明和无效化器组合不仅适用于投票。抛开投票的场景,你得到的是一个用于智能合约函数的隐私门控。

在函数运行之前,合约会检查默克尔根,验证证明,确认无效化器哈希未被使用,并将公共输入绑定到正确的应用、链、投票、申领或提款上。如果这些检查通过,它会将无效化器标记为已使用,并运行函数的其余部分。

将这个门控放在投票前面,你就得到了匿名投票。将它放在空投申领前面,你就得到了匿名申领。将它放在提款函数前面,你就得到了混币器式提款流程的核心。相同的承诺树,相同的无效化器理念,相同的证明模式。改变的只是函数体和周边的应用逻辑。

各部分在哪里运行

隐私工作通常在链下进行。用户存储票据,客户端应用构建见证数据并运行证明者来生成证明。索引器跟踪承诺和默克尔根。捆绑器将用户操作(UserOperation)传播到链上,并由 ERC-4337 代付合约赞助 Gas,因此一个全新的钱包不需要首先从用户已知的钱包中获取 ETH。

公开的执行发生在链上。验证者合约检查证明。应用合约检查有效的根和未使用的无效化器,存储无效化器哈希,并运行公开操作。

敏感的用户体验(UX)在于票据处理。应像对待密钥一样对待秘密值和无效化器。不要将它们放入分析、日志、URL、错误报告或常规的服务器端遥测中。一旦票据泄露,无论证明多么强大,隐私都将不复存在。

工具已经跟上

你不需要手动编写底层的密码学代码。一种常见的途径是使用高级零知识语言编写电路,生成一个 Solidity 验证者,然后从应用合约中调用该验证者。

合适的技术栈取决于具体任务。使用 snarkjs 的 Circom 是应用级电路的一条历史悠久的途径。使用 Barretenberg 的 Noir 是一条较新的、对开发者友好的途径。Halo2 和 gnark 是更底层的电路库。诸如 RISC Zero 或 SP1 之类的 zkVM 可以证明普通程序,但其证明成本可能比小型自定义电路更高。

对于匿名成员身份,在编写自己的电路之前,请先寻找现有的协议。Semaphore 将群组成员身份和基于无效化器的防双重使用机制打包到了合约和 JavaScript 库中。对于隐私投票和治理,MACI 是专门的途径,因为它增加了抗共谋的特性。成熟的协议通常比新电路更安全。

仅有证明是不够的

如果钱包流程泄露了关联,即使是完美的证明也会失效。从钱包 A 注册,随后又从钱包 A 操作,任何观察者都能将这些交易联系起来。在操作之前直接从钱包 A 向钱包 B 提供资金,这笔资金交易也会产生同样的问题。

这就是为什么捆绑器和代付合约如此重要。操作钱包应该是全新的,并且它不需要从用户试图与操作分离开的钱包中接收 ETH。

同样的问题也存在于链下。从同一个 IP 地址、RPC 提供商或会话提交注册和操作交易,可能会削弱电路提供的隐私保护。前端可能会通过分析、本地存储和支持日志发生泄露。零知识证明隐藏了证明内部的值。它并不能隐藏交易周围的一切。

公共输入是隐私应用失败的另一个地方。在电路中标记为公开的、作为事件发出的、包含在调用数据(calldata)中的,或由合约存储的任何内容都是可见的。审查公共输入应像审查 Solidity 合约上的访问控制一样仔细。

这为构建者带来了哪些改变

以太坊上的隐私是可交付的。构建者可以将各个部分组合成实际的应用。该技术栈包括用于私有声明的电路、用于证明检查的验证者、用于公共规则的应用合约、用于默克尔数据的索引器,以及用于无关联提交和 Gas 赞助的捆绑器加代付合约。

困难的部分在于产品设计、密钥管理、元数据卫生、审计以及扩大匿名集。如果其中任何一个环节出错,证明所提供的隐私就会荡然无存。

延伸阅读

  1. 零知识证明 (ethereum.org) (在新标签页中打开)
  2. Semaphore 文档 (在新标签页中打开)
  3. MACI 文档 (在新标签页中打开)
  4. Circom 文档 (在新标签页中打开)
  5. Noir 文档 (在新标签页中打开)
  6. Halo2 手册 (在新标签页中打开)
  7. gnark 文档 (在新标签页中打开)
  8. RISC Zero 文档 (在新标签页中打开)
  9. SP1 文档 (在新标签页中打开)
  10. EIP-4337:通过 EntryPoint 合约实现账户抽象 (在新标签页中打开)