Binius:基于binary域的超高效ZK证明系统(含解读)

1. 引言

在过去的两年中,STARKs已成为一种至关重要且不可替代的技术,可用于高效地对非常复杂的statement(如,证明某以太坊区块有效)进行易于验证的密码学证明。一个关键原因在于STARKs所用域size较小:

一个自然而然的问题是:

  • 能否将这一趋势推向逻辑结论,通过直接对0和1进行运算来构建运行速度更快的证明系统?
  • 这正是Binius(2023年Ulvetanna团队论文《Succinct Arguments over Towers of Binary Fields》)试图做的事情——Binius使用了许多数学技巧,使其与三年前的SNARKsSTARKs截然不同。

本文将介绍:

  • 1)为什么小域使证明生成更有效率
  • 2)为什么二进制域具有独特的强大功能
  • 3)Binius 用来使二进制域上的证明如此有效的技巧。

在这里插入图片描述
Binius总体示意如上图所示,阅读完本文后,将可理解上图的每个部分。

2. 有限域

密码学证明系统的关键任务之一是处理大量数据,同时保持数字较小。如果能将有关大型程序的statement压缩为涉及一个少量数字的数学方程,但这些数字与原始程序一样大,那样的话毫无意义。

为了进行复杂的算术运算,同时保持数字较小,密码学家通常使用模运算。选择一些素数“模数” p p p % \% % 运算符表示“取余数”:如 15   %   7 = 1 , 53   %   10 = 3 15\ \%\ 7 = 1, 53\ \%\ 10 = 3 15 % 7=1,53 % 10=3等。注意模运算结果总是非负的,如 − 1   %   10 = 9 -1\ \%\ 10 = 9 1 % 10=9
在这里插入图片描述
在加减时间的上下文中(如,9:00 过后四小时是几点?),实际对应的即为模运算
不过,在密码学上下文中,不仅对某个数字进行加减模运算,还进行乘法、除法和取指数运算。

在此,重新定义:
x + y ⇒ ( x + y ) % p x + y \Rightarrow (x + y) \% p x+y(x+y)%p
x ∗ y ⇒ ( x ∗ y ) % p x * y \Rightarrow (x * y) \% p xy(xy)%p
x y ⇒ ( x y ) % p x^y \Rightarrow (x^y) \% p xy(xy)%p
x − y ⇒ ( x − y ) % p x - y \Rightarrow (x - y) \% p xy(xy)%p
x / y ⇒ ( x ∗ y p − 2 ) % p x / y \Rightarrow (x * y ^{p-2}) \% p x/y(xyp2)%p

以上规则是自洽的。如,若 p = 7 p=7 p=7,则有:

  • 5 + 3 = 1 5 + 3 = 1 5+3=1(因为 8   %   7 = 1 8\ \%\ 7=1 8 % 7=1
  • 1 − 3 = 5 1-3 = 5 13=5(因为 − 2   %   7 = 5 -2\ \%\ 7=5 2 % 7=5
  • 2 ⋅ 5 = 3 2 \cdot 5 = 3 25=3(因为 10   %   7 = 3 10\ \%\ 7=3 10 % 7=3
  • 3 / 5 = 2 3/5 = 2 3/5=2(因为 ( 3 ⋅ 5 5 )   %   7 = 9375   %   7 = 2 (3\cdot 5^5)\ \%\ 7=9375\ \% \ 7 = 2 (355) % 7=9375 % 7=2

这种结构的一个更通用的术语是有限域。有限域是一种遵循通常算术定律的数学结构,但其可能值的数量有限的,因此每个值都可用固定的大小来表示。

模运算(或素数域)是最常见的有限域类型,但还有另一种类型:扩域。可能之前已经见过扩域:如,复数。

  • “想象”一个新元素,将其标记为 i i i,并声明其满足 i 2 = − 1 i^2=-1 i2=1
  • 然后可取任意常规数字和 i i i的组合,即可构成一个复数。
  • 对2个复数做乘法等运算,如: 6 i 2 + 12 i + 4 i + 8 = 16 i + 2 6i^2 + 12i + 4i + 8 = 16i + 2 6i2+12i+4i+8=16i+2

可类似地对素数域进行扩展,从而获得扩域。当开始处理较小的域时,素数域的扩域对于保护安全性变得越来越重要,而(Binius 所用的)binary二进制域完全依赖于扩域才能具有实用性。

3. 算术化

SNARKs 和 STARKs 证明计算机程序的方法是通过算术化:

  • 将待证明的程序的statement转换为涉及多项式的数学方程。
  • 该数学方程的有效解对应于程序的有效执行。

举个简单例子,假设Alice计算了第 100 个Fibonacci斐波那契数,想向Bob证明其具体值。

  • Alice创建一个编码了Fibonacci数字的多项式 F F F,使得: F ( 0 ) = F ( 1 ) = 1 , F ( 2 ) = 2 , F ( 3 ) = 3 , F ( 4 ) = 5 F(0) = F(1) = 1, F(2) = 2, F(3) = 3, F(4) = 5 F(0)=F(1)=1,F(2)=2,F(3)=3,F(4)=5,以此类推,共100步。
  • Alice需要证明的为,对于 x = { 0 , 1...98 } x = \{0, 1 ... 98\} x={0,1...98},有: F ( x + 2 ) = F ( x ) + F ( x + 1 ) F(x+2) = F(x) + F(x+1) F(x+2)=F(x)+F(x+1)
  • Alice可通过给出以下公式来让Bob相信这一点:
    H ( x ) = F ( x + 2 ) − F ( x + 1 ) − F ( x ) Z ( x ) H(x) = \frac{F(x+2) - F(x+1) - F(x)}{Z(x)} H(x)=Z(x)F(x+2)F(x+1)F(x)
    其中: Z ( x ) = ( x − 0 ) ∗ ( x − 1 ) ∗ . . . ∗ ( x − 98 ) Z(x) = (x - 0) * (x - 1) * ... * (x - 98) Z(x)=(x0)(x1)...(x98)
    • 若Alice提供满足以上方程式的有效 F F F H H H,则对于 x = { 0 , 1...98 } x = \{0, 1 ... 98\} x={0,1...98} F F F必须满足 F ( x + 2 ) − F ( x ) − F ( x + 1 ) F(x+2) - F(x) - F(x+1) F(x+2)F(x)F(x+1)
    • 若Alice额外验证 F ( 0 ) = F ( 1 ) = 1 F(0) = F(1) = 1 F(0)=F(1)=1,则 F ( 100 ) F(100) F(100)必须为实际的第100个Fibonacci数字。

Alice若想证明一些更复杂的东西,那么可用更复杂的方程式来代替“简单” relation F ( x + 2 ) = F ( x ) + F ( x + 1 ) F(x+2) = F(x) + F(x+1) F(x+2)=F(x)+F(x+1),如“ F ( x + 1 ) F(x+1) F(x+1)是以 F ( x ) F(x) F(x)为初始状态的虚拟机,执行一个计算步骤的输出”。还可将数字 100 100 100 替换为更大的数字,如 100000000 100000000 100000000,以容纳更多步骤。

所有 SNARKs 和 STARKs 都基于这一理念,即使用基于多项式(或有时是向量和矩阵)的简单方程来表示单个值之间的大量关系。并非所有 SNARKs 和 STARKs 都涉及以 与上述相同的方式检查相邻计算步骤之间的等价性:如, PLONK就不涉及做这种相邻计算步骤之间的等价性检查,R1CS 也不涉及。但许多最有效的 SNARKs 和 STARKs 都涉及,因为多次执行相同的检查(或相同的少数检查)可以更轻松地最大限度地减少开销。

4. Plonky2:从 “256 位 SNARKs 和 STARKs” 到 “64 位……只有 STARKs”

五年前,对不同类型的零知识证明的合理总结如下。
有两种类型的证明:

  • (基于椭圆曲线的)SNARKs
  • 和(基于哈希的)STARKs。

从技术上讲,STARKs 是一种 SNARKs,但在实践中,通常使用“SNARK”来指代基于椭圆曲线的品种,使用“STARKs”来指代基于哈希的构造。

  • SNARKs 很小,因此可非常快速地对其验证并轻松将其用于链上。
  • STARKs 很大,但其不需要可信设置,且具有抗量子性。

在这里插入图片描述
STARKs 的工作原理是将数据视为多项式,在大量点上计算该多项式的evaluations,并使用该扩展数据的 Merkle 根作为“多项式承诺”。

这里的一个关键历史是:

  • 基于椭圆曲线的 SNARKs 首先得到广泛使用。
  • 直到大约 2018 年,由于FRI的出现,STARKs 才变得足够高效,而那时Zcash已运行了一年多。

基于椭圆曲线的 SNARKs 有一个关键限制:如果想使用基于椭圆曲线的 SNARKs,那么这些方程中的运算必须用整数模椭圆曲线上的点数来完成——即对椭圆曲线scalar域求模——这是一个很大的数字,通常接近 2 256 2^{256} 2256:如,对于 bn128 曲线,该值为 21888242871839275222246405745257275088548364400416034343698204186575808495617 21888242871839275222246405745257275088548364400416034343698204186575808495617 21888242871839275222246405745257275088548364400416034343698204186575808495617

但实际计算重使用的是小数字:若考虑用所喜欢的语言编写的“真实”程序,其处理的大部分内容是计数器、for 循环中的索引、程序中的位置、表示 True 或 False 的各个位,以及其他几乎总是只有几位数字长的东西。

即使“原始”数据是由“小”数字组成的,证明过程也需要计算商、扩展、随机线性组合和其他数据变换,这会导致对象数量相等或更多,这些对象的平均大小与域的整个大小一样大。这造成了一个关键的低效率:为了证明对 n n n个小值的计算,必须对 n n n个大得多的值进行更多的计算。起初,STARKs 继承了 SNARKs 使用 256 位域的习惯,因此遭受了同样的低效率。

在这里插入图片描述
如上图所示为一些多项式evaluations的 Reed-Solomon 扩展。尽管原始值很小,但额外的值都会膨胀到整个域的大小(在本例中为 2 31 − 1 2^{31}-1 2311)。

2022 年,Plonky2 发布。Plonky2 的主要创新是对较小的素数进行模运算: 2 64 − 2 32 + 1 = 18446744069414584321 2^{64} - 2^{32} + 1 = 18446744069414584321 264232+1=18446744069414584321。这样,每次加法或乘法都可在 CPU 上仅用几条指令完成,并且将所有数据哈希在一起的速度比以前快 4 倍。但这有一个问题:

  • 其仅适用于 STARKs。若尝试使用 SNARKs,则具有如此小size的椭圆曲线就会变得不安全。

为了继续保证安全,Plonky2 还需要引入扩域。检查算术方程的一个关键技术是:

  • “在随机点采样”

若想检查 H ( x ) ∗ Z ( x ) H(x) * Z(x) H(x)Z(x)实际上是否等于 F ( x + 2 ) − F ( x + 1 ) − F ( x ) F(x+2) - F(x+1) - F(x) F(x+2)F(x+1)F(x),可选择某随机坐标 r r r,提供多项式承诺opening proofs H ( r ) , Z ( r ) , F ( r ) , F ( r + 1 ) , F ( r + 2 ) H(r), Z(r), F(r), F(r+1), F(r+2) H(r),Z(r),F(r),F(r+1),F(r+2),然后实际检查 H ( r ) ∗ Z ( r ) H(r) * Z(r) H(r)Z(r)是否等于 F ( r + 2 ) − F ( r + 1 ) − F ( r ) F(r+2) - F(r+1) - F(r) F(r+2)F(r+1)F(r)

若攻击者可提前猜出该坐标 r r r,那么攻击者就可欺骗证明系统——这就是为什么 r r r必须是随机的。但这也意味着坐标 r r r必须从足够大的集合中采样,以至于攻击者无法随机猜出 r r r值。

  • 若模数接近 2 256 2^{256} 2256,则集合足够大,可满足要求。
  • 但若模数为 2 64 − 2 32 + 1 2^{64} - 2^{32} + 1 264232+1,则不够大,不符合要求;当降到 2 31 − 1 2^{31} - 1 2311,则必然不满足要求。
    • 尝试20亿次来伪造一个证明,直到成功,这绝对在攻击者的能力范围内。

为阻止这种情况,可选择从扩域来采样 r r r值。如可定义 y y y,其中 y 3 = 5 y^3=5 y3=5,并取 1 , y , y 2 1,y,y^2 1,y,y2的组合。这将使坐标总数增加到大约 2 93 2^{93} 293

  • prover计算的大部分多项式不进入该扩域——其只需使用整数模 2 31 − 1 2^{31}-1 2311,因此仍然可从使用小域中获得所有效率。
  • 但随机点检查和 FRI 计算确实需深入到更大的扩域中,以获得所需的安全性。

5. 从小素数域到二进制域

计算机通过将较大的数字表示为0和1的序列,并在这些bits之上构建“电路”来计算加法和乘法等操作来进行算术运算。计算机特别适合使用 16 位、32 位和 64 位整数进行计算。
之所以选中 2 64 − 2 32 + 1 2^{64} - 2^{32} + 1 264232+1 2 31 − 1 2^{31} - 1 2311这样的模数:

  • 不仅是因为它们符合(fit within)这些界限,
  • 而且因为它们与这些界限很好地吻合(allign well with):
    • 通过执行常规的 32 位乘法,并在几个地方对输出进行按位移位和复制,即可完成模 2 64 − 2 32 + 1 2^{64} - 2^{32} + 1 264232+1乘法运算。更多技巧见Remco Bloemen博客The Goldilocks Prime(2024年7月24日有更新)。

不过,更好的办法是直接用二进制进行计算。

  • 如果加法可以“只是”XOR,而不必担心将某bit位置上的 1 + 1 上的进位溢出“carry”,"carrying"到下一bit位置,那会怎样?
  • 如果乘法可以以同样的方式更加并行化,那会怎样?

而且这些优势都来自于能够仅用1个bit来表示 True/False 值。

直接利用二进制计算的优势正是 Binius 想要做的事情。Binius团队的 zkSummit 演示文稿中的一张表格显示了效率提升:
在这里插入图片描述
尽管“大小”大致相同,但 32 位二进制域运算所需的计算资源比 31 位Mersenne域运算少 5 倍。

若认可二进制的加法无进位溢出和乘法可并行化优势,且想要用bits(0 和 1)来做所有事情。则需考虑实际上如何承诺一个代表十亿bits的多项式?

在此面临两个实际问题:

  • 1)对于表示大量值的多项式来说,这些值需要在多项式evaluations处可访问:如在上面斐波那契的例子中, F ( 0 ) , F ( 1 ) , ⋯   , F ( 100 ) F(0),F(1),\cdots,F(100) F(0),F(1),,F(100)需可访问,而在更大的计算中,需访问的值可能将达数百万个。要求所用的域需要包含达到该大小的数字。
  • 2)在 Merkle 树中证明所要承诺的关于某值的任何东西(所有 STARKs 都是如此)都需要对其进行 Reed-Solomon 编码:将 n n n个值,扩展为,如 8 n 8n 8n个值,使用冗余来防止恶意prover在计算过程中伪造一个值来作弊。 这也需要有足够大的域:要将一百万个值扩展到八百万个,需要八百万个不同的点来evaluate该多项式。

Binius 的一个关键思想是分别解决这两个问题,并通过以两种不同的方式表示相同的数据来实现。

  • 1)使用多变量(具体是multilinear)多项式来代替单变量多项式,以其在“hypercubes”上的evaluations值来表示整个计算trace。
  • 2)采用erasure coding,由于hypercube每个维度的长度均为2,无法像STARK那样做Reed-Solomon扩展,但可将hypercube看成是square(方形),基于该方形来做Reed-Solomon扩展。

