STARKs : 多项式证明(一)

相信现在很多人都听说过ZK-SNARKs(Zero-Knowledge Succinct Non-Interactive Argument of Knowledge零知识下简明的非交互知识论证,该技术可以解决如下问题:证明者和验证者交换信息,在不直接展示该知识的情况下,使验证者确信“证明者的确拥有这项知识”),这是一种通用的、简洁的零知识证明技术,它可以应用于可验证计算、需要保护隐私的加密货币等用途。

你可能不知道的是,ZK-SNARKs还有一个兄弟:ZK-STARKs。其中的T字母代表“透明的”,ZK-STARKs解决了ZK-SNARKs的一个主要缺陷,即ZK-SNARKs依赖于“可信运行环境”。ZK-STARKs还具有更简单的密码学假设,避免了使用椭圆曲线、配对和指数知识假设,而完全依赖哈希和信息理论;这也意味着即使是攻击者使用量子计算机,也能保证加密的安全。

然而,这是有代价的:证明内容的大小从288字节增加到几百 KB,在传统的中心化应用中,这样的代价不太值得;但在其他时候,特别是在类似公有链的应用程序中,对信任最小化的需求非常高,那这样的代价可能是非常值得的;如果哪一天椭圆曲线被破解或者量子计算机出现的话,那这么点代价就一定是值得的了。

   01   新的零知识证明是如何工作的?

 How does this other kind of zero knowledge proof work?

首先,让我们回顾一下零知识证明的一般工作原理是怎样的。假设你有一个(公共)函数f,一个(私有)输入x和一个(公共)输出y,你要证明你知道一个x,使f(x) = y等式成立,却不用告诉验证者x具体是什么。此外,为了使证明更简洁,你希望证明过程比计算f本身要快得多。

让我们来考虑以下几个例子

  • f是一个计算任务,在普通计算机上运行需要两周时间,但在数据中心运行只需要两小时。你把计算任务f提交到数据中心进行计算,计算中心计算后返回答案y。你在几毫秒内就可以完成验证,并确信y就是答案。

  • 你有一个加密的交易,格式为“X1是我的原始余额、X2是你的原始余额、X3是我的新余额、X4是你的新余额。”你希望创建一个有效交易的证明,证明的具体内容是:1、所有的余额都是非负数2、X1-X3 = X4-X2。你可以很容易的构建一个函数f,这个函数f的输入参数x是秘钥,执行过程:解密交易,执行检查,如果它通过,返回1,如果它不通过,返回0。y当然为1。

  • 假设你是区块链(比如以太坊)的用户,并且下载了最新的区块。你现在想要知道这个区块是不是有效的,且这个块位于有效链的末端。你可以向有完整区块信息的节点要一个这样的有效证明。x是整个区块链,f通过逐块验证有效性并最终输出前一个区块的哈希值的函数,而y是刚刚下载的块的哈希值。

那么,这些例子的困难之处在哪呢?事实证明,零知识(即隐私)证明(相对而言)容易提供;有很多方法可以把任何计算问题都转化为类似三色图的问题,三色图对应于原始问题的解,然后用传统的零知识证明协议来证明你有一个有效的图,但你却没有透露计算的具体内容。马修·格林(Matthew Green)在2014年有一篇非常精彩的文章对这一点做了详细的描述。

其实最难实现的还是简洁性。简单来说,要简洁的证明计算是否正确是非常困难的,因为计算的过程非常脆弱。假设你有一个又长又复杂的计算任务,而且你有在计算过程中改变任意一位bit从0变成1的能力,那么在很多情况下即使改变这一点点都足以使计算结果完全不同。因此,有时候很难检查出你是否对计算过程做过什么手脚,如果只对计算过程进行抽样检查的话,那就非常容易忽略你做过的这一点点手脚了。然而,这种做手脚的情况是可以通过一些数学方法避免的。

一般非常高级的处理方式是,完成此操作的协议使用了与纠删码相似的数学方法,纠删码用于数据容错。如果你有一段数据,你把数据编码成一条线,那么你就可以在该直线上选择4个点。这四个点中的任意两个都足以重建原始线,因此还能得到另外两个点。此外,如果你对数据做了任何微小的更改,那么至少保证四个点中的三个。你也可以把数据编码成一个1,000,000次的多项式,在多项式上选出2,000,000个点;这些点中的任何1000001都将恢复原始数据,因此其他点,原始数据中的任何偏差都将至少更改1,000,000个点。这里展示的算法,是使用大量多项式让误差放大。

   02  一些简单的示例帮助你更好的理解

 A Somewhat Simple Example

假设你想证明你有一个多项式P使得P(x)是一个整数,0 <= P(x) <= 9,对于所有的x从1到100万。这是一个“范围检查”的简单实例;你可以把这种检查是想象成某种验证方式。例如,在处理过一些交易之后,一组账户余额仍为正数。如果是1 <= P(x) <= 9,这可能是检查这些值是否是构成正确的数独解的一部分。

证明这个问题的“传统”方法是列出所有的1,000,000点,并通过检查值来验证它。然而,我们想看看我们是否可以找到一种在不到1,000,000步内被验证的证明方法。简单地随机检查P的值是不行的;恶意验证者总是有可能在999,999个地方找到一个P满足约束,但在最后一个地方不满足约束,并且随机抽样几个值基本上是会忽略这个值的。那么我们该做什么呢?

让我们从数学的角度来解决这个问题。假设C(x)是一个约束检验多项式;如果0 <= x <= 9,则C(x) = 0,否则为非零。有一个简单的方法来构造C(x): x * (x-1) * (x-2) *…* (x-9)(我们假设所有的多项式和其他值都使用唯一的整数,所以不需要担心中间的数字)。

现在,问题变成:证明已知P,x从1到1,000,000,使得C(P(x)) = 0,Z(x) = (x-1) * (x-2) *…(x - 1000000)。已知的数学事实是,Z(x)的多项式等于0,在x从1到1,000,000时值等于0。因此,这个问题可以被再次转换: 证明已知P和D,使得所有x的C(P(x)) = Z(x) * D(x)(注意,如果你知道一个合适的C(P(x)),那么将它除以Z(x)来计算D(x)并不太难; 你可以使用长多项式除法,或者使用基于快速傅里叶变换的更快的算法)。现在,我们已经把原来的语句转换成数学上看起来很清楚而且很可能是可以证明的。

那么,如何证明呢?我们可以将证明过程想象为证明者和验证者之间的三步通信:证明者发送一些信息,然后验证者发送一些请求,然后证明者发送更多的信息。首先,证明者提交一个x符合P(x)和D(x)从1算到10亿(即,创建一个Merkle树并向验证者发送根哈希值)。这包括0 <= P(x) <= 9的100万个点,以及可能不是这样的9.99亿个点。

我们假设验证者已经知道所有这些点的Z(x)的值;x就像一个“公钥”,每个人都必须提前知道(用户端没有Z(x)的存储空间,但是可以简单地存储全部Z(x)的Merkle根,并需要证明者为每个需要查询Z(x)值的验证者提供分支;另外,对于某些x,Z(x)是很容易计算的)。在接收到证明者提交的Merkle 根之后,验证者随机选择1到10亿之间的16个x值,并要求证明者提供那里P(x)和D(x)的Merkle分支。证明者提供这些值,验证者检查:

(i) 分支是否与之前提供的Merkle根匹配,

(ii) C(P(x))在所有16种情况下是否等于Z(x) * D(x)。

我们知道这个证明是完整的——如果你确实知道一个合适的 P(x),那么如果你计算 D(x) 并正确构造证明,它将始终通过16次检验。但是可靠性又如何呢?--也就是说,如果一个恶意的证明者提供了一个错误的 P(x),那么他们被发现的最小概率是多少?我们可以分析一下,因为 C(P(x)) 是一个由一个1,000,000次的多项式组成10次多项式,叠加起来就是10,000,000次。一般来说,我们知道两个不同程度的 N 次多项式在最多N个点上是一致的; 因此,一个10,000,000次的多项式和总是等于 Z(x) * D(x) 的多项式,如果这两个多项式不同的话,对于 x 至少有990,000,000点不相同。因此,一个错误的 P(x) 在一个回合中被抓住的概率已经达到99%;如果想要通过16次检查,不被发现的概率达到10-32,也就是说,这个方案就像计算哈希冲突一样难以伪造。

我们刚才做了什么?我们使用多项式来“提高”任何错误解的误差,所以,对于任何不正确的解决方案解决原始问题,都需要上百万次检查才能发现,转换为验证协议的解决方案,即使只进行一次检查,也可以在99%的时间内将其标记为错误。

我们可以将这个三步机制转换成非交互式证明,它可以由一个证明者广播一次,然后由任何人使用Fiat-Shamir启发式进行验证。证明者首先构建P(x)和D(x)值的Merkle树,并计算树的根哈希值。根本身就是熵的来源,它决定了证明者需要提供的树的哪些分支。然后证明者将Merkle根和分支作为证明一起广播。计算都是在证明方进行;从数据中计算Merkle根的过程,然后使用它选择要审计的分支,有效地替代了对交互式校验者的需求。

