cyberorange
  • NEW!
    NEW!  Connect Ideas Across Notes
    Save time and share insights. With Paragraph Citation, you can quote others’ work with source info built in. If someone cites your note, you’ll see a card showing where it’s used—bringing notes closer together.
    Got it
      • Create new note
      • Create a note from template
        • Sharing URL Link copied
        • /edit
        • View mode
          • Edit mode
          • View mode
          • Book mode
          • Slide mode
          Edit mode View mode Book mode Slide mode
        • Customize slides
        • Note Permission
        • Read
          • Only me
          • Signed-in users
          • Everyone
          Only me Signed-in users Everyone
        • Write
          • Only me
          • Signed-in users
          • Everyone
          Only me Signed-in users Everyone
        • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invite by email
        Invitee

        This note has no invitees

      • Publish Note

        Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

        Your note will be visible on your profile and discoverable by anyone.
        Your note is now live.
        This note is visible on your profile and discoverable online.
        Everyone on the web can find and read all notes of this public team.

        Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

        Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

        Explore these features while you wait
        Complete general settings
        Bookmark and like published notes
        Write a few more notes
        Complete general settings
        Write a few more notes
        See published notes
        Unpublish note
        Please check the box to agree to the Community Guidelines.
        View profile
      • Commenting
        Permission
        Disabled Forbidden Owners Signed-in users Everyone
      • Enable
      • Permission
        • Forbidden
        • Owners
        • Signed-in users
        • Everyone
      • Suggest edit
        Permission
        Disabled Forbidden Owners Signed-in users Everyone
      • Enable
      • Permission
        • Forbidden
        • Owners
        • Signed-in users
      • Emoji Reply
      • Enable
      • Versions and GitHub Sync
      • Note settings
      • Note Insights New
      • Engagement control
      • Make a copy
      • Transfer ownership
      • Delete this note
      • Save as template
      • Insert from template
      • Import from
        • Dropbox
        • Google Drive
        • Gist
        • Clipboard
      • Export to
        • Dropbox
        • Google Drive
        • Gist
      • Download
        • Markdown
        • HTML
        • Raw HTML
    Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
    Create Create new note Create a note from template
    Menu
    Options
    Engagement control Make a copy Transfer ownership Delete this note
    Import from
    Dropbox Google Drive Gist Clipboard
    Export to
    Dropbox Google Drive Gist
    Download
    Markdown HTML Raw HTML
    Back
    Sharing URL Link copied
    /edit
    View mode
    • Edit mode
    • View mode
    • Book mode
    • Slide mode
    Edit mode View mode Book mode Slide mode
    Customize slides
    Note Permission
    Read
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me Signed-in users Everyone
    Write
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me Signed-in users Everyone
    Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    3
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # 深入简出zkSNARKs 原创作者:香橙@cyberorange。 ###### tags: `zkSNARKs`, `Groth16` ## 1. 零知识证明初探 零知识证明是近几年密码学技术中最引人注目的技术之一,在区块链领域更是高频词,但是由于其涉及较多的数学知识,对一般的开发人员来说理解难度较大,而且资料多为英文,对国内区块链技术爱好者等学习可能造成困难。 所以我就综合论文和其他博客进行一个总结,外加点自己的理解成文,但因为我本身并非密码学专业,描述如有不当,请指正。 本文偏数学推导,非科普也不讲应用,感兴趣的话,可准备纸笔一路推导下去。 本文撰写前参考了Zcash博客,安比实验室博客,Vitalik等等许多博客以及多篇相关论文,但本文并非学术论文,所以就不一一列出引用了。 首先说说零知识证明,其实这个概念的出现已经很久了,零知识证明协议有很多种,zkSNARKs是目前应用最广泛的一种(包括各种改进方案),其他还有zk-STARKs、bulletproofs等等。 其基本概念就是一个证明者Prover向验证者Verifier证明某个陈述(Statement)是真是假,但在证明过程中不揭露任何其他信息。比如身份证明,某一个组织让组织内成员提供身份证明,但不想泄露任何信息,就可以使用零知识证明来完成。但是注意,这里的说的证明并非数学意义上命题证明意义上的证明,而是Prover去让Verifier相信某个陈述为真,属于概率证明,和数学推导的证明不一样。 目前,zcash,ethereum,filecoin等很多项目都使用了零知识证明来完成某些任务,比如隐私交易,链下交易验证,存储证明等等。 证明的简单形式化定义如下:设S是一个集合,那么一个(P,V)的证明系统必须满足下面的条件: 1)完备性(Completeness) $Prob[(P,V)(x) = Accept | x \in S] \geq \epsilon$ 2. 正确性(Soundness) $ProbP,V = Accept | x \notin S] \leq \delta$ 其中 $\epsilon \in (\frac{1}{2}, 1]$,$\delta \in [0,\frac{1}{2})$,分别为完备性概率和正确性概率,我们肯定是希望$\epsilon$越大越好,$\delta$越小越好。 简单解释,就是我们希望Prover拥有关于Statement的证据(Witness)的时候,Prover可以让Verifier相信陈述为真(完备性),而一个恶意的Prover将无法欺骗Verifier,来通过验证(正确性)。 那么,除了完备性和正确性以外,如何理解一个证明协议是否是零知识呢? 上面也说了,零知识要求证明者提供的证明除了可以验证陈述为真外,不会泄露任何信息,即证明本身在其概率空间内是均匀分布的(从熵来理解,即不包含任何信息冗余)。那么如何去定量地分析一个协议是否是零知识的? 对于零知识证明来说,证明其为零知识就需要为证明本身构造一个模拟器(Simulator),意思就是如果verifier不需要prover参与,可以自己构造出一个不可区分的证明出来的话,那么就能证明该协议是零知识的。 听起来有点绕,但逻辑很简单,就是如果不需要prover参与就可以构造证明的话,那这个过程本身就没有任何知识的参与,自然也就是零知识的,如果通过Simulator构造出来的证明,在概率分布上无法和prover提交的证明区分开,那么就相当于prover提交的证明也是零知识的。 举个例子,假设有AB两个电脑,我向A电脑输入一个字符串,B电脑自己生成随机串,如果两个字符串在概率分布上相同,则证明其实我什么有效信息也没有输进去,所以是零知识的。 下面举一个具体的例子。 - Schnorr协议 假设prover拥有私钥sk, pk = sk*G, G为椭圆曲线群上的一个生成元。 verifier拥有pk。 现在prover想向verifier证明自己拥有私钥,步骤如下: 1. 首先prover选取一个随机数k,并计算$K = k\cdot G$ ,将$K$发送给verifier; 1. verifier选取一随机数c发送给prover; 1. prover计算$r = k + c\cdot sk$,将r发送给verifier; 1. verifier验证$rG \space ?= \space K + c \cdot pk$。 由于椭圆曲线离散对数难题,有pk = sk * G,已知pk和G是无法逆推得sk的。 上面验证的关键在于Prover无法事先知道c,根据椭圆曲线离散对数难题,prover无法找到一对sk和k,使得计算的$r = k + c\cdot sk$满足验证,所以协议是满足正确性的。 而如果Prover确实知道sk,那么验证肯定是可以通过的,所以协议是满足完备性的。 但是如何证明上面的验证是零知识的呢?我们现在假设没有Prover参与,让Verifier在自娱自乐,去模拟一个零知识证明交互过程。 构造simulator: Verifier选取随机数c以及r,计算$K = rG-c\cdot pk$。此时全流程就已经被模拟出来了。 Prover发送的K,以及Verifier选取的c,最后响应的r都有了,但是这个过程本身并没有Prover参与,构造出来的证明与Prover的证明在概率上是无法区分的,所以协议本身是零知识的,而这个模拟器是依靠在发送k之前就已经知道c来构造的。 从上面的分析也可以看出,Prover一定不能事先知道c,否则也是可以模拟证明的,所以说上述零知识证明的关键就在于这个随机数。 ## 2. NP与QAP 通俗来讲,P问题即为在多项式时间内可解的一类问题,而NP问题属于在多项式时间内不可解,但是给定一个解,可以在多项式时间内验证解是否正确的一类问题,此外还有NP完全问题,属于NP问题中最难的一种,任何NP问题都可以归约(Reduction)到一个NP完全问题,所以只要找到了一个NP完全问题的多项式时间解法,由于可以相互归约,相当于解决了所有的NP问题,这个问题我也不懂,就不多说了。 zkSNARKs是基于NP问题的,因为如果是一个P问题的话,那么直接求解就行了,证明就无从谈起,所以zkSNARKs的依托于某种NP问题,很难求解但是可以快速验证。 目前zkSNARKs使用最广泛的NP完全问题是QAP(Quadratic Arithmetic Program),我们可以很快的去验证解,而很难用有限资源去求解。 简单解释QAP即,给一系列的多项式以及一个目标多项式,是否能根据这一系列的多项式,求得它们的一个线性组合,刚好可以整除多项式。即如下描述: - 给定一系列多项式$\{l_i(x),r_i(x),o_i(x) \}_{i=0}^n$,目标t(x) - 求一个线性组合 $\{a_i\}_{i=0}^n$ ,使得 $\sum_{i=0}^n a_i\times l_i(x) \cdot \sum_{i=0}^n a_i\times r_i(x)- \sum_{i=0}^n a_i\times o_i(x))=t(x)h(x)$ ,可记为 $L(X)\cdot R(x) - O(x) = t(x)h(x)$ 。 这个问题如果给出了一个解$\{a_i\}_{i=0}^n$,那么验证很简单,直接除t(x)即可验证是否满足,但是想求解就很难。 zkSNARKs的思路就是,将原问题的解转化为一个QAP问题的解,然后公开QAP问题,这样的话拥有解的人用证明公开自己拥有一个解,其他人可以简单验证证明是否成立。 当然,上面的描述过于笼统,而且也没有说明zkSNARKs到底是什么东西,也没有说明如何将一个问题转化成QAP问题。 ## 3. zkSNARKs概述 首先简单说一说zkSNARKs,它是zero-knowledge succinct non-interactive arguments of knowledge,分开来将: - zero-knowledge:表示零知识,证明某个陈述为真但不揭露任何其他信息。 - succinct代表简洁:表示证明大小很短且易于验证 - no-interactive:非交互,上面说的schnorr协议需要verifier生成一个随机数,故而是交互证明,而非交互,则不需要这样的角色,prover可以自行生成证明,给所有人验证。 - arguments:注意这里没有用proofs这个词,proof(证明)和argument(论证)在零知识证明里,是有区别的,在proof system中,即便是计算能力无限的prover也没法欺骗verifier去相信一个错误的陈述为真(统计可靠性),而在argument system中,只有多项式时间计算能力的没法欺骗(计算可靠性),比如说假设能破解椭圆曲线离散对数问题,那么可能就不再安全。 - of Knowledge:如果prover没有相应的witness(证据),那么构建相应的proof(对于proof system)或argument(对于argument system)是不可能的。 简洁之类的后面再讲,但有一个问题先说下,上面举的例子schnorr是交互型协议,其基础来源于prover无法事先预知verifier给的随机数,但是如果不能交互的话,没有第三方提供随机数,怎么构造证明?怎么构造模拟器? 对于schnorr,我们其实可以在prover生成随机数r时,用哈希函数c = hash(pk,r),来生成这个c,然后后面的过程与原本的类似,就可以不用交互了,但是这个依赖于hash函数的安全性,我们需要将其认定为是一个随机预言机( 随机预言机定义:就是它对任何输入都回传一个真正均匀随机的输出,但对相同的输入每次都会回传一模一样的输出,相当于一个黑盒。事实上哈希函数不是随机预言机 ),可以看到,其实这样schnorr零知识证明就变成一个数字签名协议了,就是schnorr签名协议。 这个过程叫Fiat-Shamir Transform,即用随机预言机解决这个随机的问题,其他有一些零知识证明方案就采用了这个思路去掉了可信setup过程。 而对于zkSNARKs,则采取了可信Setup的方法,在初始的时候就把这个随机值先生成好了,再把这个值隐藏起来不让大家知道,生成人也要保证永不泄露,具体后面再讲。 ## 4. 问题转化 接下来我来先讲解如何将一个具体的问题转换为一个zkSNARKs能处理的问题(即QAP形式),然后再将如何在QAP上构建zkSNARKs。 比如说,我想要验证prover是否执行了下面的代码,但prover在证明过程中不想泄露自己的输入值,应该怎么做呢? ```go func calc(w bool, a, b int) int { if w { return 3*a*b } else { return 5*a+b } } ``` 一段代码逻辑,我们怎么把一段具体的代码逻辑转换为一个zkSNARKs可以处理的东西呢?上面说了zkSNARKS依赖某种NP问题,最常用的是QAP也就以QAP举例。讲怎么把代码逻辑转换成QAP。 第一步是将代码逻辑写成多项式,上述代码其实就是两个等式,其中v是返回值。 $w \cdot (3a\cdot b) + (1-w)\cdot(5a+b) = v,\space w \cdot (w-1)=0$ 我们需要将这两个多项式转换成一系列多项式$\{l_i(x),r_i(x),o_i(x)\}_{i=0}^n$,并且给出目标多项式t(x),最后代入解应该是应该是$L(X)\cdot R(x) - O(x) = t(x)h(x)$的形式。 我先用一种简单的方法计算一遍方便大家理解内在逻辑,再讲另一种标准做法。 简单做法: 先对上面多项式的形式进行一些改变,转换为近似A op B = C的形式。, $w \cdot (3a\cdot b) + (1-w)\cdot(5a+b) = v$ -> $w \cdot (3a\cdot b) +5a+b -w(5a+b)=v$ ->$w \cdot (3a\cdot b - 5a-b) +5a+b=v$ ->$1:\space w \cdot (3a\cdot b - 5a - b ) = v - 5a -b \space$ $w\cdot (w-1)=0$ ->$2:\space w\cdot w = w \space$ 如上,将两个式子都转换成了$a\times b = c$的形式了,但式(1)在乘法内部也出现了乘法,我们可以再取一个中间值,得: $1:\space 1 \cdot a \times 1\cdot b = 1\cdot m\space$ $2:\space 1 \cdot w \times (3\cdot m + - 5\cdot a + - 1\cdot b ) = 1\cdot v + -5\cdot a + -1 \cdot b \space$ $3: \space 1 \cdot w \times 1 \cdot w = 1 \cdot w$ 现在我们拥有了三个多项式,由上我们可以看到,$\{l_i(x)\}_{i=0}^n$只与a,w有关,$\{r_i(x)\}_{i=0}^n$与m, a, b, w有关,$\{o_i(x)\}_{i=0}^n$与m, v, a, b, w有关 现在我们把多项式里变量的系数当成另一个多项式的函数值,则有三个多项式,为了方便,就假设这些系数是取点1, 2, 3时的函数值。 相当于: $1:\space l_a(1) \cdot a \times r_b(1)\cdot b =o_m(1) \cdot m\space$ $2:\space l_w(2) \cdot w \times (r_m(2)\cdot m +r_a(2)\cdot a + r_b(2)\cdot b ) = o_v(2)\cdot v + o_a(2)\cdot a + o_b(2) \cdot b \space$ $3: \space l_w(3) \cdot w \times r_w(3) \cdot w = o_w(3) \cdot w$ 可得: $\{l_i(x) \}_{i=0}^n$: $l_a(1)=1,l_a(2)=0,l_a(3)=0$(因为左操作数里,a只在第一个式子出现,下面与其类似) $l_w(1)=0,l_w(2)=1,l_w(3)=1$ $\{r_i(x) \}_{i=0}^n$: $r_m(1)=0,r_m(2)=3,r_m(3)=0$ $r_a(1)=0,r_a(2)=-5,r_a(3)=0$ $r_b(1)=1,r_m(2)=-1,r_b(3)=0$ $r_w(1)=0,r_w(2)=0,r_w(3)=1$ $\{o_i(x) \}_{i=0}^n$: $o_m(1)=1,o_m(2)=0,o_m(3)=0$ $o_a(1)=0,o_a(2)=-5,o_a(3)=0$ $o_b(1)=0,o_b(2)=-1,o_m(3)=0$ $o_w(1)=0,o_w(2)=0,o_m(3)=1$ $o_v(1)=0,o_v(2)=1,o_v(3)=0$ 通过上面的定点,我们就分别把三个式子中参数的系数,变成了函数值,然后我们可以用拉格朗日插值法(不懂的自行百度),将上面的描点拟合成曲线,最后可以计算得。 $\{l_i(x)\}_{i=0}^n$: $l_a(x)=\frac{1}{2}x^2-\frac{5}{2}x+3$ $l_w(x)=-\frac{1}{2}x^2+\frac{5}{2}x-2$ $\{r_i(x)\}_{i=0}^n$: $r_m(x)=-3x^2+12x-9$ $r_a(x)=5x^2-20x+15$ $r_b(x)=\frac{3}{2}x^2-\frac{13}{2}x+6$ $r_w(x)=\frac{1}{2}x^2-\frac{3}{2}x+1$ $\{o_i(x)\}_{i=0}^n$: $o_m(x)=\frac{1}{2}x^2-\frac{5}{2}x+3$ $o_a(x)=5x^2-20x+15$ $o_b(x)=x^2-4x+3$ $o_w(x)=\frac{1}{2}x^2-\frac{3}{2}x+1$ $o_v(x)=-x^2+4x-3$ 由此,我们就求得了一系列多项式$\{l_i(x),r_i(x),o_i(x)\}_{i=0}^n$,而t(x)=(x-1)(x-2)(x-3),这里的逻辑可能有点费解,要理清楚。 细讲一下,我们在上面用系数当成点1,2,3处的函数值得出了一系列多项式,现在我们把正确的m, v, a, b, w当做系数给这些多项式做排列组合后得到$P(x) = L(x)\cdot R(x) - O(x)$,代入 1 相当于就退化为$P(1) = 1 \cdot a \times 1\cdot b - 1\cdot m = 0$,代入2,3也类似,所以组合成的多项式P(x)一定有根1, 2, 3,自然有因式t(x)=(x-1)(x-2)(x-3)。 我们是从系数中得到多项式,而原来多项式中变量的值当做系数,知道一个正确的系数,就相当于执行了下面这个函数逻辑。 假设输入$w=1,a=2,b=3$,则可以求出$m=6, v=18$,用这个参数排列组合上述多项式一定满足条件。 ```go func calc(w bool, a, b int) int { if w { return 3*a*b } else { return 5*a+b } } ``` 不相信可以用上面的值算一下: $L(x)=2l_a(x)+l_w(x)=2\cdot(\frac{1}{2}x^2-\frac{5}{2}x+3)-\frac{1}{2}x^2+\frac{5}{2}x-2 = \frac{1}{2}x^2-\frac{5}{2}x+4$ $R(x)=6r_m(x)+2r_a(x)+3r_b(x)+r_w(x)=-3x^2+11x-5$ $O(x)=6o_m(x)+2o_a(x)+3o_b(x)+o_w(x)+18o_v(x)=-\frac{3}{2}x^2+\frac{7}{2}x+4$ 再计算$P(x)=L(x)R(x)-O(x)=-\frac{3}{2}x^4+13x^3-\frac{81}{2}x^2+53x-24$ 接下来验证P(x)能否整除t(x)=(x-1)(x-2)(x-3),$h(x)=\frac{P(x)}{(x-1)(x-2)(x-3)}=-\frac{3}{2}x+4$,可以看到,代入正确的witness确实是可以得到一个满足的多项式的。 此时我们完成了zk-SNARKs的第一步,即将验证代码的执行转成了验证多项式是否成立。 上面讲了一个简单版的怎么将问题转化为QAP,下面讲一个更通用的方法,但其实原理和上面的过程是一样的,所以下面的过程有什么地方理解不了,看看上面的简化版就行了,只不过下面拆解得更基础,电路更多了而已。 用constraint system来做,一般举例都用的R1CS(rank 1 constraint system),R1CS是由一系列三向量组(x,y,z)组成的序列,R1CS有一个解向量s,需要满足$s \cdot \mathbf{x}\times s\cdot \mathbf{y}-s \cdot \mathbf{z} = 0$。 所谓constraint system就是要限制,比如说w(w-1)=0,就限制了w只能为1或0,(w-2)*1=0,就限制了w只能为2。 我们首先需要将多项式转成算术电路,因为电路的可满足性是一个NP问题,我们可以等会再将这个NP问题规约成QAP问题,还是以上面的多项式为例。 - From 多项式 to R1CS - 算术电路拍平 首先将多项式的所有操作转化为 $x\space (op) \space y = z$ 的形式,op可以是+,-,*,/ 等。 $1:w \cdot (3a\cdot b - 5a-b) +5a+b=v$ $2:\space w\cdot w = w \space$ 那么转化为$x\space (op) \space y = z$ 以后,可以得到下面这个电路: ``` sym_1 = a * b sym_2 = 3 * sym_1 sym_3 = 5 * a sym_4 = sym_2 - sym_3 sym_5 = sym_4 - b sym_6 = w * sym_5 sym_7 = sym_3 + b v = sym_6 + sym_7 w = w * w ``` - 可以看出,为了将所有操作都转成 $x\space (op) \space y = z$ 的形式,加入了sym_1...sym_7等中间变量。 上面的形式即算数电路形式,向电路输入参数得到结果,即将代码逻辑拆成了电路的计算,计算完以后就会得到一组满足电路的值,至于为什么叫电路,我就不画图了,引一张图,这是$x^3+x+5=out$的算术电路形式,上面的电路化成图自己脑补。 - ![](C:\Users\xcshuan\OneDrive\业余社区\r1cs.png) - 电路转成R1CS 现在我们把所有变量包括中间变量组成一组向量,`s = [one,a,b,w,v,sym_1,sym_2,sym_3,sym_4,sym_5,sym_6,sym_7]^T`,s即为R1CS中的解向量,其中one表示数字1。 现在我们对上面的式子进行转换,这部分应该很好理解,下面向量里的值作用相当于简单版里的「系数」,原理和简单版差不多,除了式子更多些而已。 a * b = sym_1 ``` x = [0,1,0,0,0,0,0,0,0,0,0,0] y = [0,0,1,0,0,0,0,0,0,0,0,0] z = [0,0,0,0,0,1,0,0,0,0,0,0] ``` - 可知对于上面的x,y,x和s,向量式$s \cdot x\times s\cdot y-s \cdot z = 0$是满足的。 3 * sym_1 = sym_2 ``` x = [3,0,0,0,0,0,0,0,0,0,0,0] y = [0,0,0,0,0,1,0,0,0,0,0,0] z = [0,0,0,0,0,0,1,0,0,0,0,0] ``` - 5 * a = sym_3 ``` x = [5,0,0,0,0,0,0,0,0,0,0,0] y = [0,1,0,0,0,0,0,0,0,0,0,0] z = [0,0,0,0,0,0,0,1,0,0,0,0] ``` - (sym_2 - sym_3) * 1 = sym_4 ``` x = [0,0,0,0,0,0,0,1,-1,0,0,0] y = [1,0,0,0,0,0,0,0,0,0,0,0] z = [0,0,0,0,0,0,0,0,1,0,0,0] ``` - (sym_4 - b) * 1 = sym_5 ``` x = [0,0,-1,0,0,0,0,0,1,0,0,0] y = [1,0,0,0,0,0,0,0,0,0,0,0] z = [0,0,0,0,0,0,0,0,0,1,0,0] ``` - w * sym_5 = sym_6 ``` x = [0,0,0,1,0,0,0,0,0,0,0,0] y = [0,0,0,0,0,0,0,0,0,1,0,0] z = [0,0,0,0,0,0,0,0,0,0,1,0] ``` - (sym_3 + b) * 1 = sym_7 ``` x = [0,0,1,0,0,0,0,1,0,0,0,0] y = [1,0,0,0,0,0,0,0,0,0,0,0] z = [0,0,0,0,0,0,0,0,0,0,0,1] ``` - (sym_6 + sym_7) * 1 = v ``` x = [0,0,0,0,0,0,0,0,0,0,1,1] y = [1,0,0,0,0,0,0,0,0,0,0,0] z = [0,0,0,0,1,0,1,0,0,0,0,0] ``` - w * w = w ``` x = [0,0,0,1,0,0,0,0,0,0,0,0] y = [0,0,0,1,0,0,0,0,0,0,0,0] z = [0,0,0,1,0,0,0,0,0,0,0,0] ``` - From R1CS to QAP 我们就得到了一组$\mathbf{x,y,z}$向量,$\mathbf{x,y,z}$各自组合可以得到一个矩阵,每一行均满足$s \cdot \mathbf{x}\times s\cdot \mathbf{y}-s \cdot \mathbf{z} = 0$。 我们可以对每一列进行根简单版本一样的处理,即使用拉格朗日插值法算出多项式。 比如可以算出代入x=1,2,3,4,5,6,7,8,9,用x,y,z的每一列的值当做函数值,然后用拉格朗日插值法,即可得多项式系列$\{l_i(x),r_i(x),o_i(x)\}_{i=0}^n$,基本思路和简单方案一致,只不过这个方案中多项式的阶数更高, t(x) = (x-1)(x-2)(x-3)(x-4)(x-5)(x-6)(x-7)(x-8)(x-9)。 举个例子,可以计算$l_1(x)$如下: $l_1(x)=\frac{29}{10080}x^8-\frac{101}{840}x^7+\frac{127}{60}x^6-\frac{4889}{240}x^5+\frac{18597}{160}x^4-\frac{95489}{240}x^3+\frac{494329}{630}x^2-{83647}{105}x+312$ 其他的一样求出,后续思路与简单版一样,代入正确的s可以得到一个能整除t(x)的P(x)。 当一个prover知道一组正确的解向量s时,即可通过验证,跟简单形式的理解是一样的。 ## 5. 细节详解 上面花了很大篇幅讲述如何将一个代码逻辑问题转换为一个zkSNARKs可以处理的QAP表达,现在来讲讲,针对一个QAP问题,怎么做zkSNARKs的证明过程呢? 让我们慢慢去思考这个问题,首先,我们有一系列的多项式$\{l_i(x),r_i(x),o_i(x)\}_{i=0}^n$,以及一个t(x),在非零知识的过程中我们只需要让prover传回一个解即可,但如果要做成零知识呢?我们一步一步引入密码工具完成zk-SNARKs。 我们首先明确我们的目的,对于一个QAP问题,witness就是$\{a_i\}_{i=0}^n$,我们想达到的是,prover知道这么一组满足给定QAP问题的系数witness,是prover能通过验证的充要条件,但是witness的信息不会泄露。 我们先思考需要交互的情况: 1)此时verifier随机选取一个数s,发送给prover。 2)prover根据$\{l_i(x),r_i(x),o_i(x)\}_{i=0}^n$,代入自己的系数计算出L(x),R(x),O(x),并令P(x) = L(x)R(x)-O(x),求得P(x)/t(x) = h(x),发送L(s),R(s),O(s)与h(s)给verifier。 3. verifier计算$L(s)R(s)=h(s)\cdot t(s)+O(s)$是否成立。 但是很明显,上述协议根本不是一个证明协议,证明本身是可以任意伪造的,因为我们只通过一次检验,没法保证prover如实计算了P(x)以及h(x),这个不能用来证明的协议放在这里这里仅作一个引子。 另外,最开始我们不考虑通用化的问题,所以要证明的statement就体现在构造出来的QAP问题中,后面会讲通用化。 接下来要引入密码学工具一步一步限制prover达到目的,**准备纸笔开始推公式吧!** **圈一个重点,免得大家迷失方向,下面做的所有工作都是为了这三件事**: 1. 验证证明是用给定CRS公共参考串里面的值线性组合而来。 1. 验证证明中L R O三者的系数是一致的。 1. 验证给出的证明是满足QAP的解。 ### 5.1 同态隐藏 首先我们要用上同态隐藏,假设有一个映射E(),映射前的某些操作可以在映射后体现,就说明映射具有同态性。 定义: (1)对于一个给定的x,知道E(x),很难逆推得到x。 (2)对于不同的x和y,$E(x) \neq E(y)$。 (3)对于某些操作,映射后依然存在,比如E(x+y)可以从E(x)和E(y)中计算出来。 一个简单的例子是椭圆曲线域上的幂运算,$E(x) = g^x$,g为生成元,根据椭圆曲线离散对数问题,知道E(x)和g并不能反推回x。 $E(x+y) = g^{(x+y)} = g^x\space \times g^y\space =E(x)E(y)$ 在未引入双线性映射的时候,图方便用E(s)来写,引入后都用幂形式$g^s$写,两者在本文意义相同。 举个例子,verifier可以选择一个随机数s,然后发送$\{g^{\{s^i\}}\}_{i=0}^n = g^s, g^{s^1}, g^{s^2}...g^{s^n}$给prover,verifier知道一个多项式$f(x) = 3x^2+2$,想验证prover知不知道此多项式,可将$\{g^{\{s^i\}}\}_{i=0}^n$发送给prover,prover计算$g^{f(s)} =g^{3s^2+2}= {(g^{s^2})}^3\cdot (g)^2$,将$g^{f(s)}$发回,verifier既可验证,计算过程prover无法知道具体的s值为多少。 概念上科普下同态隐藏——上面的计算好像是对一堆黑盒子做操作,虽然不知道黑盒子里面是什么(隐藏性质),但操作完又能得到想要的值(同态性质)。在zkSNARKs里我们不需要全同态操作,全同态操作目前尚不实用。 根据同态隐藏的性质,我们修改下上面的基础协议看看能不能行: 1)此时verifier随机选取一个数s,计算$\{ E(s^i) \}_{i=0}^d$(d为h的阶,可以算出来)以及$\{E(l_i(s)),E(r_i(s)),E(o_i(s))\}_{i=0}^n$,发送给prover。 2)prover根据$\{l_i(x),r_i(x),o_i(x)\}_{i=0}^n$,代入自己的系数计算出L(x),R(x),O(x),并令P(x) = L(x)R(x)-O(x),求得P(x)/t(x) = h(x)。用$\{E(l_i(s)),E(r_i(s)),E(o_i(s))\}_{i=0}^n$计算出E(P(x)),用$\{ E(s^i) \}_{i=0}^d$计算出E(h(s)),并将计算结果发送给verifier。 3)verifier计算$E(P(s))=E(h(s)\cdot t(s))$是否成立。 虽然prover现在可以用参数计算结果了,但仔细揣摩会发现这个协议依然是无效的,因为没法保证prover一定是用$\{E(l_i(s)),E(r_i(s)),E(o_i(s))\}_{i=0}^n$的排列组合计算出来的值,比如Verifier发送$E(l_i(5))$,prover也可以用随便选个E(3)进行计算。verifier无法识别这种情况,看来我们需要引出另一个工具来解决这个问题了。 ### 5.2 KCA(Knowledge of Coefficient Test and Assumption) 假如Verifier对$\{E(s^i)\}_{i=0}^n$,每一个值再计算相应的$\{E(\alpha\cdot s^i)\}_{i=0}^n$,而$\alpha$对于prover是未知的,因为离散对数难题,prover也无法从中提取出$\alpha$,那么我们就能限制prover只能用我发送的值进行计算了。 比如Verifier发送$E(5), E(5^1), E(5^2)...E(5^n),E(\alpha5), E(\alpha5^1), E(\alpha5^2)...E(\alpha5^n)$,那么prover就只能被迫用5这个值进行计算$E(f(5))$,以及$E(\alpha f(5))$,最终验证prover返回的两个值,$E(f(5))^\alpha ?= E(\alpha f(5))$即可保证prover一定是诚实的按照$E(5^i)$的排列组合计算出来的,如果prover想用$E(4)$进行计算,可是他提供不了$E(\alpha 4)$就通过不了这一步验证了。 #### 5.2.1 串用问题 但是上面的解决方案可能导致串用问题,因为所有的$\alpha$都一样,prover可能用$l_i(x)$串在R(x)的计算中,那么我们给$\{l_i(x),r_i(x),o_i(x)\}_{i=0}^n$三个不同的 $\{ \alpha_l,\alpha_r,\alpha_o \}$,所以verifier需要事先计算$\{E(l_i(s)),E(r_i(s)),E(o_i(s))\}_{i=0}^n$以及对应的$\{E(\alpha_l\cdot l_i(x)),E(\alpha_r\cdot r_i(x)),E(\alpha_o\cdot o_i(x))\}_{i=0}^n$,即可保证都是根据各自的隐藏值算的。 这样的话,prover就可以直接通过$\{E(l_i(s)),E(r_i(s)),E(o_i(s))\}_{i=0}^n$计算$E(L(s)),E(R(s)),E(O(s))$,通过$\{E(s^i)\}_{i=0}^d$计算$E(h(s))$,并用$\{E(\alpha_l\cdot l_i(x)),E(\alpha_r\cdot r_i(x)),E(\alpha_o\cdot o_i(x))\}_{i=0}^n$计算得$L_{\alpha_l} =E(\alpha_l\cdot L(x)),R_{\alpha_r} =E(\alpha_r\cdot R(x)),O_{\alpha_o} =E(\alpha_o\cdot O(x))$,并将结果全部发送给verifier。 verifier在收到以后,验证$E(L(s))^{\alpha_l}=E(L_{\alpha_l})$,$E(R(s))^{\alpha_r}=E(R_{\alpha_r})$,$E(O(s))^{\alpha_o}=E(O_{\alpha_o})$几个等式是否成立,再验证$E(L(s)\cdot R(s))=E(h(s)\cdot t(s)+O(s))$是否成立。(**注意,此时方案已经不成立,因为在验证中出现了乘法,但是verifier无法得到隐藏值的乘法值,这个问题在下面的双线性映射部分会得到解决,因为双线性映射具备乘法同态,这一步放这里起一个递进的作用**) 等于多加了一步验证解决了**"证明是用给定参考串里面的值线性组合计算而来。"** #### 5.2.2 系数一致性问题 但是又有新的问题,我们知道QAP问题中,$\{l_i(x),r_i(x),o_i(x)\}_{i=0}^n$,三个多项式分别排列组合后得到L(x), R(x),O(x),他们的系数序列$\{a_i\}_{i=0}^n$应该是相同的。 但仅仅通过不同的$\alpha$并不能保证prover一定会用相同的系数序列计算,我们可以用不同的几组系数a1, a2, a3计算,也可能能通过验证。我们又需要新的思路,经过上面一步的经验你们应该已经清楚了,没错,我们再加一步验证。 此时我们搬出检验和思路,可以通过检验和来解决这个问题,verifier除了需要事先计算$\{E(s^i)\}_{i=0}^d$,$\{E(l_i(s)),E(r_i(s)),E(o_i(s))\}_{i=0}^n$,$\{E(\alpha_l\cdot l_i(x)),E(\alpha_r\cdot r_i(x)),E(\alpha_o\cdot o_i(x))\}_{i=0}^n$之外,还需要计算一个$\{E(\beta(l_i(s)),E(\beta(r_i(s)),E(\beta(o_i(s)))\}_{i=0}^n$,相当于对每一个$\{l_i(s),r_i(s),o_i(s)\}$三元组,都计算一个检验和,最后加起来,需要系数序列相等才可能满足$\beta$的系数约束。 基于这个新的序列,prover在生成证明的时候还需要多计算一个结果,$E(z_i(s))=E(\beta\cdot (l_i(s)+r_i(s)+o_i(s)))^{a_i}$,这个$a_i$就是针对每一组$\{l_i(x),r_i(x),o_i(x)\}$解中相同的系数,得$Z = E(Z(s))=\prod_{i=0}^n E(z_i(s))$发送给verifier。 verifier再多一步验证$E(Z(s)) = E(L(s)+R(s)+O(s))^\beta$,即可验证线性组合是否相同。 但是如果只用一个$\beta$,其实prover还是有操作空间的,比如当l(s)=r(s)的时候,有检验和$\beta(v_L\cdot l(s)+v_R\cdot r(s)+v_O\cdot o(s))$,按理说需要$v_L=v_R=v_O$,才能提取出一个公因子$v\cdot \beta$,变成统一的$v\cdot \beta(l(s)+r(s)+o(s))$。 但由于l(s)=r(s),则可以进行如下操作: $\beta((2v_O-v_R)\cdot l(s)+v_R\cdot r(s)+v_O\cdot o(s))$ $=\beta(2v_O\cdot l(s)-v_R\cdot l(s)+v_R\cdot r(s)+v_O\cdot o(s))$ $=\beta(2v_O\cdot l(s)+v_O\cdot o(s))=v_O\beta(2l(s)+o(s))$. 也得到了满足提取公因子的形式,可以通过验证,但此时系数并不一样。为了解决这个问题,我们可以对不同的$\{l_i(x),r_i(x),o_i(x)\}{i=0}^n$选择不同的$\beta$,即分别生成${E(\beta_l\cdot l_i(s)),E(\beta_r\cdot r_i(s)),E(\beta_o\cdot o_i(s)}{i=0}^n$,可避免这种情况,此时prover计算$E(z_i(s))=E(\beta_l\cdot l_i(s)+\beta_r\cdot r_i(s)+\beta_o\cdot o_i(s))^{a_i}$,$Z = E(Z(s))=\prod_{i=0}^n E(z_i(s))$替代上面得操作,verifier验证$E(Z(s)) = E(\beta_lL(s)+\beta_rR(s)+\beta_oO(s))$是否成立。 总之这一步我们的目的就是确保prover计算的时候,三个系数序列一定要是相等的。 ### 5.3. 变为非交互 上面说了一堆,其实还是针对交互型协议的,每一次verifier每次都需要计算特别多的数字序列,如$\{E(s^i)\}_{i=0}^d$,$\{E(\alpha\cdot s^i)\}_{i=0}^n$,$\{E(l_i(s)),E(r_i(s)),E(o_i(s))\}_{i=0}^n$,$\{E(\alpha_l\cdot l_i(x)),E(\alpha_r\cdot r_i(x)),E(\alpha_o\cdot o_i(x))\}_{i=0}^n$,$\{E(\beta_l\cdot l_i(s)+\beta_r\cdot r_i(s)+\beta_o\cdot o_i(s)\}_{i=0}^n$。 现实情况中,多项式的阶数都非常高(即算数电路特别多),一般都几百万甚至上亿,每一次参数生成需要几小时乃至几天,对于verifier太耗时了,但如果$s,\alpha,\beta,\gamma$等值没有泄露的话,其实所有的prover可以用相同的参数生成证明,这个就叫公共参考串(CRS)。 但如果用公共参考串这里就引出两个问题: - 如何保证生成公共参考串的人不泄露这几个隐私参数? - 在verifier验证的时候,很多地方都直接用上了$s,\alpha,\beta,\gamma$这几个参数才可以验证,但是这几个参数在非交互情况下又必须隐藏,怎么保证还可以验证呢? #### 5.3.1 安全多方计算 关于上面两个问题,第一个问题是通过安全多方计算解决的,就是会有很多人参与生成公共参考串,每个人贡献出自己的随机数,最后通过某些手段拼合,由于没人知道所有的随机值,那么只要还剩一个人没有作恶,协议就是安全的。 举个例子,计算$\{E(s^i)\}_{i=0}^n$,$\{E(\alpha\cdot s^i)\}_{i=0}^n$; 让a先计算$\{E(s_a^i)\}_{i=0}^n$,$\{E(\alpha\cdot_a s_a^i)\}_{i=0}^n$,交给b; b在此基础上计算,$\{E(s_a^i)^{s_b^i}\}_{i=0}^n$,$\{E(\alpha_a\cdot s_a^i) ^ {\alpha_b\cdot s_b^i}\}_{i=0}^n$,得到$\{E(s_as_b)^i\}_{i=0}^n$,$\{E(\alpha_a\alpha_b \cdot (s_as_b)^i )\}_{i=0}^n$; 以此类推,可以扩展到计算其他参数以及更多人参与,细节就不赘述了,这里能理解即可。 #### 5.3.2 双线性映射 第二个问题是通过双线性映射(椭圆曲线配对,Pairing)解决的,双线性映射是具有如下性质的映射函数: 双线性映射可以用五元组$(p,G_1,G_2,G_T,e,g_1,g_2)$来描述,G1,G2,GT是三个素数阶乘法循环群,阶数皆为p,定义在这三个群上的一个映射关系e:$G1\times G2 —>GT$,$g_1$是$G_1$的生成元,$g_2$是$G_2$的生成元,$e(g_1,g_2)$是$G_T$的生成元$g_t$,若$G_1=G_2$则称为对称双线性映射,否则为非对称双线性映射。下面举例子为了方便,未特定指明都用对称双线性映射计算,因为非对称还需注明哪些元素属于G1,哪些属于G2,不过要清楚,两种双线性映射都可以用于zk-SNARKs,且由于非对称效率更高,实际中一般用的都是非对称,比如基于BLS12-381曲线构造的Pairing。 并满足以下性质: - 双线性性:对于任意$a,b, c\in Zp$,有$e(g_1^a, g_2^b) = e(g_1, g_2)^{ab}$,$e(g_1^a\cdot g_1^b,g_2^c)=e(g_1^a,g_2^c)e(g_1^b,g_2^c)$; - 非退化性:存在$a\in G_1,b\in G_2$,使得$e(a,b)\neq(1)G_t$( $(1)G_t$ 代表GT群的单位元); - 可计算性:对任意的$R\in G1,S\in G2$,存在有效的算法计算e(R, S)的值。 具体关于这样的双线性配对如何构造就超出本文谈论范围了。 那么如何利用双线性配对在不知道$s,\alpha,\beta,\gamma$这几个参数的时候,进行验证呢? 举个例子,在上面验证$\alpha$对的验证中,本来需要verifier知道$\alpha$的值,验证$E(L(s))^{\alpha_l}=E(L_{\alpha_l})$,现在可以改为验证$e(g^{L(s)},g^{\alpha_l})=e(g^{L_{\alpha_l}},g)$是否成立即可,这样的话,就可以只公开$g^{\alpha_l}$而不是用私密参数$\alpha_l$本身做验证了。 #### 5.3.3 malleable问题 但这样的话又会导致新的问题,即证明得可操作问题-malleable问题,因为prover此时可以获取$g^{\alpha_l},g^{\alpha_r},g^{\alpha_o},g^{\beta_l},g^{\beta_r},g^{\beta_o}$。这样的话,prover甚至非prover都可以对证明做一些小操作了。 比如对于证明中的$g^{L(x)}$,我们给它加一个数5,$g^{L(s)+5}$,其他的不做任何改动,由于我们知道$g^{\alpha_l},g^{\beta_l}$,则可以算出$g^{\alpha_lL(s)}*({g^{\alpha_l}})^5=g^{\alpha_l(L(s)+5)}$,$g^{\beta_lL(s)}*({g^{\beta_l}})^5=g^{\beta_l(L(s)+5)}$。 这样的话,也可以通过验证$e(g^{L(s)+5},g^{\alpha_l})=e(g^{\alpha_l(L(s)+5)},g)$,以及$e(g^{L(s)+5},g^{\beta_l})\cdot e(g^{R(x)},g^{\beta_r})\cdot e(g^{O(x)},g^{\beta_o})=e(g^{Z(s)}\cdot ({g^{\beta_l}})^5,g)$,其中$g^z=g^{\beta L(s)}\cdot g^{\beta R(s)}\cdot g^{\beta O(s)}$,这个计算上面已经讲过了,也就是说,我可能很容易用一个证明伪造出另一个可以通过的证明。 那么为了避免这种可操作性,需要新加一层限制,这个改进可以有多种思路。 我们首先可以把$\beta$验证变得更严格,即把在生成证明的proving key中将$\{g^{(\beta_l\cdot l_i(s)+\beta_r\cdot r_i(s)+\beta_o\cdot o_i(s)}\}_{i=0}^n$改为$\{g^{\gamma(\beta_l\cdot l_i(s)+\beta_r\cdot r_i(s)+\beta_o\cdot o_i(s)}\}_{i=0}^n$,这样的话,在trust Setup时就需要多一个$\gamma$,与之相应的,用于验证的verification key中就需要加入$\{g^{\beta_l\gamma},g^{\beta_r\gamma},g^{\beta_o\gamma},g^{\gamma}\}$。 验证这一步时变为,$e(g^{L(s)},g^{\beta_l\gamma})\cdot e(g^{R(x)},g^{\beta_r\gamma})\cdot e(g^{O(x)},g^{\beta_o\gamma})=e(g^Z,g^\gamma)$,由于无法求出$\gamma$,也就无法进行操作了。 不过上面的解决方案有一个问题就是,这一步验证需要四次Pairing双线性计算,而双线性计算是很耗时的,那我们能不能从$\alpha$验证入手来解决这个问题呢?也是可以的,针对$L,R,O$选取随机$\rho_l,\rho_r$,并令$\rho_o=\rho_l\cdot\rho_r$,此时给他们分配不同的生成元$g_l=g^{\rho_l},g_r=g^{\rho_r},g_r=g^{\rho_o}$。 这样的话,proving key变为$(\{g^{s^i}\}_{i=0}^d,\{g_l^{l_i(s)},g_r^{r_i(s)},g_o^{o_i(s)},g_l^{\alpha_ll_i(s)},g_r^{\alpha_rr_i(s)},g_o^{\alpha_oo_i(s)},g_l^{\beta l_i(s)},g_r^{\beta r_i(s)},g_o^{\beta o_i(s)}\}_{i=0}^n )$,然后verification key变为$\{g^{\alpha_l},g^{\alpha_r},g^{\alpha_o},g^{\beta\gamma},g^{\gamma}\}$。 则在做$\alpha$对验证时,变为$e(g_l^{l_{\alpha_l}},g)=e(g_l^{l},g^{\alpha_l})$,其他类似,由于不知道$\rho_l,\rho_r$,那就无法进行操作了,同时$\beta$共用,则验证变为,$e(g_l^{L(s)}\cdot g_r^{R(s)}\cdot g_o^{O(s)},g^{\beta\gamma})=e(g^{Z(s)},g^{\gamma})$,只用两次配对计算。 这样就在非交互的前提下解决了这个问题。 ### 5.4 σ偏移 此外,prover为了保证零知识,可能连$g_l^{L(s)},g_r^{R(s)},g_o^{(O(s))}$这些值都不想透露,因为在解比较少的时候,可以通过暴力法匹配计算出L(s)的值,所以我们可以对每个值都加一点只有prover自己知道的偏移,但同时保证验证可以通过,就能避免被暴力破解。 由于等式最终要满足$L(s)\cdot R(s) - O(s) = t(s)\cdot h(s)$成立,而t(s)又是所有人都知道的,所以我们只能改动$L(s),R(s),O(s)$,观察这个式子可知,我们只能$L(s),R(s),O(s)$上偏移t(s)的倍数,否则没法保证还能整除。 我们可以计算一下偏移的具体数值: $(L(s)+\sigma_lt(s))\cdot (R(s)+\sigma_rt(s))-(O(s)+\sigma_ot(s))=t(s)(\Delta+h(s))$ 现在我们可以将$\Delta$求出来。 $L(s)R(s)-O(s)+t(s)(\delta_rL(s)+\delta_lR(s)+\delta_l\delta_rt(s)-\delta_o)=t(s)\Delta+t(s)h(s)$ 由于$L(s)R(s)-O(s)=t(s)h(s)$ 所以约掉以后得: $\Delta=\delta_rL(s)+\delta_lR(s)+\delta_l\delta_rt(s)-\delta_o$ 这个式子放到证明过程中即为, $g_l^{L_p(s)}=(g_l^{t(s)})^{\sigma_l} \cdot g_l^{L(s)}$,$g_l^{L_{\alpha p}(s)}=(g_l^{\alpha_l t(s)})^{\sigma_l} \cdot g_l^{\alpha_l L(s)}$(L(s)的计算和上面差不多,然后$g_r^{R_p(s)},g_o^{O_p(s)}$用同样的方法求得。 同时计算$h_{\sigma}(x)=\frac{L(x)R(x)-O(x)}{t(x)}+\delta_rL(x)+\delta_lR(x)+\delta_l\delta_rt(x)-\delta_o$,并计算出$g^{h(s)}$。 $g^{z_p(s)}=(g_l^{\beta t(s)})^{\sigma_l}(g_r^{\beta t(s)})^{\sigma_r}(g_o^{\beta t(s)})^{\sigma_o}g_l^{\beta L(s)}\cdot g_r^{\beta R(s)}\cdot g_o^{\beta O(s)}$ 然后也是可以通过验证的,不过通过上面我们也可以发现要在proving key中加入$\{g_l^{t(x)},g_r^{t(x)},g_o^{t(x)},g_l^{\alpha_lt(x)},g_r^{\alpha_rt(x)},g_o^{\alpha_ot(x)},g_l^{\beta t(x)},g_r^{\beta t(x)},g_o^{\beta t(x)}\}$供prover所用,才能在偏移后生成证明。 verifier进行下面的验证: $e(g_l{L_p(s)},g{\alpha_l})=e(g_l^{L_{\alpha p}(s)},g)$,对R和O做相同验证。 $e(g_l^{L_p(s)}g_r^{R_p(s)}g_o^{O_p(s)},g^{\beta\gamma})=e(g^Z_p(s),g^\gamma)$ $e(g_l^{L_p(s)},g_r^{R_p(s)})=e(g_o^{t(s)},g^{h(s)})\cdot(g_o^{O_p(s)},g)$ 这几个式子的成立,是可以从前面推出来的,不相信的同学依旧可以动手推导一番,用一点加减乘除以及双线性计算的拆分组合即可。 由于这个偏移的存在,即便想暴力匹配也是不可能的了。 ### 5.5 通用化 上面写了这么多内容,但是却有一个问题,那就是,针对每一个问题,都需要一次参数生成。 proving key:$(\{g^{s^i}\}_{i=0}^d,\{g_l^{l_i(s)},g_r^{r_i(s)},g_o^{o_i(s)},g_l^{\alpha_ll_i(s)},g_r^{\alpha_rr_i(s)},g_o^{\alpha_oo_i(s)},g_l^{\beta l_i(s)},g_r^{\beta r_i(s)},g_o^{\beta o_i(s)}\}_{i=0}^n)$。 verification key:$\{g^{\alpha_l},g^{\alpha_r},g^{\alpha_o},g^{\beta\gamma},g^{\gamma}\}$。 虽然针对这个具体的问题,参数可以一直复用,但换一个问题,似乎又得做一遍重复操作了。 此时,我们希望我们设计的协议是通用的,即我们可以往协议中加入一个公共输入,针对不同的公共输入,就对应不同的问题。 这个该怎么做呢?首先回想一下,最开始我们想证明什么?是给定一系列多项式$\{l_i(x),r_i(x),o_i(x)\}_{i=0}^n$,目标t(x),然后想求一个线性组合$\{a_i\}_{i=0}^n$,使得 $\sum_{i=0}^n a_il_i(x) \cdot \sum_{i=0}^n a_ir_i(x)-\sum_{i=0}^n a_io_i(x)=t(x)h(x)$ 。 那么我们可不可以针多项式,从里面摘出一部分系数做验证,prover再生成证明呢?或者针对同一个多项式,我加入一些限制条件呢?比如说,按上面那个例子,我可以要求给出的证明必须满足,输出值为183,在此前提下给出证明。 即prover生成的证明必须是要和公共输出对应,针对的是同一个问题,这样的话,我每次选择不同多项式的局部,prover都得生成针对性的证明才行,就可以对不同的问题复用参数生成。 现在我有一个特定的$\{l_i(x),r_i(x),o_i(x)\}_{i=0}^n$,对应一个特定的问题,我可以取出前l个用于验证。 即将上面的多项式分为$\{l_i(x),r_i(x),o_i(x)\}_{i=0}^l$和$\{l_i(x),r_i(x),o_i(x)\}_{i=l+1}^n$,只对后面这部分进行KCA处理,而前面一部分$\{l_i(x),r_i(x),o_i(x)\}_{i=0}^l$放入verification key,对应的$\{a_i\}_{i=0}^l$直接当成公共输入(可认为是prover要证明的论述statement)用来限制prover的证明,根据不同的问题,这个公共输入也不一样。 这样prover生成证明时,verifier验证证明时也将公共输入输进去,如果依然成立就能保证prover是对一个特定问题生成证明,相当于把问题本身的一部分当成了公共输入。 下面以数学形式进行推导理解: 结合上面所有的改进,我们对协议进行一个总结: 整个协议过程变为: - Setup: - 选择两个椭圆曲线群$G_1,G_2$以及一个双线性映射$e()$; - 将一个具体的问题转化为QAP问题,即得到一系列多项式$\{l_i(x),r_i(x),o_i(x)\}_{i=0}^n$,目标t(x); - 通过安全多方计算生成参数,注意有$\rho_o=\rho_l\cdot\rho_r,g_l=g^{p_l},g_r=g^{p_r},g_o=g^{p_o}$:proving Key:$(\{g^{s^i}\}_{i=0}^d,\{g_l^{l_i(s)},g_r^{r_i(s)},g_o^{o_i(s)}\}_{i=0}^n,\{g_l^{\alpha_ll_i(s)},g_r^{\alpha_rr_i(s)},g_o^{\alpha_oo_i(s)},g_l^{\beta l_i(s)},g_r^{\beta r_i(s)},g_o^{\beta o_i(s)}\}_{i=l+1}^n,g_l^{t(x)},g_r^{t(x)},g_o^{t(x)},g_l^{\alpha_lt(x)},g_r^{\alpha_rt(x)},g_o^{\alpha_ot(x)},g_l^{\beta t(x)},g_r^{\beta t(x)},g_o^{\beta t(x)})$, 可以看到proving key中只对l+1...m范围内的多项式组进行了KCA。 verification key: $(g,\{g_o^{t(x)},{g_l^{l_i(s)},g_r^{r_i(s)},g_o^{o_i(s)}}\}_{i=0}^l,g^{\alpha_l},g^{\alpha_r},g^{\alpha_o},g^{\gamma},g^{\beta\gamma})$  在verification key中也加入了部分多项式组。 - Proving - 随机选取$\delta_l,\delta_r,\delta_o$,计算$L(x) = \sum_{i=0}^n a_il_i(x)$_,_$g_l^{L_p(s)}=(g_l^{t(s)})^{\sigma_l} \cdot \prod_{i=l+1}^n (g_l^{l_i(s)})^{a_i}$_,_$g_l^{L_{\alpha p}(s)}=(g_l^{\alpha_l t(s)})^{\sigma_l} \cdot \prod_{i=l+1}^n (g_l^{\alpha_ll_i(s)})^{a_i}$,这里只有部分加入了$\alpha$对的计算,并用同样的方法求出$g_r^{R_p(s)},g_r^{R_{\alpha p}(s)},g_o^{O_p(s)},g_o^{O_{\alpha p}(s)}$。 - 计算$h_\delta(x)=\frac{L(x)R(x)-O(x)}{t(x)}+\delta_rL(x)+\delta_lR(x)+\delta_l\delta_rt(x)-\delta_o$_,_并计算$g^{h_\delta(s)}$。 - 计算$g^{z_p(s)}=(g_l^{\beta t(s)})^{\sigma_l}(g_r^{\beta t(s)})^{\sigma_r}(g_o^{\beta t(s)})^{\sigma_o}\prod_{i=l+1}^n (g_l^{\beta l_i(s)}g_r^{\beta r_i(s)}g_o^{\beta o_i(s)})^{a_i}$,这里也只有部分参与KCA计算,最后生成完整证明$\{g_l^{L_p(s)},g_r^{R_p(s)},g_o^{O_p(s)},g_l^{L_{\alpha p}(s)},g_r^{R_{\alpha p}(s)},g_o^{O_{\alpha p}(s)},g^{h_\delta(s)},g^{z_p(s)}\}$。 - 验证 - 加入对公共输入加入特定系数:$g_l^{L_v(s)}=\prod_{i=0}^m(g_l^{l_i(s)})^{a_i}$,用同样的方法生成$g_r^{R_v(s)},g_o^{O_v(s)}$. - 验证$\alpha$对,即验证$e(g_l^{L_p(s)},g^{\alpha_l})=e(g_l^{L_{\alpha p}(s)},g)$,对R和O做相同验证。 - 验证系数一致性,即验证$e(g_l^{L_p(s)}g_r^{R_p(s)}g_o^{O_p(s)},g^{\beta\gamma})=e(g^Z_p(s),g^\gamma)$ - 验证答案是否正确:$e(g_l^{L_p(s)}g_l^{L_v(s)},g_r^{R_p(s)}g_r^{R_v(s)})=e(g_o^{t(s)},g^{h(s)})\cdot(g_o^{O_p(s)}g_o^{O_v(s)},g)$ 在完整的写完协议后,我们可以分析下怎么做到的,首先由于同态性,上面的式子肯定是成立的,并且由于verifier在验证时,已经输入了一部分系数,所以说prover给出的证明必须要在verifier给出系数的前提下才可以通过。 讲个具体实例可能比较好理解,比如说你想设计一个电路,用于验证prover持不持有跟某个比特币地址对应的私钥,如果没有公共输入,你要么给每个地址都生成一次参数,要么就只能证明prover有没有某个私钥对应任意一个比特币地址(这个没有任何意义)。 但是如果加入公共输入,那就可以通用化,我们可以把地址的限制当成公共输入,那么prover的证明就必须得有与公共地址对应的私钥才行。 比如将比特币创世地址`1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa`转化为公共输出的限制,如果有人能证明自己拥有对应的私钥,那就有大概率可以证明自己是中本聪了。 ### 5.6 模拟器 对了,前面说到,如果零知识证明要是零知识的,协议要可以构造模拟器出来,且模拟器生成的证明在概率分布上与prover生成的证明无法区分。 那么对于zk-SNARKs,如何构造模拟器呢? 其实很简单,只要我们知道$\alpha_l,\alpha_r,\alpha_o,\beta,\gamma,s$这些被隐藏的值具体是什么,很容易就能伪造出一个证明,且不需要任何知识,所以这些参数又被称为陷门(trapdoor)或者有毒废料(toxic waste),是不能公开的,否则证明就无意义了。 具体推导就不推了,这个很简单,就跟最前面用来做引子的例子里,没有任何安全防护的状态是一样的。 ### 5.7 改进-Groth16 上面的分析应该已经足够去理解zk-SNARKs了,不过最后给出的方案其实还有改进空间,或者说还可以权衡。上面方案prover的证明一共包括$\{g_l^{L_p(s)},g_r^{R_p(s)},g_o^{O_p(s)},g_l^{L_{\alpha p}(s)},g_r^{R_{\alpha p}(s)},g_o^{O_{\alpha p}(s)},g^{h_\delta(s)},g^{z_p(s)}\}$。 即证明中包括八个椭圆曲线群元素,但我们知道,如果零知识证明要上链的话,我们肯定希望证明的size越小越好。 Jens Groth在16年发布了《On the Size of Pairing-based Non-interactive Arguments》里面提出了一种新的zk-SNARKs改进-被称为Groth16,证明只需要三个椭圆曲线群元素,大大减少了proof Size,按证明上链来说,等于一个区块可以塞下更多证明了,而且通信消耗也降低了。(当然,改进一直在继续),那么Groth16是怎么做的呢? 为了方便,我这就按最简单的写一些推导,示例是用非对称双线性映射,所以区分$g_1,g_2且g_t=e(g_1,g_2)$: proving key:$(g_1^{\alpha},g_1^{\beta},g_1^{\delta},g_2^{\beta},g_2^{\gamma},g_2^{\delta},\{g_1^{s^i}\}_{i=0}^{n-1},\{g_2^{s^i}\}_{i=0}^{n-1},\{g_1^{\frac{\beta l_i(s)+\alpha r_i(s)+o_i(s)}{\gamma}}\}_{i=0}^{l},\{g_1^{\frac{\beta l_i(s)+\alpha r_i(s)+o_i(s)}{\delta}}\}_{i=l+1}^{m},\{g_1^{\frac{s^it(s)}{\delta}}\}_{i=0}^{n-2})$ 以及$\{l_i(x),r_i(x),o_i(x)\}_{i=0}^m$ verification key: $(g_1,g_2,g_2^{\gamma},g_2^{\delta},g_t^{\alpha\beta},\{g_1^{\frac{\beta l_i(s)+\alpha r_i(s)+o_i(s)}{\gamma}}\}_{i=0}^{l}\})$ 生成证明:选取两个随机数$r_l,r_r$用于偏移,计算$h(x)=\frac{\sum_{i=0}^n (a_il_i(x) \cdot a_ir_i(x)-a_io_i(x))}{t(x)}$,通过$\{g_1^{\frac{s^it(x)}{\delta}}\}_{i=0}^{n-2}$线性组合算出$\{g_1^{\frac{h(s)t(s)}{\delta}}\}_{i=l+1}^{m}$ $g_1^{A(s)}=g_1^{\alpha}\cdot \prod_{i=0}^m g_1^{a_il_i(s)}\cdot g_1^{r_l\delta}$ $g_2^{B(s)}=g_2^{\beta}\cdot \prod_{i=0}^m g_2^{a_ir_i(s)}\cdot g_2^{r_r\delta}$ $g_1^{B(s)}=g_1^{\beta}\cdot \prod_{i=0}^m g_1^{a_ir_i(s)}\cdot g_1^{r_r\delta}$ $g_1^{C(s)}=\prod_{i=l+1}^m g_1^{\frac{a_i(\beta l_i(s)+\alpha r_i(s)+o_i(s))}{\delta}} \cdot {g_1^{\frac{h(s)t(s)}{\delta}}}\cdot g_1^{r_rA(s)}\cdot g_1^{r_lB(s)} \cdot g_1^{-r_lr_r\delta}$ 将$(g_1^{A(s)},g_2^{B(s)},g_1^{C(s)})$当成证明。 verifier计算:$g_1^{C_v(s)}=\prod_{i=0}^l g_1^{a_i}\cdot \{g_1^{\frac{\beta l_i(s)+\alpha r_i(s)+o_i(s)}{\gamma}}\}_{i=0}^{l}$(这里的$a_i$即statement,prover要求解的问题) 最后验证$e(g_1^{A(s)},g_2^{B(s)})=e(g_1^{\alpha},g_2^{\beta})\cdot e(g_1^{C_v(s)},g_2^\gamma)\cdot e(g_1^{C(s)},g_2^{\delta})=g_t^{\alpha\beta}\cdot e(g_1^{C_v(s)},g_2^\gamma)\cdot e(g_1^{C(s)},g_2^{\delta})$ 等式是否成立。 详细分析见星想法的文章:[零知识证明 - Groth16算法介绍](https://mp.weixin.qq.com/s/SguBb5vyAm2Vzht7WKgzug)以及Groth16原始论文,且除了Groth16,zk-SNARKs还有很多其他改进方案。 这样的话,证明就只有三个椭圆曲线群元素了,大大减少了证明大小,但是groth16是有取舍的。 groth16用$\alpha$ 和$\beta$保证系数一致性,并且用$\alpha \cdot \beta$限制证明中必包含这两个,然后用$\gamma$和$\delta$限制l r o之间的串用问题。但是groth16无法保证no-malleable,也就是说,你可以很简单的用已有的groth16证明生成一个新的有效证明。 比如有证明$(g_1^{A(s)},g_2^{B(s)},g_1^{C(s)})$,我们可以简单得到$(g_1^{A(s)},g_2^{B(s)}\cdot g_2^{x\delta},g_1^{C(s)}\cdot g_1^{x}\cdot g_1^{A(s)})$,亦可通过验证。 PPIO的朋友针对groth16的可操作性问题写了一篇详细分析:[如何利用 Groth16 的漏洞伪造证明](https://mp.weixin.qq.com/s/PAulyP65AfL2M14zJU606w) ### 5.8 进一步了解 可以进一步阅读零知识证明相关论文,或者直接了解一些零知识证明开源库,思考下和自己熟悉的领域有什么结合点。 安比试验室收集了一堆零知识证明的资料:[零知识证明学习资源汇总](https://mp.weixin.qq.com/s/-LU4FsFKt7ebX4sbKXNylA) 以及github仓库[awesome-zero-knowledge-proofs](https://github.com/matter-labs/awesome-zero-knowledge-proofs) 下面是一些开源库,可以实操一下零知识证明。 - [libsnark (C++)](https://github.com/scipr-lab/libsnark) - [great tutorial](https://github.com/christianlundkvist/libsnark-tutorial) - [bellmnan (rust)](https://github.com/zkcrypto/bellman/) - [demo circuit](https://github.com/ebfull/bellman-demo) - [jsnark (Java, bindings to libsnark)](https://github.com/akosba/jsnark) - [snarky (Ocaml, from authors of Coda)](https://github.com/o1-labs/snarky) - [zokrates (toolbox for zkSNARKs on Ethereum)](https://github.com/Zokrates/ZoKrates) 谢谢大家的阅读,如发现文章有错漏请指正,可联系微信@xcshuan0608,备注:区块链技术交流+组织+姓名。

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully