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/gadgets/ecc/var-base-scalar-mul.html

变基标量乘(Variable-base scalar multiplication)

在 Orchard 电路中,我们需要检查 ,其中 ,且标量域(scalar field)为 ,

我们有 ,其中

见证标量(Witness scalar)

我们试图计算 ,其中 。设 。那么我们可以计算

前提是 ,即 ,这覆盖了我们需要的整个范围,因为事实上

因此,给定一个标量 ,我们见证 的布尔分解(boolean decomposition)。(为便于输入到变基标量乘算法中,我们使用大端(big-endian)位序。)

变基标量乘(Variable-base scalar multiplication)

我们使用一个优化过的倍加(double-and-add)算法,从 《Faster variable-base scalar multiplication in zk-SNARK circuits》 复制而来,并做了一些变量名修改:

Acc := [2] T
for i from n-1 down to 0 {
    P := k_{i+1} ? T : −T
    Acc := (Acc + P) + Acc
}
return (k_0 = 0) ? (Acc - T) : Acc

剩下要检查的是,每对要相加的点的 x 坐标都互不相同。

当在素数阶群(prime-order group)中相加点时,我们可以依赖 Halo 论文 附录 C 中的定理 3,它表明:如果我们有两个这样的点,它们相对于给定的奇素数阶基有非零索引(index),且这些索引取在 范围内时,忽略符号是互不相同的,那么它们就有不同的 x 坐标。这很有帮助,因为对标量乘算法中出现的点的索引进行推理,比直接对它们的 x 坐标进行推理要容易。

因此,所需的检查等价于说,上述算法的下列"带索引版本"永远不会触发 assert:

acc := 2
for i from n-1 down to 0 {
    p = k_{i+1} ? 1 : −1
    assert acc ≠ ± p
    assert (acc + p) ≠ acc    // X
    acc := (acc + p) + acc
    assert 0 < acc ≤ (q-1)/2
}
if k_0 = 0 {
    assert acc ≠ 1
    acc := acc - 1
}

acc 的最大值是:

    <--- n 1s --->
  1011111...111111
= 1100000...000000 - 1

=

标记为 X 的 assert 显然不可能失败,因为 。可以看出,除了最后那个条件分支外,acc 是单调递增的。它在 取最大值时达到其最大值,即

所以,为了完全避免例外情形(exceptional cases),我们将需要 。但如果最后 次迭代使用完全加法(complete addition),我们就可以把 增大

使用不完全加法(incomplete addition)时算法第一个失败的 将是 ,因为 。我们需要 才能使上述环绕(wraparound)技巧奏效。

sage: q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001
sage: 2^253 + 2^252 - 1 < (q-1)//2
False
sage: 2^252 + 2^251 - 1 < (q-1)//2
True

所以循环的最后三次迭代()需要使用完全加法(complete addition),末尾的条件减法也是如此。用 ⸭ 表示不完全加法(如我们在规范中所做),把它写出来,我们有:

Acc := [2] T
for i from 253 down to 3 {
    P := k_{i+1} ? T : −T
    Acc := (Acc ⸭ P) ⸭ Acc
}
for i from 2 down to 0 {
    P := k_{i+1} ? T : −T
    Acc := (Acc + P) + Acc  // complete addition
}
return (k_0 = 0) ? (Acc + (-T)) : Acc  // complete addition

优化倍加的约束程序(Constraint program for optimized double-and-add)

定义一个移动求和(running sum),其中 且:

辅助函数 。 在代入 之后,这变为:

这里, 被分配到一个单元格(cell)中。这与之前的各个 不同,后者是从 隐式推导出来的,但从未真正被分配过。

被用于另外三个步骤中,使用完全加法(complete addition):

如果最低有效位(least significant bit) 我们置 否则我们置 。然后我们使用完全加法返回

输出

(注意 表示 。)

不完全加法(Incomplete addition)

我们需要六个 advice 列来见证 。然而,由于 是相同的,我们可以在一行中执行两次不完全加法,复用相同的 。我们把不完全加法中使用的标量位分成 两半并行处理。这意味着我们实际上有两个 for 循环:

  • 第一个,覆盖 半部分,,在 处有一个特殊情形;以及
  • 第二个,覆盖 半部分,即剩余的 ,在 处有一个特殊情形。

对于每个 半部分,我们有三组门(gate)。注意 是从 取值; 不是对行做索引。

这个门仅用于第一行(在 for 循环之前)。我们检查 被初始化为与初始 一致的值。 其中

这个门用于对应 for 循环的所有行,除最后一行外。

其中

这个门用于 for 循环的最后一次迭代,处理特殊情形,其中我们检查输出 被正确见证。 其中

完全加法(Complete addition)

