Bulletproofs 详解:隐藏的金额如何被验证

· 6 分钟阅读

Bulletproofs 是一种简短的零知识范围证明,它让加密货币能够隐藏每一笔交易金额,同时每个节点仍能检查没有凭空产生新币。本文讲解 Pedersen 承诺、隐藏的金额为什么需要范围证明、Bulletproofs 如何让这些证明保持小巧,以及 Blocknet 如何在每个输出上使用它们。

什么是 Bulletproofs?

简短回答

Bulletproofs 是一种简短的零知识证明,由 Bünz、Bootle、Boneh、Poelstra、Wuille 和 Maxwell 于 2018 年发表。在加密货币中,它证明一个隐藏的金额位于有效范围内,例如 0 到 2^64,而不透露这个金额。它不需要可信设置,证明大小只随范围的对数增长。

这篇论文 Bulletproofs: Short Proofs for Confidential Transactions and More 发表于 2018 年的 IEEE 安全与隐私研讨会(IEEE Symposium on Security and Privacy)。作者为机密交易设计了它:在机密交易中,区块链上的金额被替换为承诺。要理解为什么需要这样的证明,先从承诺说起。

什么是 Pedersen 承诺?

承诺是密封信封的密码学版本。你把一个数字放进去,把信封展示给所有人,之后可以证明里面装的是什么。没有人能从信封上读出这个数字,你事后也无法换成另一个数字。

Pedersen 承诺由 Torben Pedersen 在 1991 年提出,它用椭圆曲线上的点来实现这一点。要对金额 v 做承诺,钱包会选一个随机秘密 r,称为盲化因子,然后计算:

C = v·G + r·H

G 和 H 是曲线上两个固定的公开点,其选取方式保证没有人知道如何把其中一个写成另一个的倍数。结果 C 是一个 32 字节的点。它有两个性质:

节点如何检查一笔它读不懂的交易?

简短回答

Pedersen 承诺可以相加。两个承诺之和就是对两个金额之和的承诺,盲化因子也随之相加。节点把一笔交易的输入承诺加总,再把输出承诺加上手续费加总,检查两个总数是否相等,这样无需看到任何金额就能确认收支平衡。

两个承诺相加得到 (v1 + v2)·G + (r1 + r2)·H,它本身就是对 v1 + v2 的承诺。发送方会安排好盲化因子,使输入的盲化因子总和等于输出的总和。如果金额也平衡,输入之和与输出之和的差就恰好为零,任何节点都能验证这一点。

网络手续费是交易中唯一公开的数字。节点把它转换成盲化因子为零的承诺,加到输出一侧,于是检查式就是:输入 = 输出 + 手续费。

隐藏的金额为什么需要范围证明?

承诺的运算是对一个非常大的数取模进行的,这个数就是曲线群的阶,约为 2^252。在这种运算中,“负数”金额其实就是一个巨大的正数。有人可以花掉一个价值 10 个币的输入,创建两个输出,一个价值 11,一个价值“负 1”。承诺会完全平衡,于是 1 个币凭空出现,而且所有观察者都看不到。

范围证明堵住了这个漏洞。它证明承诺中的金额位于 0 到 2^64 之间,而不说出具体是多少。每个输出都受到这样的约束后,几个输出相加永远不可能大到绕过群的阶,因此承诺平衡就意味着金额平衡。

早期的机密交易设计使用的范围证明随位数线性增长。Bulletproofs 只随位数的对数增长,使一个 64 位证明缩小到几百字节。

Bulletproofs 是如何工作的?

一个 Bulletproof 分三步证明一个范围。

  1. 把金额拆成位。 一个 64 位金额变成一个由 64 个位组成的向量。证明者对这个向量做承诺,并证明它的两个事实:每一项都是 0 或 1,并且这些位按 2 的幂加权后的和等于被承诺的金额。
  2. 把这些事实变成一个内积。 借助随机挑战,证明者把所有这些条件折叠成一个关于两个向量内积的命题。由于挑战是随机的,一个虚假的命题只能以可忽略的概率通过。
  3. 通过减半来证明内积。 每一轮,证明者发送两个曲线点 L 和 R,两个向量都折叠成原来一半的长度。经过 log2(64) = 6 轮后,只剩下一对数字。