5.1 从单变量多项式到hypercubes(即multilinear多变量多项式)

首先是多项式本身。基于椭圆曲线的 SNARKs、2019 年的 STARKs、Plonky2 和其他系统通常处理单变量的多项式: F ( x ) F(x) F(x)
而 Binius 则从Spartan协议中汲取灵感,并使用多元多项式: F ( x 1 , x 2 , ⋯   , x k ) F(x_1,x_2,\cdots,x_k) F(x1,x2,,xk)。事实上,Binius中将整个计算trace表示在“hypercubes”的evaluations,其中每个 x i x_i xi要么是 0,要么是 1。

如,若想要表示斐波那契数列,且仍然使用一个足够大的域来表示它们,则可将前16个数可视化为如下形式:【16个数,对应multilinear多项式仅需4个变量,即为4维空间】
在这里插入图片描述
即有:

  • F ( 0 , 0 , 0 , 0 ) F(0,0,0,0) F(0,0,0,0)值为1
  • F ( 1 , 0 , 0 , 0 ) F(1,0,0,0) F(1,0,0,0)值为1
  • F ( 0 , 1 , 0 , 0 ) F(0,1,0,0) F(0,1,0,0)值为2
  • ⋯ \cdots ,以此类推
  • F ( 1 , 1 , 1 , 1 ) F(1,1,1,1) F(1,1,1,1)值为987

根据以上hypecube evaluations值,只有一个multilinear(每个变量的degree为 1)多项式可产生这些evaluations值。因此,可将该组evaluations值视为表示多项式;实际上永远不需要费心计算该多项式的具体系数。【即以点值表示法来表示该多项式,而不需要用系数表示法来表示该多项式】

以上例子只是为了示意说明,在实践中,使用hypercubes的整个目的是让为了能使用individual bits。

计算斐波那契数的“Binius 原生”方法是使用更高维度的hypercubes,使用每组(如 16 bits)来存储数字。这需要一些聪明才智才能在bits之上实现整数加法,但使用 Binius 并不太难。

5.2 Reed-Solomon扩展:将hypercube看成是square(方形)

接下来聊聊erasure coding纠删码。STARKs 的工作方式是:

  • 对于 n n n个值,Reed-Solomon 将其扩展为更大数量的值(通常为 8 n 8n 8n,一般取值范围为 2 n 2n 2n 32 n 32n 32n),然后从该扩展集合中随机选择一些 Merkle 分支并对其进行某种检查。

hypercubes在每个维度上的长度为 2。因此,直接对hypercubes进行扩展是不切实际的:

  • 没有足够的“空间”从 16 个值中采样 Merkle 分支。

那该怎么做呢?

  • 假设hypercubes是一个正方形!

详细的如何基于该方形来做Reed-Solomon扩展,见下一节“简化版Binius示例”内容。

6. 简化版Binius示例

本简化版Binius示例Python代码见:

