Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

译自 halo2 book:https://zcash.github.io/halo2/design/proving-system/permutation.html

置换论证(Permutation argument)

鉴于 halo2 电路中的门是"局部地"操作的(作用于当前行或定义好的相对行中的单元格),因此常常需要把某个任意单元格中的值复制到当前行中,以供门使用。这是通过等式约束(equality constraint)来完成的,该约束强制源单元格与目标单元格包含相同的值。

我们实现这些等式约束的方式,是构造一个表示这些约束的置换,然后在证明中使用置换论证来强制它们成立。

记号

置换(permutation)是一个集合到其自身的一一(one-to-one)且映满(onto)的映射。一个置换可以唯一地分解为若干循环(cycle)的复合(在循环的排序以及每个循环的旋转意义下)。

我们有时使用循环记号(cycle notation)来书写置换。设 表示一个循环,其中 映到 , 映到 , 映到 (对任意大小的循环有显然的推广)。把两个或多个循环并排书写表示对应置换的复合。例如, 表示这样的置换:把 映到 , 映到 , 映到 , 映到

构造置换

目标

我们想要构造一个置换,使得处于同一等式约束集合中的每一组变量构成一个循环。例如,假设我们有一个电路,定义了如下等式约束:

由此我们得到等式约束集合 。我们想要构造置换:

它定义了从 的映射。

算法

我们需要跟踪一组循环,这是一个不相交集合的集合(set of disjoint sets)。针对这一问题已有高效的数据结构;为简单起见,我们选择一个并非渐近最优、但易于实现的数据结构。

我们将当前状态表示为:

  • 一个数组 ,表示置换本身;
  • 一个辅助数组 ,跟踪每个循环的一个特定代表元素;
  • 另一个数组 ,跟踪每个循环的大小。

我们有这样一个不变式:对于给定循环 中的每个元素 , 都指向同一个元素 。这使我们能够通过检查 来快速判定给定的两个元素 是否在同一循环中。此外, 给出包含 的循环的大小。(这一点仅对 成立,而不对 成立。)

算法以恒等置换(identity permutation)的表示作为起点:对所有 ,我们设 ,,

要添加一个等式约束 :

  1. 检查 是否已经在同一循环中,即是否 。若是,则无需做任何事。
  2. 否则, 属于不同的循环。让 为较大的循环、 为较小的循环:当 时把二者交换。
  3. 沿着映射绕较小(右侧)循环走一圈,对每个元素
  4. 通过交换 ,把较小的循环拼接(splice)进较大的循环中。

例如,给定两个不相交的循环 :

A +---> B
^       +
|       |
+       v
D <---+ C       E +---> F
                ^       +
                |       |
                +       v
                H <---+ G

在添加约束 之后,上述算法产生如下循环:

A +---> B +-------------+
^                       |
|                       |
+                       v
D <---+ C <---+ E       F
                ^       +
                |       |
                +       v
                H <---+ G

错误的替代做法

如果我们不检查 是否已经在同一循环中,那么我们可能会撤销一个等式约束。例如,如果我们有如下约束:

而我们试图仅用上述算法的第 5 步来实现等式约束的添加,那么我们最终会构造出循环 ,而不是正确的

论证规约(Argument specification)

我们需要检查 列中单元格的一个置换,这些列以拉格朗日基表示为多项式

我们将给这 列中的每个单元格标注一个 中唯一的元素。

假设我们在这些标签上有一个置换, 其中各循环对应于等式约束集合。

如果我们考虑序对的集合 ,那么每个循环中的各个值彼此相等,当且仅当用 对每个序对中的标签进行置换后,得到的是同一个集合:

一个关于循环 (A B C D) 的示例。置换标签之前的集合是 {(A, 7), (B, 7), (C, 7), (D, 7)},置换之后是 {(D, 7), (A, 7), (B, 7), (C, 7)},二者相同。如果其中一个 7 被替换为 3,那么置换标签之后的集合就不再相同。

由于标签互不相同,集合相等等同于多重集合(multiset)相等,而后者可以用乘积论证(product argument)来检查。

为一个 次单位根(root of unity),设 为一个 次单位根,其中 , 为奇数且 我们将用 作为置换论证中第 列第 行单元格的标签。

我们用一个由 个多项式 构成的向量来表示 ,使得

注意,恒等置换可以用由 个多项式 构成的向量来表示,使得

我们将用一个挑战 把每个 序对压缩为 正如我们在查表中所用的乘积论证一样,我们还使用一个挑战 来随机化乘积的每一项。

现在,给定由 表示的置换,作用于由 表示的各列,我们想要确保:

这里 表示未置换的 序对,而 表示已置换的 序对。

满足 ,且对 :

那么,强制如下规则就足够了:

这里假设列数 使得上述第一条规则中的多项式能够落在 PLONK 配置的次数界之内。我们将在下文看到如何处理更多列数的情况。

用于得到恒等置换简洁表示的这一优化由 Vitalik Buterin 为 PLONK 提出,描述在 PLONK 论文第 8 节末尾。注意,只要启用等式约束的列数不超过 ,这些 就都是各不相同的二次非剩余(quadratic non-residue);对于 Halo 2 所用的曲线,这一点在实践中总是成立。

零知识调整

查表论证类似,我们需要对上述论证作出调整,以应对每一列最后 行被填充为随机值的情况。

我们把可用行的数量限制为 。我们添加两个选择子,其定义方式与查表论证中相同:

  • 在最后 行上置为 ,其余处为 ;
  • 仅在第 行置为 ,其余处为 (即它被置于可用行与盲化行之间的那一行)。

我们仅对可用行启用上面的乘积规则:

在第 行启用的规则保持不变:

由于我们不能再依赖绕回来保证每个乘积 处重新变为 ,我们改为需要约束 。这就引出了与查表论证中所描述的相同的问题。因此我们允许 取零或一:

这给出了完美完备性和零知识。

跨越大量列

halo2 的实现在实践中并不限制可启用等式约束的列数。因此,它必须解决一个问题:上述方法可能产生一个乘积规则,其中的多项式超出了 PLONK 配置的次数界。次数界可以提高,但如果没有其他规则需要更大的次数,这样做会效率低下。

取而代之,我们把乘积拆分到 个由 列组成的列集(column set)上,使用乘积列 ,并用另一条规则把乘积从一个列集的末端复制到下一个列集的开端。

也就是说,对 我们有:

为简单起见,这里的书写假设启用等式约束的列数是 的倍数;如果不是,那么最后一个列集的乘积将少于 项。

对于第一个列集我们有:

对于每个后续列集 ,我们用如下规则把 复制到下一个列集的起点 :

对于最后一个列集,我们允许 取零或一:

这与之前一样给出了完美完备性和零知识。