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 字节的点。它有两个性质:
- 隐藏性。 由于
r是随机的,C看起来就像一个随机点。同一个金额承诺两次会得到两个毫无关联的承诺,仅凭承诺本身,观察者得不到关于v的任何信息。 - 绑定性。
C一旦公布,钱包就无法把它打开为另一个金额。要做到这一点,就得解决这条曲线上的离散对数问题。
节点如何检查一笔它读不懂的交易?
Pedersen 承诺可以相加。两个承诺之和就是对两个金额之和的承诺,盲化因子也随之相加。节点把一笔交易的输入承诺加总,再把输出承诺加上手续费加总,检查两个总数是否相等,这样无需看到任何金额就能确认收支平衡。
两个承诺相加得到 (v1 + v2)·G + (r1 + r2)·H,它本身就是对 v1 + v2 的承诺。发送方会安排好盲化因子,使输入的盲化因子总和等于输出的总和。如果金额也平衡,输入之和与输出之和的差就恰好为零,任何节点都能验证这一点。
网络手续费是交易中唯一公开的数字。节点把它转换成盲化因子为零的承诺,加到输出一侧,于是检查式就是:输入 = 输出 + 手续费。
隐藏的金额为什么需要范围证明?
承诺的运算是对一个非常大的数取模进行的,这个数就是曲线群的阶,约为 2^252。在这种运算中,“负数”金额其实就是一个巨大的正数。有人可以花掉一个价值 10 个币的输入,创建两个输出,一个价值 11,一个价值“负 1”。承诺会完全平衡,于是 1 个币凭空出现,而且所有观察者都看不到。
范围证明堵住了这个漏洞。它证明承诺中的金额位于 0 到 2^64 之间,而不说出具体是多少。每个输出都受到这样的约束后,几个输出相加永远不可能大到绕过群的阶,因此承诺平衡就意味着金额平衡。
早期的机密交易设计使用的范围证明随位数线性增长。Bulletproofs 只随位数的对数增长,使一个 64 位证明缩小到几百字节。
Bulletproofs 是如何工作的?
一个 Bulletproof 分三步证明一个范围。
- 把金额拆成位。 一个 64 位金额变成一个由 64 个位组成的向量。证明者对这个向量做承诺,并证明它的两个事实:每一项都是 0 或 1,并且这些位按 2 的幂加权后的和等于被承诺的金额。
- 把这些事实变成一个内积。 借助随机挑战,证明者把所有这些条件折叠成一个关于两个向量内积的命题。由于挑战是随机的,一个虚假的命题只能以可忽略的概率通过。
- 通过减半来证明内积。 每一轮,证明者发送两个曲线点
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。隐私是交易格式的一部分,没有透明模式,所以无法发送金额可见的付款。
节点在接受一笔交易之前会检查以下内容:
- 每个范围证明。 每个输出的 Bulletproof 都必须能针对其承诺验证通过,证明金额在 0 到 2^64 之间。这些证明使用标签为 “Blocknet Range Proof” 的证明记录,使每个证明都与 Blocknet 绑定。
- 收支平衡。 每个输入都带有一个伪输出承诺,环签名证明它承诺的金额与被花费的真实币相同,同时不透露真实币是环中的哪个成员。然后节点检查伪输出之和等于输出加手续费。
- 新币。 区块奖励遵循公开的发行计划,所以对于挖出的输出,节点会按该高度重新计算它应有的承诺,并检查两者是否一致。
你可以在 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 之间。两者结合,就能在不泄露任何金额的情况下排除隐藏的增发。