举个例子,在此为了方便起见,使用常规整数作为域(在实际实现中,这将是二进制域元素)。

  • 1)首先,取要提交的hypercubes,并将其编码为正方形:
    在这里插入图片描述

  • 2)用 Reed-Solomon 扩展该方形。即,将每一行视为在x = {0, 1, 2, 3}处求值的 3阶多项式,并在x = {4, 5, 6, 7}处对相同的多项式求值:
    在这里插入图片描述
    请注意,数字会迅速膨胀!这就是为什么在实际实现中,总是使用有限域,而不是常规整数:如,若对整数做模 11运算,则所扩展的第一行将只是[3, 10, 0, 6]。

    若想尝试扩展并亲自验证这些数字,可使用https://github.com/ethereum/research/blob/master/binius/utils.py#L123中的简单 Reed-Solomon 扩展代码。

  • 3)将该扩展按列处理,并根据这些列构建一个 Merkle 树。该Merkle 树的根即为承诺。
    在这里插入图片描述

  • 4)现在,假设prover想要在某个时刻证明该多项式在 r = { r 0 , r 1 , r 2 , r 3 } r=\{r_0,r_1,r_2,r_3\} r={r0,r1,r2,r3}处的值。

    • Binius 中有一个细微差别,使其比其他多项式承诺方案稍弱:prover不应该知道或能够猜测出 r r r值,直到prover承诺 Merkle 根之后(换言之,即 r r r应是依赖于 Merkle 根的伪随机值)。这使得该方案对于“数据库查找”毫无用处(如“好的,你给了我 Merkle 根,现在证明 P ( 0 , 0 , 1 , 0 ) P(0,0,1,0) P(0,0,1,0)给我看!”)。
    • 但实际使用的零知识证明协议通常不需要“数据库查找”——只需要在随机评估点检查该多项式。因此,这种限制对于实现ZKP协议的目的来说是可以的。
    • 假设选择 r = { 1 , 2 , 3 , 4 } r=\{1,2,3,4\} r={1,2,3,4}(该多项式在该点的值为 − 137 -137 137,可通过https://github.com/ethereum/research/blob/master/binius/utils.py#L100代码来确认)。
  • 5)现在,进入实际的证明过程。将 r r r分为两部分:

    • 第一部分“column part”: { 1 , 2 } \{1,2\} {1,2},表示的是某行内各列的线性组合,对应计算“tensor product” ⨂ i = 0 1 ( 1 − r i , r i ) \bigotimes_{i=0}^1 (1 - r_i, r_i) i=01(1ri,ri)

    • 第二部分“row part”: { 3 , 4 } \{3,4\} {3,4},表示的是各行的线性组合,对应计算“tensor product” ⨂ i = 2 3 ( 1 − r i , r i ) \bigotimes_{i=2}^3 (1 - r_i, r_i) i=23(1ri,ri)

    • 5.1)先看第二部分(各行的线性组合)。注意有 ( a 1 , a 2 ) ⨂ ( b 1 , b 2 ) = [ a 1 ∗ b 1 , a 2 ∗ b 1 , a 1 ∗ b 2 , a 2 ∗ b 2 ] (a_1,a_2)\bigotimes(b_1,b_2)=[a_1*b_1,a_2*b_1,a_1*b_2,a_2*b_2] (a1,a2)(b1,b2)=[a1b1,a2b1,a1b2,a2b2]。从而有 ⨂ i = 2 3 ( 1 − r i , r i ) = [ ( 1 − r 2 ) ∗ ( 1 − r 3 ) , r 2 ∗ ( 1 − r 3 ) , ( 1 − r 2 ) ∗ r 3 , r 2 ∗ r 3 ] \bigotimes_{i=2}^3 (1 - r_i, r_i)=[(1-r_2)*(1-r_3),r_2*(1-r_3),(1-r_2)*r_3,r_2*r_3] i=23(1ri,ri)=[(1r2)(1r3),r2(1r3),(1r2)r3,r2r3]

    • 5.2)当此时取 r = { 1 , 2 , 3 , 4 } r=\{1,2,3,4\} r={1,2,3,4}时,有 r 2 = 3 , r 3 = 4 r_2=3,r_3=4 r2=3,r3=4,从而有:
      ⨂ i = 2 3 ( 1 − r i , r i ) = [ ( 1 − 3 ) ∗ ( 1 − 4 ) , 3 ∗ ( 1 − 4 ) , ( 1 − 3 ) ∗ 4 , 3 ∗ 4 ] = [ 6 , − 9 , − 8 , 12 ] \bigotimes_{i=2}^3 (1 - r_i, r_i)=[(1 - 3) * (1 - 4), 3 * (1 - 4), (1 - 3) * 4, 3 * 4] \\ = [6, -9, -8, 12] i=23(1ri,ri)=[(13)(14),3(14),(13)4,34]=[6,9,8,12]

    • 5.3)据此,计算现有各行的线性组合,可获得新“row” t ′ t' t
      [ 3 , 1 , 4 , 1 ] ∗ 6   + [ 5 , 9 , 2 , 6 ] ∗ ( − 9 )   + [ 5 , 3 , 5 , 8 ] ∗ ( − 8 )   + [ 9 , 7 , 9 , 3 ] ∗ 12 = [ 41 , − 15 , 74 , − 76 ] \begin{matrix}[3, 1, 4, 1] * 6\ + \\ [5, 9, 2, 6] * (-9)\ + \\ [5, 3, 5, 8] * (-8)\ + \\ [9, 7, 9, 3] * 12 = \\ [41, -15, 74, -76] \end{matrix} [3,1,4,1]6 +[5,9,2,6](9) +[5,3,5,8](8) +[9,7,9,3]12=[41,15,74,76]

    可将其看成是partial evaluation。若将full tensor product ⨂ i = 0 3 ( 1 − r i , r i ) \bigotimes_{i=0}^3 (1 - r_i, r_i) i=03(1ri,ri),与,full vector of all values,相乘,则可获得evaluation值为 P ( 1 , 2 , 3 , 4 ) = − 137 P(1, 2, 3, 4) = -137 P(1,2,3,4)=137
    在此,与partial tensor product相乘,仅使用了一半的evaluation coordinates,并将具有 N N N个值的grid,降低为,具有 N \sqrt{N} N 个值的row。
    若将该row给其它人,则其它人可使用剩下的一半evaluation coordinates,来完成剩余的计算。

  • 6)prover将新“row” t ′ t' t提供给verifier,与此同时,还提供某些随机采样列的Merkle proofs。总的数据量为 O ( N ) O(\sqrt{N}) O(N )

    • 在本例中,假设prover提供的为最后一列。实际应用时,prover需要提供几十列才能实现足够的安全性。
  • 7)接下来,需利用 Reed-Solomon codes的线性性优势——其具有的关键属性为:对 某Reed-Solomon 扩展进行线性组合得到的结果,与,对线性组合进行 Reed-Solomon 扩展得到的结果,相同。 这种“顺序独立性”通常发生在两个都是线性的运算时。
    Verifier利用该特性:

    • 7.1)计算prover所提供的row t ′ t' t的扩展,即若 t ′ = [ 41 , − 15 , 74 , − 76 ] t'=[41, -15, 74, -76] t=[41,15,74,76],则计算其扩展为 [ − 849 , − 2629 , − 800 , − 10746 ] [-849, -2629, -800, -10746] [849,2629,800,10746]
    • 7.2)计算prover所提供的列的相同线性组合——即将prover所提供的列 [ − 291 , 572 , − 30 , − 341 ] [-291,572,-30,-341] [291,572,30,341],与 ⨂ i = 2 3 ( 1 − r i , r i ) = [ 6 , − 9 , − 8 , 12 ] \bigotimes_{i=2}^3 (1 - r_i, r_i)= [6, -9, -8, 12] i=23(1ri,ri)=[6,9,8,12],相乘,获得结果 − 10746 -10746 10746
      此时,在相同的列位置,二者具有相同值 − 10746 -10746 10746。从而证明 Merkle 根是“善意”构建的(或至少“足够接近”),且其与 t ′ t' t匹配——至少绝大多数列是相互兼容的,且与 t ′ t' t兼容。
      在这里插入图片描述
    • 7.3)但verifier仍需再做一个检查:即检查该多项式在 r = { r 0 , r 1 , r 2 , r 3 } r=\{r_0,r_1,r_2,r_3\} r={r0,r1,r2,r3}处的求值。
      到目前为止,verifier的任何步骤实际上都不依赖于prover所声称的值。因此,按照以下方式进行检查。
      • 7.3.1)取evaluation point r r r的“column part” 的tensor product: ⨂ i = 0 1 ( 1 − r i , r i ) \bigotimes_{i=0}^1 (1 - r_i, r_i) i=01(1ri,ri)
        本例中,有 r = { 1 , 2 , 3 , 4 } r=\{1,2,3,4\} r={1,2,3,4},有 r 0 = 1 , r 1 = 2 r_0=1,r_1=2 r0=1,r1=2,从而有: ⨂ i = 0 1 ( 1 − r i , r i ) = [ ( 1 − 1 ) ∗ ( 1 − 2 ) , 1 ∗ ( 1 − 2 ) , ( 1 − 1 ) ∗ 2 , 1 ∗ 2 ] = [ 0 , − 1 , 0 , 2 ] \bigotimes_{i=0}^1 (1 - r_i, r_i)=[(1 - 1) * (1 - 2), 1 * (1 - 2), (1 - 1) * 2, 1 * 2] = [0, -1, 0, 2] i=01(1ri,ri)=[(11)(12),1(12),(11)2,12]=[0,1,0,2]
      • 7.3.2)将“column part” 的tensor product,与 t ′ t' t row相乘,(即取 t ′ t' t的线性组合),有: 0 ∗ 41 + ( − 1 ) ∗ ( − 15 ) + 0 ∗ 74 + 2 ∗ ( − 76 ) = − 137 0 * 41 + (-1) * (-15) + 0 * 74 + 2 * (-76) = -137 041+(1)(15)+074+2(76)=137,正好等于该多项式直接在 r = { 1 , 2 , 3 , 4 } r=\{1,2,3,4\} r={1,2,3,4}处计算的值。

以上内容非常接近“简化版”Binius 协议的完整描述。
这已经具有一些有趣的优势,如:

  • 由于数据被分成行和列,因此只需要一半大小的域。

但这还远远没有实现以二进制进行计算的全部好处。为此,需要完整版 Binius 协议。但首先,让需更深入地了解binary二进制域。

7. binary二进制域

最小可能的域是模2的算术域,它非常小,可写出其加法和乘法表:
在这里插入图片描述

