本文介绍了带符号桶索引的多标量乘法(MSM)技术,也称为NAF方法。该方法通过将桶索引视为带符号整数,减少了桶的数量和内存使用。文章详细解释了如何将标量切片映射到新的范围,并提供了伪代码算法。同时讨论了处理最高切片溢出的问题,并提出了一种改进的技巧来避免最终进位。
如果我们把桶索引当作有符号整数,我们就能在桶索引编码中节省一位,因此减少桶的数量,并将桶的内存使用量减半。这个方法被称为带符号桶索引方法,也被称为 NAF 方法。这个方法在 [1] 中被报告,并且被 zPrize MSM-WASM track 的前两名实施者实现。
NAF 代表 Non-Adjcent Form(非相邻形式)。NAF 的经典定义记录在 Textbook Definition of Non-Adjacent Form (NAF)。
在桶方法中,我们把每个标量
s 切分成
c-bit 的切片
si,并且使用
si 作为桶索引。如果我们忽略桶
0 (因为
0∗P=0),那么我们有
si∈{1,...,2c−1}
所以,我们总共需要
2c−1 个桶。
但是,如果有一种方法(稍后解释)把
si 映射到以下范围
{−2c−1,...,−1,1,...,2c−1−1}
我们可以使用以下技巧,
if slice s_i > 0, add P to bucket // 如果切片 s_i > 0,则将 P 添加到桶中
if slice s_i < 0, add -P to bucket // 如果切片 s_i < 0,则将 -P 添加到桶中
然后,我们只需要桶
{1,...,2c−1},也就是总共
2c−1 个桶。
例如,对于切片
si∈{1,2,3,4,5,6,7},总共需要
23−1=7 个桶,如果我们把
si 映射到
{−4,−3,−2,−1,1,2,3},只需要
2(3−1)=4 个桶。
该部分从 Gregor Baude 在一个 slack 频道中的描述修改而来
给定窗口大小
c, 记
L=2c。
首先,把标量
s 切分成
K 个切片
si 在数学上意味着
s=s0+s1L+...+sK−1LK−1=∑isiLi
注意,在两个切片之间,我们有如下等式:
siLi+si+1Li+1=(si−L)Li+(si+1+1)Li+1
换句话说,我们可以把一个切片减少
L,通过把
1 进位到下一个更高的切片。
最初,
si 在 [0, L) 范围内,所以我们需要
L−1 个桶(忽略
0 桶)。我们想要摆脱上半部分。所以,无论何时
si>=L/2,我们把它替换成
si−L 并把 1 加到下一个切片
si+1。
在这次转换之后,
si 在
[−L/2,L/2) 范围内。如果
si 是负数,我们可以对切片和点都取反,因为
(−si)∗G=si∗(−G)。这样做会使我们的
si 非符号位进入
[0,L/2] 范围。所以得到一半大小的范围。确实,我们把
2c−1 个桶减少到
2c−1 个桶。
所以伪代码的算法是:
let carry = 0; // 设进位为0
for (i = 0,...,K-1) {
s[i] += carry; // we have to add the carry bit from last iteration! // 我们必须加上来自上次迭代的进位!
if (s[i] >= L/2) {
s[i] = L - s[i]; // note: we subtract L and negate at once // 注意: 我们同时减去 L 并取反
carry = 1;
addToBucket(-point, s[i]); // use negated point // 使用取反的点
} else {
carry = 0;
addToBucket(point, s[i]);
}
}
警告:如何处理最高切片
sK−1>=L/2 的情况?
上面的实现忽略了最终的进位结果(我们没有把它加到任何地方)。所以,这里有一个隐含的假设:最终的进位是
0。如果最终的进位是
1,那么这个和就不对了。
什么时候最终的进位会是
1?
让我们首先假设窗口大小不能均分标量位长度。具体来说,对于标量长度
128 位(那是你在 GLV 分解后得到的),假设你的窗口宽度是
c=13。因为
13∗9=117,你会得到
10 个切片,但是最终的切片只有
11 位被设置
(128−117),而不是完整的
13 位。这意味着在上面算法的最后一步,我们永远不会处于
s[i]>=L/2 的情况,所以最终的进位永远不会是
1。我们很好!
但是,如果 c 能均分标量位长度,大约一半的标量会有
1 的最终进位。所以,这会使算法不正确。这具体发生在
c=16 的情况,这是 MSM 大小为
218 的最佳窗口宽度。
一个简单的解决方案是把标量位长度设置为比实际值大一位,
129 而不是
128。然后,你保证永远不会有最终进位。但是,这很愚蠢,因为在
c=16 的情况下,这意味着你的最终切片只有
1 位,并且你为了这
1 个额外的位运行了一个完整的子 MSM。
一个改进的技巧是转换标量,使它们永远不会设置它们的 MSB。(注意:我们在这里谈论的是整个标量的 MSB,而不是单个切片的 MSB。这一切都是为了避免最终进位。)以下技巧在 Yrrid 的解决方案中实现。
如果标量的 MSB 被设置,我们对标量和点取反,并继续正常处理。这可以工作因为:(M - s)(-P) = -s (-P) = s P
如果没有 GLV 分解,标量有
255 位,也就是
15∗17,所以坏的情况是
c=15,17。我们可以简单地避免这些窗口大小,或者做一些天真的事情,假装标量大小是
256。在 GLV 分解后,我们需要处理两个标量的一半,两个都不能设置它们的 MSB。
[1] Multi-scalar multiplication: state of the art & new ideas with Gus Gutoski [youtube 49:00]
####### tags: msm
zkp
public
- 原文链接: hackmd.io/jNXoVmBaSSmE1Z...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!