我们复用完全加法(complete addition)约束来实现倍加的最后 轮。这需要每轮两行,因为每次完全加法都需要 9 个 advice 列。在第 10 个 advice 列中,我们暂存为正确实现倍加所需的其他单元格:

  • 基点的 坐标,这样我们可以对它进行条件取负,作为其中一次完全加法的输入。
  • 移动求和(running sum),我们在两行上而非顺序地对它进行约束。

布局(Layout)

约束(Constraints)

除了完全加法约束之外,我们定义以下门(gate):

其中

LSB(最低有效位)

布局(Layout)

约束(Constraints)

其中

溢出检查(Overflow check)

对于任何 , 都不会溢出,因为它是仅到 为止的若干位的加权求和,而 小于 (也小于 )。

然而, 可能会溢出

注:对于全宽标量乘,可能无法在基域中表示 (例如当基域是 Pasta 的 时)。 在这种情况下,我们需要对涉及 的那一行进行特殊处理,使其对于我们用于全宽标量的任何表示都是正确的。 我们对 的表示将是这个对:。 我们将用 代替 作为 ,并把 约束到 254 位,使它能放入一个 元素。 然后我们只需把下面的论证推广到适用于 (因为 的最大值是 )。

由于溢出只可能发生在约束 的最后一步,我们有 。那么只需再检查 (使得 )以及 即可。这些条件合在一起就意味着 作为整数成立,因此如所需有

注:位 并不表示一个对 取模归约后的值,而是表示未归约的 的一个表示。

的优化检查(Optimized check)

由于 (若 互换也成立),我们有

我们可以假设

(对于 Orchard 中变基标量乘的使用而言这是真的,在那里我们知道 。如果我们互换 使得 ,这也成立。 但对于一个全宽标量 时,它成立。)

因此,

给定 ,我们如下证明 的等价性:

  • 把该区间平移 ,得到 ;
  • 观察到 保证落在 内,因此对 取模时既不会溢出也不会下溢;
  • 利用 这一事实,观察到

(我们可以用另一种方式看出这是正确的:观察到它检查的是 ,所以上界如我们所预期地对齐。)

现在,我们可以从 继续优化:

约束为全 或非全 ,几乎可以"免费"实现,方法如下。

回想 ,所以我们有:

所以 全为 当且仅当

最后,我们可以通过检查 ,把 两种情形的 位分解合并起来。

溢出检查约束(Overflow check constraints)

。溢出检查的约束为:

定义

见证 ,并把 分解为

那么所需的门(gate)为:

其中 可以由另一个移动求和计算得到。注意 这个因子对约束没有影响,因为右边为零。

移动求和范围检查(Running sum range check)

我们在电路中利用一个 查找范围检查(lookup range check)来减去 的低 位。该范围检查减去 的前 位,并把结果右移,得到

溢出检查(通用版)(Overflow check (general))

回想我们定义了 ,其中

对于任何 , 都不会溢出,因为它是仅到并包括 为止的若干位的加权求和。当 时,此求和至多为 ,小于 (也小于 )。

然而,对于全宽标量乘,可能无法在基域中表示 (例如当基域是 Pasta 的 时)。在这种情况下, 可能会溢出

所以,我们需要对涉及 的那一行进行特殊处理,使其对于我们用于全宽标量的任何表示都是正确的。

我们对 的表示将是这个对:。我们将用 代替 作为 ,并把 约束到 254 位,使它能放入一个 元素。

然后我们只需把 Orchard 电路中变基标量乘所使用的溢出检查 推广到适用于 (因为 的最大值是 )。

注:位 并不表示一个对 取模归约后的值,而是表示未归约的 的一个表示。

溢出只可能发生在约束 的最后一步,并且只有在 设置了权重为 的那一位时(即 时)才会发生。如果我们改为设置 ,那么现在 就不会溢出,并且应当等于

那么只需再检查 作为整数成立(其中 )即可。

表示为 ,其中我们约束 ,并约束 为布尔值。为使这是一个规范表示,我们还需要

如果 :

  • 约束 。这不会溢出,因为在这种情况下 ,因此

如果 :

  • 我们应当在 时且仅在此时有 ,即把 见证为布尔值,然后
    • 如果 ,则约束
      • 这可以通过约束 二者之一来完成。( 不会溢出。)
    • 如果 ,则约束
      • 这可以通过约束 来完成。

溢出检查约束(通用版)(Overflow check constraints (general))

如上把 表示为

溢出检查的约束为:

注意标记为 的四个 130 位约束分成两对,出现在不相交(disjoint)的情形中。因此我们可以用一个新的见证变量 把它们合并为两个 130 位约束;另一个约束始终是对 的约束:

( 是无约束的,在 的情况下可以见证为 。)

成本(Cost)

  • 25 次 10 位范围检查和一次 3 位范围检查,用于把 约束到 253 位;
  • 25 次 10 位范围检查和一次 3 位范围检查,用于在 时把 约束到 253 位;
  • 两次 13 个 10 位范围检查。