可通过扩展来制作更大的二进制域:如从 F 2 F_2 F2(整数模 2)开始,然后定义 x x x,其中 x 2 = x + 1 x^2=x+1 x2=x+1,可获得如下加法和乘法表:
在这里插入图片描述
事实证明,通过重复此构造,可将二进制域扩展为任意大的尺寸。与基于实数的复数不同,基于实数的复数中,可添加一个新元素 i i i,但不能再添加任何内容(Quaternion四元数确实存在,但其在数学上很奇怪,如 a b ≠ b a ab \neq ba ab=ba。基于有限域,可不断添加新的扩展。具体为,定义如下元素:

  • x 0 x_0 x0满足 x 0 2 = x 0 + 1 x_0^2=x_0+1 x02=x0+1
  • x 1 x_1 x1满足 x 1 2 = x 1 x 0 + 1 x_1^2=x_1x_0+1 x12=x1x0+1
  • x 2 x_2 x2满足 x 2 2 = x 2 x 1 + 1 x_2^2=x_2x_1+1 x22=x2x1+1
  • x 3 x_3 x3满足 x 3 2 = x 3 x 2 + 1 x_3^2=x_3x_2+1 x32=x3x2+1
  • ⋯ \cdots

这通常被称为tower construction(塔式构造),因为每次连续扩展都可以看作是向塔中添加新的一层。这不是构造任意大小的二进制域的唯一方法,但其具有 Binius 利用的一些独特优势。

将数字以bits list表示,如 1100101010001111 \texttt{1100101010001111} 1100101010001111

  • 第一个bit表示 1 1 1的倍数
  • 第二个bit表示 x 0 x_0 x0的倍数
  • 后续的bit依次表示 x 1 , x 1 ∗ x 0 , x 2 , x 2 ∗ x 0 x_1,x_1*x_0,x_2,x_2*x_0 x1,x1x0,x2,x2x0的倍数
  • ⋯ \cdots

这种编码很好,因可将其分解为:【注意,其中有些类似 00 ∗ x 1 = 0 00*x_1=0 00x1=0的项直接被去掉了。】
1100101010001111 = 11001010 + 10001111 ∗ x 3 = 1100 + 1010 ∗ x 2 + 1000 ∗ x 3 + 1111 ∗ x 2 x 3 = 11 + 10 ∗ x 2 + 10 ∗ x 2 x 1 + 10 ∗ x 3 + 11 ∗ x 2 x 3 + 11 ∗ x 1 x 2 x 3 = 1 + x 0 + x 2 + x 2 x 1 + x 3 + x 2 x 3 + x 0 x 2 x 3 + x 1 x 2 x 3 + x 0 x 1 x 2 x 3 \texttt{1100101010001111} = \texttt{11001010} + \texttt{10001111} * x_3= \texttt{1100} + \texttt{1010} * x_2 + \texttt{1000} * x_3 + \texttt{1111} * x_2x_3\\= \texttt{11} + \texttt{10} * x_2 + \texttt{10} * x_2x_1 + \texttt{10} * x_3 + \texttt{11} * x_2x_3 + \texttt{11} * x_1x_2x_3\\ = 1 + x_0 + x_2 + x_2x_1 + x_3 + x_2x_3 + x_0x_2x_3 + x_1x_2x_3 + x_0x_1x_2x_3 1100101010001111=11001010+10001111x3=1100+1010x2+1000x3+1111x2x3=11+10x2+10x2x1+10x3+11x2x3+11x1x2x3=1+x0+x2+x2x1+x3+x2x3+x0x2x3+x1x2x3+x0x1x2x3

这是一种相对不常见的符号,但也可将二进制域元素表示为整数,采用bit表示法,其中更重要的bits在右边(即类似于little-endian表示)。从而有: 1 = 1 , x 0 = 01 = 2 , 1 + x 0 = 11 − 3 , 1 + x 0 + x 2 = , 1 + x 0 + x 2 = 11001000 = 19 , ⋯ 1=1, x_0=01=2, 1+x_0=11-3, 1+x_0+x_2=,1 + x_0 + x_2 = \texttt{11001000} = 19,\cdots 1=1,x0=01=2,1+x0=113,1+x0+x2=,1+x0+x2=11001000=19,,最终有 1100101010001111 \texttt{1100101010001111} 1100101010001111,表示为 61779 61779 61779

二进制域中的加法只是XOR异或(顺便说一下,减法也是如此),即意味着:

  • 对于任意的 x x x,有 x + x = 0 x+x=0 x+x=0

对于二进制域中的乘法,如 x ∗ y x*y xy,则有非常简单的递归算法:

  • 将每个数组切分为两半: x = L x + R x ∗ x k , y = L y + R y ∗ x k x = L_x + R_x * x_k, y = L_y + R_y * x_k x=Lx+Rxxk,y=Ly+Ryxk
  • 对应乘法拆分为: x ∗ y = ( L x ∗ L y ) + ( L x ∗ R y ) ∗ x k + ( R x ∗ L y ) ∗ x k + ( R x ∗ R y ) ∗ x k 2 x * y = (L_x * L_y) + (L_x * R_y) * x_k + (R_x * L_y) * x_k + (R_x * R_y) * x_k^2 xy=(LxLy)+(LxRy)xk+(RxLy)xk+(RxRy)xk2
    • 其中最后一项可应用reduction rule,将 ( R x ∗ R y ) ∗ x k 2 (R_x * R_y) * x_k^2 (RxRy)xk2,替换为 ( R x ∗ R y ) ∗ ( x k − 1 ∗ x k + 1 ) (R_x * R_y) * (x_{k-1}*x_k+1) (RxRy)(xk1xk+1)

有更有效的方法进行乘法运算,类似于Karatsuba 算法快速傅里叶变换,本文对此不展开讨论。

二进制域中的除法是通过结合乘法和逆运算来完成的: 3 5 = 3 ∗ 1 5 \frac{3}{5} = 3 * \frac{1}{5} 53=351。进行求逆的“简单但缓慢”的方法是广义费马小定理的应用:

  • 1 x = x 2 2 k − 2 \frac{1}{x} = x^{2^{2^k}-2} x1=x22k2,对于任意的 k k k,其中 2 2 k > x 2^{2^k}>x 22k>x

此时有 1 5 = 5 14 = 14 \frac{1}{5} = 5^{14} = 14 51=514=14,从而有 3 5 = 3 ∗ 14 = 9 \frac{3}{5} = 3 * 14 = 9 53=314=9
还有一种更复杂但更高效的求逆算法——详情见1997年论文《On efficient inversion in tower fields of characteristic two》。
有可使用https://github.com/ethereum/research/blob/master/binius/binary_fields.py中代码,来尝试二进制域加法、乘法和除法运算。

在这里插入图片描述
上图所示为4-bit 二进制域元素的加法表(即仅由 1 , x 0 , x 1 , x 0 x 1 1,x_0,x_1,x_0x_1 1,x0,x1,x0x1组合成的元素)
在这里插入图片描述
上图所示为4-bit 二进制域元素的乘法表。
修改https://github.com/ethereum/research/blob/master/binius/binary_fields.py中尾部代码为:

N=16
rawmulcache = [[None for _ in range(N)] for _ in range(N)]
addcache = [[None for _ in range(N)] for _ in range(N)]
mulcache = [[None for _ in range(N)] for _ in range(N)]

for i in range(N):
    for j in range(N):
        rawmulcache[i][j] = binmul(i, j)
        addcache[i][j] = BinaryFieldElement(i ^ j)
        mulcache[i][j] = BinaryFieldElement(binmul(i, j))

print(addcache)
print(mulcache)

对应的运行结果为:【实际即为以上2图的各结果值】

//加法表
[[<0>, <1>, <2>, <3>, <4>, <5>, <6>, <7>, <8>, <9>, <10>, <11>, <12>, <13>, <14>, <15>], [<1>, <0>, <3>, <2>, <5>, <4>, <7>, <6>, <9>, <8>, <11>, <10>, <13>, <12>, <15>, <14>], [<2>, <3>, <0>, <1>, <6>, <7>, <4>, <5>, <10>, <11>, <8>, <9>, <14>, <15>, <12>, <13>], [<3>, <2>, <1>, <0>, <7>, <6>, <5>, <4>, <11>, <10>, <9>, <8>, <15>, <14>, <13>, <12>], [<4>, <5>, <6>, <7>, <0>, <1>, <2>, <3>, <12>, <13>, <14>, <15>, <8>, <9>, <10>, <11>], [<5>, <4>, <7>, <6>, <1>, <0>, <3>, <2>, <13>, <12>, <15>, <14>, <9>, <8>, <11>, <10>], [<6>, <7>, <4>, <5>, <2>, <3>, <0>, <1>, <14>, <15>, <12>, <13>, <10>, <11>, <8>, <9>], [<7>, <6>, <5>, <4>, <3>, <2>, <1>, <0>, <15>, <14>, <13>, <12>, <11>, <10>, <9>, <8>], [<8>, <9>, <10>, <11>, <12>, <13>, <14>, <15>, <0>, <1>, <2>, <3>, <4>, <5>, <6>, <7>], [<9>, <8>, <11>, <10>, <13>, <12>, <15>, <14>, <1>, <0>, <3>, <2>, <5>, <4>, <7>, <6>], [<10>, <11>, <8>, <9>, <14>, <15>, <12>, <13>, <2>, <3>, <0>, <1>, <6>, <7>, <4>, <5>], [<11>, <10>, <9>, <8>, <15>, <14>, <13>, <12>, <3>, <2>, <1>, <0>, <7>, <6>, <5>, <4>], [<12>, <13>, <14>, <15>, <8>, <9>, <10>, <11>, <4>, <5>, <6>, <7>, <0>, <1>, <2>, <3>], [<13>, <12>, <15>, <14>, <9>, <8>, <11>, <10>, <5>, <4>, <7>, <6>, <1>, <0>, <3>, <2>], [<14>, <15>, <12>, <13>, <10>, <11>, <8>, <9>, <6>, <7>, <4>, <5>, <2>, <3>, <0>, <1>], [<15>, <14>, <13>, <12>, <11>, <10>, <9>, <8>, <7>, <6>, <5>, <4>, <3>, <2>, <1>, <0>]]
//乘法表
[[<0>, <0>, <0>, <0>, <0>, <0>, <0>, <0>, <0>, <0>, <0>, <0>, <0>, <0>, <0>, <0>], [<0>, <1>, <2>, <3>, <4>, <5>, <6>, <7>, <8>, <9>, <10>, <11>, <12>, <13>, <14>, <15>], [<0>, <2>, <3>, <1>, <8>, <10>, <11>, <9>, <12>, <14>, <15>, <13>, <4>, <6>, <7>, <5>], [<0>, <3>, <1>, <2>, <12>, <15>, <13>, <14>, <4>, <7>, <5>, <6>, <8>, <11>, <9>, <10>], [<0>, <4>, <8>, <12>, <9>, <13>, <1>, <5>, <14>, <10>, <6>, <2>, <7>, <3>, <15>, <11>], [<0>, <5>, <10>, <15>, <13>, <8>, <7>, <2>, <6>, <3>, <12>, <9>, <11>, <14>, <1>, <4>], [<0>, <6>, <11>, <13>, <1>, <7>, <10>, <12>, <2>, <4>, <9>, <15>, <3>, <5>, <8>, <14>], [<0>, <7>, <9>, <14>, <5>, <2>, <12>, <11>, <10>, <13>, <3>, <4>, <15>, <8>, <6>, <1>], [<0>, <8>, <12>, <4>, <14>, <6>, <2>, <10>, <7>, <15>, <11>, <3>, <9>, <1>, <5>, <13>], [<0>, <9>, <14>, <7>, <10>, <3>, <4>, <13>, <15>, <6>, <1>, <8>, <5>, <12>, <11>, <2>], [<0>, <10>, <15>, <5>, <6>, <12>, <9>, <3>, <11>, <1>, <4>, <14>, <13>, <7>, <2>, <8>], [<0>, <11>, <13>, <6>, <2>, <9>, <15>, <4>, <3>, <8>, <14>, <5>, <1>, <10>, <12>, <7>], [<0>, <12>, <4>, <8>, <7>, <11>, <3>, <15>, <9>, <5>, <13>, <1>, <14>, <2>, <10>, <6>], [<0>, <13>, <6>, <11>, <3>, <14>, <5>, <8>, <1>, <12>, <7>, <10>, <2>, <15>, <4>, <9>], [<0>, <14>, <7>, <9>, <15>, <1>, <8>, <6>, <5>, <11>, <2>, <12>, <10>, <4>, <13>, <3>], [<0>, <15>, <5>, <10>, <11>, <4>, <14>, <1>, <13>, <2>, <8>, <7>, <6>, <9>, <3>, <12>]]

这种类型的二进制域的妙处在于:

  • 1)其结合了“常规”整数和模运算的一些最佳部分。
    • 1.1)与常规整数一样:二进制域元素也是无界的,可根据需要不断扩展。
    • 1.2)与模运算一样:若对某个大小限制内的值进行运算,则所有结果也都保持在同一界限内。如,取 42 42 42的连续次幂,有 1 , 42 , 199 , 215 , 245 , 249 , 180 , 91 , ⋯ 1,42,199,215,245,249,180,91,\cdots 1,42,199,215,245,249,180,91,,255次幂为 4 2 255 = 1 42^{255}=1 42255=1
      N=256
      rawmulcache = [[None for _ in range(N)] for _ in range(N)]
      powcache = [None for _ in range(N)]
      a = 42
      temp = BinaryFieldElement(1)
      
      for i in range(N):
          if i == 0:
              powcache[i] = BinaryFieldElement(1)
          else:
              for j in range(i):
                  powcache[i] = BinaryFieldElement(binmul(temp.value, a))
          temp = powcache[i]        
      
      print(powcache)
      
      对应的执行结果为:【即对应为 4 2 0 , 4 2 1 , 4 1 2 , ⋯   , 4 2 255 42^0,42^1,41^2,\cdots,42^{255} 420,421,412,,42255
      [<1>, <42>, <199>, <215>, <245>, <249>, <180>, <91>, <116>, <31>, <122>, <109>, <220>, <43>, <237>, <16>, <34>, <12>, <77>, <239>, <47>, <107>, <101>, <23>, <177>, <247>, <198>, <253>, <50>, <46>, <65>, <162>, <192>, <68>, <14>, <114>, <166>, <70>, <49>, <59>, <207>, <28>, <111>, <227>, <98>, <132>, <74>, <124>, <212>, <224>, <119>, <10>, <244>, <211>, <115>, <140>, <129>, <230>, <206>, <54>, <168>, <52>, <151>, <125>, <254>, <39>, <160>, <255>, <13>, <103>, <40>, <248>, <158>, <156>, <163>, <234>, <131>, <217>, <135>, <95>, <242>, <106>, <79>, <208>, <102>, <2>, <63>, <73>, <105>, <90>, <94>, <216>, <173>, <152>, <37>, <159>, <182>, <100>, <61>, <118>, <32>, <51>, <4>, <134>, <117>, <53>, <189>, <186>, <41>, <210>, <89>, <75>, <86>, <19>, <55>, <130>, <243>, <64>, <136>, <7>, <147>, <251>, <139>, <18>, <29>, <69>, <36>, <181>, <113>, <179>, <200>, <143>, <148>, <104>, <112>, <153>, <15>, <88>, <97>, <145>, <196>, <194>, <123>, <71>, <27>, <252>, <24>, <233>, <150>, <87>, <57>, <240>, <85>, <6>, <185>, <60>, <92>, <231>, <228>, <241>, <127>, <193>, <110>, <201>, <165>, <83>, <191>, <133>, <96>, <187>, <3>, <21>, <142>, <190>, <175>, <167>, <108>, <246>, <236>, <58>, <229>, <219>, <184>, <22>, <155>, <48>, <17>, <8>, <203>, <154>, <26>, <214>, <223>, <62>, <99>, <174>, <141>, <171>, <33>, <25>, <195>, <81>, <128>, <204>, <9>, <225>, <93>, <205>, <35>, <38>, <138>, <56>, <218>, <146>, <209>, <76>, <197>, <232>, <188>, <144>, <238>, <5>, <172>, <178>, <226>, <72>, <67>, <157>, <137>, <45>, <84>, <44>, <126>, <235>, <169>, <30>, <80>, <170>, <11>, <222>, <20>, <164>, <121>, <120>, <82>, <149>, <66>, <183>, <78>, <250>, <161>, <213>, <202>, <176>, <221>, <1>]
      
    • 1.3)与常规整数和模运算一样:遵循常规数学定律: a ∗ b = b ∗ a , a ∗ ( b + c ) = a ∗ b + a ∗ c a*b = b*a, a * (b+c) = a*b + a*c ab=ba,a(b+c)=ab+ac,甚至一些奇怪的新定律,如 a 2 + b 2 = ( a + b ) 2 a^2 + b^2 = (a+b)^2 a2+b2=(a+b)2(其中 2 a b 2ab 2ab项被忽略掉,因为在binary域中有 1 + 1 = 0 1+1=0 1+1=0)。
  • 2)二进制域可方便地与bits配合使用:若将 2 k 2^k 2k bits范围内的数字做数学运算,则所有运算结果也在 2 k 2^k 2k bits范围内。
    • 2.1)这避免了以太坊EIP-4844中的尴尬——其中 blob 的各个“chunks”必须是对52435875175126190479447740508185965837690552500527637822603658699938581184513(为BLS12-381曲线scalar域)做模运算后的数字,因此编码二进制数据涉及丢弃一些空间并在应用层进行额外检查,以确保每个元素存储的值小于 2 248 2^{248} 2248
    • 2.2)这也意味着二进制域算法在计算机上的运行速度非常快——无论是 CPU,还是理论上最优的 FPGA 和 ASIC 设计。

