在内积证明(inner product arguments)的语境下,范围证明是指证明标量 已经被承诺为 ,并且对于某个非负整数 , 小于 。
本文将展示 Bulletproofs 论文是如何构建这种证明的。其高层理念是,如果我们能证明向量 仅由 1 和 0 组成,并且 是 的二进制表示,那么 必然小于 。这就好比说一个能装进 8 位无符号整数的数字必然小于 256。
使用 Bulletproofs 进行范围证明的优势在于,可以直接构建范围证明,而无需使用算术电路。
Monero 使用了 Bulletproofs 范围证明(即本文介绍的算法)来确保交易金额之和不为负数(在有限域中,负数是指大于 的元素,因为它们是小于或等于 的元素的加法逆元,其中 是域的阶)。
本文是 ZK Bulletproofs 系列文章的一部分。
符号说明
是一个全零的 维向量。
是一个全 1 的 维向量。
是一个 维向量 。
是一个 维向量 。
是一个 维向量 。
注意 。
范围证明概述
要证明 是对一个值小于 的标量的承诺,需要证明以下几点:
- 是二进制的(只包含值 和 )。
- 内积 。
第二点很容易证明,我们进行常规的内积证明,然后揭示 是承诺中的向量之一——或者让验证者自己构建 的承诺。然而,在没有算术电路的情况下证明 是二进制的,需要用到一些代数技巧。
四个实用的技巧
Bulletproofs 论文隐式地使用了四个代数技巧,在直接查看范围证明算法之前,最好先显式地讲解它们。
1. 证明 是二进制的
声明 是二进制的等价于以下两个断言:
例如,如果 ,那么 。
在这种情况下,,因为
现在考虑 不是二进制的情况,例如 。 将是 , 和 的 Hadamard 乘积(Hadamard product)将是 。
更一般地说,如果 有一个非二进制的元素,该元素减去 后,在 中对应的结果元素将是非零的。当计算 Hadamard 乘积时,在那个特定的索引处, 和 都将是非零的,其乘积也是非零的,这意味着 。
然而,如果 中某个特定元素为 ,那么 在该索引处将为 ,因此在该索引处的 Hadamard 乘积也将为零。
最后,如果 中某个特定元素为 ,那么 在该索引处将为 ,它们在该索引处的逐元素乘积仍然为零。
因此,如果 是二进制的并且 按 计算,那么 。
2. 证明一个向量为全零
假设我们希望证明 Pedersen 承诺 包含一个全零向量。我们创建 Pedersen 承诺 ,并希望向验证者证明 。
看起来仅仅发送盲化因子 就足够了,但为了让我们的方案更具可组合性,我们不希望泄露盲化因子,因为这可能会影响我们创建的其他承诺。
相反,证明者将 发送给验证者,验证者回复一个充满随机值的向量 。现在证明者必须证明
请注意,这是一个概率测试。在 的情况下, 是有可能的,但概率可以忽略不计;同时证明者不可能伪造这样的 ,因为他们无法预先知道 会是什么。
然而,传输 需要 的通信开销,因此验证者改为只发送单个随机元素 ,而证明者计算 并将 用作随机向量。
然后,证明者证明 。
3. 证明内积具有 的形式,其中 由验证者选择,且证明者计算
我们还没有机制来证明 ,因为这是一个 Hadamard 乘积,而不是内积。然而,声明向量 恒等于 就相当于声明 。根据内积规则,我们可以将 移到内积的另一侧,现在我们得到 。
验证者将收到对 和 的承诺,而不是对 的承诺。这就需要验证者自行构建对 的承诺,以确信证明者在内积中使用了 作为第二个向量。
我们所依赖的关键技巧是,证明者使用基向量 和 来对其向量进行承诺,而验证者使用 和 。
当证明者发送求值结果 时,证明者必须确保 项会与验证者基向量 中的 相抵消。
具体来说,证明者构建承诺
并将 发送给验证者。由于在本例中 为零,所以不需要对 进行承诺并发送。
证明者的多项式将是
关键在于,证明者将 与 进行了 Hadamard 相乘。在以前, 被计算为 (没有 )。这将在稍后允许当验证者计算承诺 时,抵消掉所有的 项。在底层逻辑上, 是 ,因此当验证者计算 时, 将被抵消,即:
然而,证明者现在还不能计算 或 ,因为验证者还没有发送 。因此,在收到 后,验证者发送 ,证明者计算 并计算多项式 :
其中
证明者将系数 和 承诺为
并将 发送给验证者。验证者用 回应,证明者对向量多项式 和 进行求值:
注意, 只包含 和 的盲化因子。在之前的实现中, 被计算为 ,其中 是 的盲化因子,它也是多项式 的常数项系数。
这里没有盲化因子 ,因为没有对 的承诺,也就是说 不是秘密的——它是 。证明者发送 ,验证者检查:
第一个关键区别是,由于之前讨论的原因,对 的承诺是相对于基向量 而不是 进行的。
其次, 没有常数承诺。通常情况下,等式为 ,但在本例中 是对 的承诺。
一般来说,如果 包含验证者已知的值,验证者可以自行构建对 的承诺,正如我们在下一节中所展示的。
4. 在涉及加法公开常数的情况下证明内积
正如上一节所暗示的,如果验证者知道底层向量,验证者就可以重构承诺。
例如,假设我们要证明
其中 和 是验证者已知的向量, 是验证者事先已知的标量。与 不同,这些向量和标量在证明开始之前就是已知的。请注意,在本例中 没有被 进行 Hadamard 乘法。
证明者仍然像往常一样只对秘密值 、 和 做出承诺:
像往常一样,多项式 和 的常数项是原始内积中的向量,线性项是 和 。在从验证者那里收到 后,证明者计算 并构造(但不求值) 和 :
请注意, 没有与 进行 Hadamard 相乘,但线性项 仍然进行了相乘。我们稍后将展示验证者如何处理这一点。
目前,我们将 计算为
其中
注意 中的常数项是 而不是 。承诺被计算为
并发送给验证者,然后验证者发送随机值 。
证明者计算:
注意 中的常数项是 。证明者发送 。最后,验证者计算:
和 分别包含 和 ,但 和 并不包含。因此,验证者计算对这些向量的承诺,并将它们加到承诺 和 中。对于 ,基向量 会导致 变成 ,因此必须相对于 来计算承诺。最后,盲化因子 包含 ,但 并不包含 。因此,证明者必须将 乘以 。
通过计算 、 和 ,验证者可以确信内积计算中确实包含了这些项。
范围证明
为了证明 是一个小于 的值,我们需要证明三件事:
- 内积 ,即 是 的二进制表示
最后两个声明并不直接采用内积的形式。然而,我们可以稍微修改它们以实现这一目标。我们实际上要表达的是,向量
都是 。我们可以使用上一节中的技巧来证明它们为零。也就是说,证明者需要确立
和
其中 是从验证者发送的 值派生出的随机向量。
原始的 Bulletproofs 论文对第一个声明进行了如下微调,以便我们可以使用上一节中的第三个技巧:
因此,证明者需要建立三个内积:
将三个内积合并为一个
使用由验证者提供的随机数 进行随机线性组合,可以将这三个内积合并为一个内积。
通过一些非常繁琐的内积代数运算,我们可以如下合并所有的内积。我们将在附录中展示推导过程。
下面方框中的项包含验证者已知的值,因此我们将构建验证算法来显式检查这些值。也就是说,由验证者(而不是证明者)计算对框内各项值的承诺:
为了节省篇幅,Bulletproofs 论文将项 称为 ,所以该内积可以写为
注意 是一个验证者可以计算的值。
范围证明算法
证明者选择 及其二进制表示 ,并计算 。
证明者然后随机选择盲化因子 ,并使用基向量 和 计算对 和 的组合承诺为
证明者随后选择即将创建的向量多项式 和 的线性项作为 和 ,并对它们进行承诺
证明者将内积承诺为 ,它是关于未知离散对数的基点 (与 无关)进行的承诺:
证明者将 发送给验证者。
验证者回复随机值 ,证明者将使用它们将三个内积合并为一个。
内积的左半部分 将作为 的常数项,而 将作为 的常数项。
因此,我们将 构造为
并将 构造为
注意,出于上面前置条件部分第 3 点所讨论的原因,我们将 与 进行了逐元素相乘。
证明者现在可以构造 ,其常数项系数 、线性项系数 和二次项系数 计算如下:
其中
证明者发送对 和 的承诺如下
不需要对 进行承诺——请注意,它正是我们试图证明的内积,因此验证者已经拥有了它的承诺,即 。
验证者发送随机数 ,证明者计算
注意, 的常数项乘以了 ,以反映原始内积的 项。
验证者随后计算新的基向量 并运行以下检查:
回顾一下,证明者并没有对用于内积左右两侧的完整向量进行承诺,而只承诺了 和 。其余的向量是对验证者已知的加法公开向量,因此验证者通过构造对常数项的承诺,并将它们加到由证明者提供的秘密向量的承诺上,从而重构了对向量的承诺。
作为提醒,以下是原始内积,验证者已知的值已加框标出:
建议读者验证:在原始内积中加框的项(验证者已知的值)已经在上述的一组等式检查中,由验证者在框内各项里进行了重构。
通过复制证明者的部分计算,验证者可以断言证明者确实如其所称的那样执行了计算。
验证算法的正确性
我们现在展示,如果证明者是诚实的,那么最终的验证检查是完全正确的。
下面我们将展示精确的代数运算,但从直觉上看,验证者是在“重构”内积的左向量 、内积的右向量 以及输出结果 。
验证者并没有被给出对 和 的承诺,而只有对 和 的承诺。同样,验证者并没有被给出对输出 的承诺,而只给出了对 的承诺。
加法项以及被 逐元素相乘的项必须由验证者进行重构。
的正确性
对于 检查,根据定义这是成立的,因为这正是证明者计算 的方式。
关于 和 承诺的 和 的正确性
对于
我们做以下替换:
所有 项按如下方式抵消:
与 相关的盲化因子按如下方式抵消:
与 项抵消:
拆分内积:
抵消等式两侧都出现的项:
将 移到另一侧:
求值的正确性
要看出
是正确的,我们可以如下代入各项:
其中 、、、 为:
然而,这样的代数运算将极其混乱。相反,我们观察到 是向量多项式内积 的常数项。为了抵消 中 的盲化因子,请注意 包含了 ,因此这将与 中的 gamma 项相抵消。
因为 Pedersen 承诺是加法同态的,验证者可以简单地计算 并将其加到 上,从而计算出多项式 常数项的承诺。
对数级大小的范围证明
我们可以通过发送一个对 和 的承诺 ,并使用对数级大小的证明来证明所承诺的向量具有内积 ,从而减少数据传输的大小,然后验证
以及
是相对于基向量 和 成立的。
将范围证明算法应用于子集和问题
子集和问题提出这样的问题:“给定一组数字,是否存在一个子集(可能包括整个集合)的总和为 ?”例如,如果 且集合为 ,答案是“是”,因为 。但是,如果 ,则答案为“否”。
子集和问题是一个 NP-Complete(NP完全)问题,这意味着,类似于布尔电路或算术电路,它可以表示NP中的任何问题。也就是说,NP中的任何问题都可以重写(技术术语为“归约(reduced)”)为一个子集和问题实例。
通过将 替换为 ,我们可以证明自己知道子集和问题的解,而无需泄露答案。具体来说,如果 ,证明者会知道 。一般来说, 中的 1 表示我们把该元素包含在子集中,0 则表示不包含在子集中。
因此,Bulletproofs 能够为 NP 中的任何问题证明对任何见证(witness)的知识。
附录:将三个内积合并为一个的推导
从三个内积开始
我们将展示如何使用之前学过的内积代数来推导最终结果
- 中间项可以被拆分为独立的内积:
-
我们可以把常数 移到内积里面:
-
把验证者已知的值移到右边:
- 将所有的 项都转换为 :
- 把 项合并为一个:
- 合并左边的两个 项:
- 把左边最后一项拆分为两个内积:
- 合并 项:
- 我们可以利用规则 来合并包含 的项。此处 为 , 为 , 为 。
- 我们现在拆分右边的项:
- 将右边内积中的标量提取出来:
- 提取出公因子 :
因为 ,我们得到:
推导完毕。