证明的大小正是来自这种减半。论文摘要指出,对 n 位的范围,一个范围证明只需 2·log2(n) + 9 个群元素和域元素。对 64 位来说,就是六轮产生的 12 个点再加 9 个元素,共 21 个元素;每个元素 32 字节时,总共 672 字节。

这种证明是非交互式的。不需要验证者实时发送挑战,双方都通过对证明记录做哈希来推导挑战,这是一种称为 Fiat-Shamir 启发式的标准技术。Bulletproofs 也不需要可信设置:生成元是公开的,推导过程也是公开的,安全性只依赖离散对数假设。多个证明可以聚合,只增加对数数量的元素,验证也可以批量进行。

哪些项目使用 Bulletproofs?

Monero 在 2018 年 10 月 18 日的网络升级(v0.13.0 版本)中启用了 Bulletproofs,以减小交易体积。dalek-cryptography 项目的 Rust bulletproofs 库 在 Curve25519 的 Ristretto 群上实现了它,Blocknet 的密码学库正是基于这个库构建的。

Blocknet 如何隐藏每一笔金额?

在 Blocknet 上,每笔交易的每个输出都用 Pedersen 承诺代替金额,并且每个输出都带有自己的 64 位 Bulletproof。隐私是交易格式的一部分,没有透明模式,所以无法发送金额可见的付款。

节点在接受一笔交易之前会检查以下内容:

你可以在 Blocknet 区块浏览器上看到这一切。交易页面把金额标为 “hidden · Pedersen commitments”,每个输出都显示它的承诺、一段加密备注,以及大小为 “672 bytes · Bulletproof” 的范围证明,正是上面计算得出的大小。

接收方仍然能看到真实的金额。每个输出都带有用发送方和接收方钱包共享的秘密加密的金额,接收方的钱包用私有查看密钥解密它。金额只对付款双方可见,其他人都看不到。

隐藏金额与 Blocknet 的其他隐私层协同工作:16 个输出的环签名隐藏发送方,一次性隐身地址隐藏接收方,Dandelion++ 隐藏交易进入网络的位置。Blocknet 隐私功能指南逐一介绍了每一层。

获取属于你自己的私密 BNT

获得金额隐藏的币,一个简单的方法就是挖矿。Blocknet 使用内存密集型的 Argon2id 工作量证明,普通电脑就能挖。seine 在你的 CPU 上挖矿,同时可以让 NVIDIA 显卡一起挖,并且无需任何设置就会自动调优。bntpool.com 发给你的每笔收益,都会以带有承诺和 Bulletproof 的私密输出到账。挖矿指南带你在几分钟内从下载走到份额被接受。

常见问题

Bulletproofs 需要可信设置吗?

不需要。Bulletproofs 只依赖离散对数假设,也就是椭圆曲线签名所依赖的同一个假设,并使用任何人都能推导出来的公开参数。它没有设置仪式,也没有事后必须销毁的秘密。包括 Monero 和 Blocknet 在内的隐私币都用它来做范围证明。

Bulletproofs 范围证明有多大?

一个 64 位的 Bulletproof 包含 2 × log2(64) + 9 = 21 个元素。在 Ristretto 群上每个元素占 32 字节,所以每个证明都是 672 字节,Blocknet 区块浏览器对每个输出显示的正是这个大小。证明大小随范围的对数增长,所以位数翻倍只多出两个元素。

谁能看到一笔 Blocknet 交易的金额?

只有发送方和接收方。链上为每个输出存储一个 Pedersen 承诺和一个范围证明,以及用双方钱包共享的秘密加密的金额。接收方的钱包用私有查看密钥解密它。网络手续费是唯一公开的数字,这样节点才能把它计入余额检查。

金额都隐藏了,节点怎么知道没有凭空造币?

每个节点会对每笔交易检查两件事。Pedersen 承诺必须能对上账,即输入等于输出加手续费;每个输出都必须带有有效的范围证明,证明其金额在 0 到 2^64 之间。两者结合,就能在不泄露任何金额的情况下排除隐藏的增发。