以上二进制域的这些特性,意味着可以做类似上面的 Reed-Solomon 编码,但完全避免“简化版Binius示例”中的整数“膨胀”问题,而且这种方式对于计算机擅长的计算类型来说非常“原生”。二进制域的“splitting分裂”属性—— 1100101010001111 = 11001010 + 10001111 ∗ x 3 \texttt{1100101010001111} = \texttt{11001010} + \texttt{10001111} * x_3 1100101010001111=11001010+10001111x3,然后继续切分到想要的尽可能小的粒度,这对于实现很大的灵活性也至关重要。

8. 完整版Binius协议

完整版Binius协议Python代码实现见:

现在,对“简化版Binius”做如下调整,即可获得“完整 Binius”:

  • 1)在二进制域上工作
  • 2)对各个bit进行commit

完整版Binius协议难以理解的原因在于:

  • 其在对待bits matrix的不同方式之间来回切换。

但只要理解了二进制域,就更容易理解该协议了——因为Binius 并不依赖任何“更难的数学”。

  • 这不是椭圆曲线配对,那里有越来越深的代数几何兔子洞要钻;
  • 在这里,二进制域就是所需的一切。

再看一下完整的图表:
在这里插入图片描述
现在,应该已熟悉了大部分组件。

  • 1)将hypercubes “Flatten”为grid(上面也称为方形)的思想
  • 2)将grid基于二进制域进行“Extend”,即为Reed-Solomon扩展;将Reed-Solomon扩展后的grid按列处理,构建Merkle树,以该Merkle tree root为承诺。
  • 3)Prover:基于所选的evaluate point(上图所示为2, 0, 3, 4),分前后两半,使用tensor product,分别做column combination和row combination。在做row combination时,取得的新“row” t ′ t' t。【上图中所示,row part的tensor product结果为10, 15, 8, 12,column part的tensor product结果为3, 2, 0, 0
    Prover给Verifier提供:
    • 在所选evaluate point的evaluation值(上图中下方紫色块)14
    • merkle proof列(上图右上角紫色区域的2列),
    • 与新row t ′ t' t(上图中间紫色区域的一行)11, 4, 6, 1
  • 4)Verifier:本质是:检查“Reed-Solomon 扩展然后计算row combination”和“计算row combination然后 Reed-Solomon 扩展”之间等价性的思想。
    • 4.1)对Prover所提供的merkle proof列,基于所选的evaluate point,分前后两半,使用tensor product,然后计算row combination,如上图所示,获得3, 9——再按little endian形式,做"Decompose into bits"表示——3为0011,9为1001。
    • 4.2)对Prover所提供的新row t ′ t' t 11, 4, 6, 1,逐列重新"Decompose into bits"表示——如11对应为1011,4对应为0100,6为0110,1为0001。从而获得一个grid,对该grid做Reed-Solomon扩展 “Extend”,检查所扩展出的相应列的值,与4.1)中Prover所提供的merkle proof列经row combination和“Decompose into bits”之后的值,是否相等——即“Check that both computation paths produce the same bits here”。
  • 5)Verifier:除以上4.1)和4.2)操作之外,Verifier还需再做一个检查:即检查该多项式在所选的evaluate point的evaluation值。
    • 5.1)基于所选的evaluate point的前半部分,计算column part的tensor product,如上图所示结果为3, 2, 0, 0
    • 5.2)将column part的tensor product的结果,与Prover所提供的新row t ′ t' t 11, 4, 6, 1相乘,计算所得结果为14检查该结果值与Prover所提供的evaluation值是否相等。