没有有效 P(x) 的恶意证明者唯一可以做的事就是一遍又一遍的尝试做出有效的证明,直到最终可以非常幸运地得到计算Merkle根选择的分支,但可靠性为10-32,以现在的计算能力,需要数十亿年的恶意证明才能作出可通过的证据。

   03   更进一步说明,或许你会想知道更多

 Going Further

为了说明这种技术的强大之处,让我们使用它来做一些不那么重要的事情:证明你知道第一百万个斐波纳契数。为了证明,我们将证明你已知计算带的多项式,P(x)代表第x个斐波那契数。斐波那契数的规则如下:C(x1,x2,x3)= x3-x2-x1(注意如何C(P(x),P(x + 1),P(x + 2))= 0表示所有x,则P(x)表示斐波纳契数列)。

转换后的问题是:证明已知P和D ,C(P(x), P(x+1), P(x+2)) = Z(x) * D(x)。对于证明审计的16个索引中的每一个,证明者需要为P(x)、P(x+1)、P(x+2)和D(x)提供Merkle分支。验证程序还需要提供Merkle分支,以显示P(0) = P(1) = 1。否则,整个过程是相同的。

现在,要实现这一目标,有两个问题需要解决。第一个问题是如果我们试着用常规的数字来解决问题在实践中效率不高,因为数字本身很容易变的非常大。例如,第一百万个斐波那契数是一个208988位数。如果我们真的想要在现实中简洁的实现,而不是用常规数字做多项式,我们需要利用有限域——系统数量仍然遵循相同的数学规则,就像一个a*(b + c)=(a * b)+(a *c)和(a ^2 -b ^ 2)=(a - b)*(a + b),但保证每个数字占用一定量的空间。要证明第一百万个斐波那契数就需要一个更复杂的设计,在这个有限域数学的基础上实现大数运算。

最简单的有限域是模运算;也就是说,对于某些素数N,将a + b mod N替换为a + b的每个示例,减法和乘法也是和加法相同处理方式。对于除法使用模逆运算(例如:如果N = 7,那么3 + 4 = 0,2 + 6 = 1,3 * 4 = 5,4 / 2 = 2和5 / 2 = 6)。你可以从维基百科中“主要字段”查看的描述,或关于模块化算术的维基百科文章(通过直接搜索“有限字段”找到的文章)中了解有关这些数字系统的更多信息。不幸的是,“素数领域”往往非常复杂,与抽象代数相关,不要在意这些了。

其次,你可能已经注意到,在我上面关于可靠性的证明概要中,我忽略了一种攻击方式:如果攻击者提交的不是可信的1,000,000个P(x)和9,000,000个D(x)的值,而是任何相对低次多项式上都没有的值,该怎么办?那么,一个无效的C(P(x))必须与任何有效C(P(x))不一样,至少在9.9亿点上是不一样的,因此可能存在更有效的攻击类型。例如,攻击者可以为每一个x生成一个随机值p,然后计算d = C(p) / Z(x),并将这些值提交给 P(x)和D(x)。这些值不会在任何低阶多项式上,但它们会通过测试。

事实证明,这种可能性还是可以有效的防范的,尽管这样做需要相当复杂的工具,因此你可以相当合理地说,它们填补了STARKs数学创新的空缺。此外,该解决方案也有一个限制:你可以清除对任何1,000,000次多项式的数据的证明(例如,你需要更改所有值的20%以使其成为1,000,000多项式),但是你不能排除仅与一个或两个坐标不同的多项式数据。因此,这些工具可以提供的是邻近证明 - 证明P和D上的大多数点都是多项式的正确解。

尽管有两个“意外情况”,但是作为证明方法已经足够了。首先,验证者需要检查几个索引,以弥补这些局限性引入的错误。其次,如果我们还要做“边界约束检查”(例如,在上面的Fibonacci示例中验证P(0)= P(1)= 1),我们还要扩展临近证明,不仅证明大多数点是在同一个多项式上,但也证明了那两个特定点(或者你想要检查的任何其他数量的特定点)都在该多项式上。

 04   总结

 Review and summarize 

在本系列的第二部分中,我将更详细地描述接近检查的解决方案,在第三部分中,我将描述如何构造更复杂的约束函数,不仅检查斐波那契数和范围,还检查任意计算。

猜你喜欢

转载自blog.csdn.net/weixin_42470308/article/details/81781216
今日推荐