“完整版 Binius” 有什么新功能?基本上有三件事:

  • 1)hypercubes和方形中的各个值必须为bits(0 或 1)
  • 2)扩展过程将bits扩展为更多bits,方法是将bits按列分组,并暂时假装它们是更大的域元素
  • 3)在row combination之后,有一个逐元素“Decompose into bits”的步骤,会将扩展转换回bits。

核心流程主要有2个:

  • 1)新的扩展流程。Reed-Solomon 码有一个基本限制,若将 n n n个值扩展为 k ∗ n k*n kn个值,即需要以 k ∗ n k*n kn个不同值为坐标,即意味着需要有具有 k ∗ n k*n kn个不同值的域size。使用 F 2 F_2 F2(又名bits),是无法满足该要求的。为此:
    • 1.1)将相邻 F 2 F_2 F2元素pack在一起,以组成更大的值。
      • 如上图中,将2个bits pack在一起组成 { 0 , 1 , 2 , 3 } \{0,1,2,3\} {0,1,2,3}中的元素。因为该扩展仅需要4个evaluation points,这样就足够了。
      • 在“real” proof场景中,可能需要一次pack 16个bits。
    • 1.2)然后基于这些packed values做Reed-Solomon code,然后再将其unpack回bits。
      在这里插入图片描述
      其中:
      from binary_fields import BinaryFieldElement as B
      
      # Polynomial evaluations
      evals = [0, B(3)]
      print(multilinear_poly_eval(evals, [0]))  //0
      print(multilinear_poly_eval(evals, [1])) //3
      print(multilinear_poly_eval(evals, [2])) //1
      print(multilinear_poly_eval(evals, [3])) //2
      print(multilinear_poly_eval(evals, [99])) //210
      print(multilinear_poly_eval(evals, [199])) //142
      
  • 2)row combination。为了使 “evaluate at a random point” checks在密码上是安全的,需要从相当大的空间中采样该点,应比hypercubes本身大得多。因此,虽然hypercubes内的点是bits,但hypercubes外的求值将大得多。如上图中,row combination最终结果为11, 4, 6, 1。但这样存在的问题在于:已知如何将bits pairs组合为更大的值,然后对其做Reed-Solomon扩展,但如何处理这些更大值的pairs做相同的操作呢?Binius中的技巧为:
    • 按位处理:如上图中11对应为1011,4对应为0100,6为0110,1为0001。组合出新的grid,再做Reed-Solomon扩展。【上图例子中取的是4行,实际可能多达128行。第一行对应 1 1 1,第二行对应 x 0 x_0 x0,第三行对应 x 1 x_1 x1,第四行对应 x 0 ∗ x 1 x_0*x_1 x0x1,以此类推,第128行对应 x 6 ∗ ⋯ ∗ x 0 x_6*\cdots * x_0 x6x0

完整版Binius总体流程回顾:

  • 1)取出hypercubes中的bits,并将其转换成grids
  • 2)将每行上相邻的bits groups视为更大的域元素,并对它们进行算术运算以对各行做 Reed-Solomon 扩展
  • 3)对每bits列进行row combination,其输出为每行对应一个列bits(方形越大于 4 × 4 4\times 4 4×4,则获得的列越小)
  • 4)将该输出视为一个矩阵,并再次将其各bits按行处理

这种方式有效的原因在于:

  • 1)在“正常”数学中,如果开始按数字分割digits,则(通常)以任意顺序执行线性运算并获得相同结果的能力将失效。如,若以数字 345 为例,将其乘以 8,然后乘以 3,得到 8280,如果反向执行这两个运算,也会得到 8280。但如果在这两个步骤之间插入“按digit分割”操作,它就会崩溃:如果先执行 8x 然后执行 3x,将得到: 345 → × 8 2760 → [ 2 , 7 , 6 , 0 ] → × 3 [ 6 , 21 , 18 , 0 ] 345 \xrightarrow{\times 8} 2760 \rightarrow [2, 7, 6, 0] \xrightarrow{\times 3} [6, 21, 18, 0] 345×8 2760[2,7,6,0]×3 [6,21,18,0];但若先做 3x,然后再做 8x,则会得到: 345 → × 3 1035 → [ 1 , 0 , 3 , 5 ] → × 8 [ 8 , 0 , 24 , 40 ] 345 \xrightarrow{\times 3} 1035 \rightarrow [1, 0, 3, 5] \xrightarrow{\times 8} [8, 0, 24, 40] 345×3 1035[1,0,3,5]×8 [8,0,24,40]
  • 2)但在以tower结构构建的二进制域中,这种事情确实有效。原因与其可分离性有关: 若将一个大值乘以一个小值,那么在每个segment中发生的事情将保留在每个segment中。如若将 1100101010001111 \texttt{1100101010001111} 1100101010001111乘以 11 \texttt{11} 11,其结果与先将 1100101010001111 \texttt{1100101010001111} 1100101010001111分解为 11 + 10 ∗ x 2 + 10 ∗ x 2 x 1 + 10 ∗ x 3 + 11 ∗ x 2 x 3 + 11 ∗ x 1 x 2 x 3 \texttt{11} + \texttt{10} * x_2 + \texttt{10} * x_2x_1 + \texttt{10} * x_3 + \texttt{11} * x_2x_3 + \texttt{11} * x_1x_2x_3 11+10x2+10x2x1+10x3+11x2x3+11x1x2x3,然后将每个部分分别乘以 11 \texttt{11} 11,二者结果相同。

9. 综合起来

一般来说,零知识证明系统通过对多项式来做出statement,这些statement同时代表了关于底层evaluations的statement:正如Fibonacci例子中所示, F ( X + 2 ) − F ( X + 1 ) − F ( X ) = Z ( X ) ∗ H ( X ) F(X+2) - F(X+1) - F(X) = Z(X) * H(X) F(X+2)F(X+1)F(X)=Z(X)H(X)同时检查Fibonacci检查的所有步骤。

通过证明随机点的evaluations来检查多项式的statement:

  • 已知 F F F的commitment
  • 随机选择如1892470为evaluate point
  • 请求在该evaluate point处的 F , Z , H F,Z,H F,Z,H evaluation proof(以及 H H H在相邻点的evaluation proof)。
  • 在该随机点的检查,即代替了对整个多项式的检查:若该多项式方程不匹配,则其在特定的随机坐标处匹配的机会就很小。

实际上,效率低下的主要原因在于,在实际程序中所处理的大多数数字都很小:for 循环中的索引、真/假值、计数器等。但是,当使用 Reed-Solomon 编码“扩展”数据以提供使基于 Merkle 证明的检查安全所需的冗余时,大多数“额外”值最终会占据整个域的大小,即使原始值很小。

为了解决该问题,希望将域尽可能地小。Plonky2 将数字从 256 位数减少到 64 位数,然后 Plonky3 进一步减少到 31 位。但即使这样也并非最优。使用二进制域,可对单个bits进行操作。这使得编码变得“dense密集”:若实际基础数据有n bits,那么编码也将有n bits,该扩展也将有8 * n bits,而无需额外的开销。【此处的8 * n ,是为了呼应上面提到的8倍扩展。此处的"额外的开销",是指数据量与 n n n紧密相关,而不会过度扩大。此处的“编码”,是指将基础数据转换为有限域表示,当采用二进制域时,可用相同位数的有限域元素来表示该基础数据。 】

现在,第三次看一下该图:
在这里插入图片描述
在 Binius 中,致力于multilinear多项式:

  • 1)有一个hypercube P ( x 0 , x 1 . . . x k ) P(x_0, x_1 ... x_k) P(x0,x1...xk),其中 P ( 0 , 0...0 ) , P ( 0 , 0...1 ) , ⋯   , P ( 1 , 1...1 ) P(0, 0 ... 0), P(0, 0 ... 1),\cdots,P(1, 1 ... 1) P(0,0...0),P(0,0...1),,P(1,1...1)各个evaluations值,为所关心的值。
  • 2)为证明在某点的某evaluation,会将hypercube中相同的数据“re-interpret重新解析”为方形。
  • 3)然后对每行进行扩展,使用基于bits groups的Reed-Solomon编码,以提供随机Merkle branch queries安全所需的数据冗余性。
  • 4)计算行的随机线性组合,其系数设计(即tensor product)为row combination后的新row,确实持有所关心的在随机点的evaluation值。
  • 5)prover:将新row(将其重新解析为如128行bits),以及一些随机选择的具有Merkle branches的列,发送给verifier。相应的数据量为 O ( N ) O(\sqrt{N}) O(N ),其中新row size为 O ( N ) O(\sqrt{N}) O(N ),所随机选的各列的数据量为 O ( N ) O(\sqrt{N}) O(N )
  • 6)verifier:
    • 将新row重新解析为如128行bits,然后做扩展后的结果(即"extension of the row combination");将prover所发来的随机扩展Merkle branches的列做row combination的结果(即"row combination of the extension"),二者必须匹配。
    • 对新row做column combination,计算结果应于prover所声称的evaluation值匹配。

这即为Binius证明系统,或者称为多项式承诺方案——为证明系统的关键基石。

10. 本文未涉及内容

本文没有涉及的内容有:

  • 1)高效的算法来做行扩展:这实际上是提高verifier O ( N ) O(\sqrt{N}) O(N )计算效率所必需的。使用简单的Lagrange插值,只能得到 O ( N 2 3 ) O(N^{\frac{2}{3}}) O(N32)
    为此,需使用二进制域的快速傅里叶变换,可参考Vitalik 2019年5月12日博客 Fast Fourier Transforms(尽管确切的实现会有所不同,因为本文使用了效率较低的、不基于递归扩展的构造)。
  • 2)算术化。单变量多项式很方便,因为可以做类似的 F ( X + 2 ) − F ( X + 1 ) − F ( X ) = Z ( X ) ∗ H ( X ) F(X+2) - F(X+1) - F(X) = Z(X) * H(X) F(X+2)F(X+1)F(X)=Z(X)H(X)来关联计算中的相邻步骤。在hypercubes中,“下一步”的解释并不像“ X + 1 X + 1 X+1“一样干净清晰。可以做 X ∗ k X*k Xk并跳转到取整的powers of k k k,但这种跳跃行为会牺牲 Binius 的许多关键优势。Binius论文介绍了此问题的解决方案(如,参见第 4.3 节),但这本身就是一个“深不可测的兔子洞”。
  • 3)如何真正安全地进行特定值检查。Fibonacci示例需要检查关键边界条件: F ( 0 ) = F ( 1 ) = 1 F(0) = F(1) = 1 F(0)=F(1)=1,以及 F ( 100 ) F(100) F(100)的值。但是对于“raw” Binius,在预先知道的evaluation point进行检查是不安全的。有相当简单的方法可以将已知evaluation检查转换为未知evaluatioin检查,使用所谓的sum-check协议;但本文没有讨论这些。
  • 4)lookup协议是另一项最近被广泛使用的技术,用于制作超高效的证明系统。Binius 可与lookup协议结合用于许多应用程序。
  • 5)超越平方根验证时长。平方根很昂贵:一个 2 32 2^{32} 232 bits的Binius 证明长约 11 MB。可使用其他证明系统来解决这个问题,以制作“Binius 证明的证明”,从而获得 Binius 在证明主statement方面的效率和较小的proof size。另一个选择是更复杂的FRI-Binius协议(详情见2024年论文《Polylogarithmic Proofs for Multilinears over Binary Towers》),可创建(如常规 FRI的)poly-logarithmic-sized proof。
  • 6)Binius 如何影响“SNARK-friendly”。基本总结是,如果使用 Binius,不再需要太关心如何使计算“算术友好”:“常规”哈希不再比传统算术哈希更有效率,乘法模 2 32 2^{32} 232或模 2 256 2^{256} 2256与乘法模 p p p相比不再是一个大难题,等等。但这是一个复杂的话题;当一切都以二进制完成时,很多事情都会发生变化。

期望在未来几个月内基于二进制域的证明技术会有更多改进。

参考资料

[1] Vitalik 2024年4月29日博客 Binius: highly efficient proofs over binary fields

Binius系列博客

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值