Tornado 龙卷风混币原理
项目背景Tornado(https://tornado.cash/)是以太坊隐私赛道著名的混币项目,其混币技术主要使用了 zk-SNARK 零知识证明。 1、关于 zk-SNARK 零知识证明的原理可以参见 if(DAO) 之前的文章: https://mirror.xyz/0xd05cFA28Eaf8B4eaFD8Cd86d33c6CeD1a1875417/X3qSOjObTknXQ_iGhDBFYETibD0TVW0twz5QDIthjGI 2、混币的意思是混币者通过 Tornado 合约将混币者的以太坊地址和资金去向地址失去关联,从而达到隐匿资金去向的目的。项目原理1、Bob 是第 3 个将自己的代币 Deposit 存入龙卷风合约进行混币的客户。 2、Alice 是第 4 个将自己的代币 Deposit 存入龙卷风合约进行混币的客户。 3、Alice 在 Deposit 时(1)Alice random k4 ,r4。k4 和 r4 in { 0 ,1 } ^ 256。( k4 ,r4 ) 也是 Alice 未来的取款凭证 note4。 (2)Alice 计算得到 C4 ...
Erasure Coding 纠删码原理( Near Protocol )
Erasure Code 是什么1、Erasure Code是一种编码技术,它可以将 n 份原始数据,增加 m 份数据,并能通过n+m份中的任意 n 份数据,还原为原始数据。即如果有任意小于等于 m 份的数据失效,仍然能通过剩下的数据还原出来。 2、纠删码技术在分布式存储系统中的应用主要有三类 (1)RS( Reed-Solomon )里德-所罗门纠删码 (2)AC( Array Code: RAID5、RAID6等)阵列纠删码 (3)LDPC( LowDensity Parity Check Code )低密度奇偶校验纠删码( LDPC 目前主要用于通信、视频和音频编码等领域 )Erasure Code 的优势副本策略和纠删码是存储领域常见的两种数据冗余技术。相比于副本策略,纠删码具有更高的磁盘利用率:RS码原理Reed-Solomon(RS)码是存储系统较为常用的一种纠删码,它有两个参数n和m,记为RS(n ,m)。n 代表原始数据块个数。m代表校验块个数。以n=5,m=3为例 : 1、encoding 编码过程(D是原始数据块,得到的C为校验块,构建 Bi 有专门的数学方法...
零知识证明之zk-STARK
zk-STARK(零知识的可扩展的透明知识论证)zero knowledge - Scalable Transparent ARgument of Knowledge zero knowledge(零知识):Private input(秘密的输入)将会被隐藏,除了 Prover 以外的任何人都不知道 Private input 的内容,在知识论证的过程中也不能反推出 Private input。 Scalable(扩展性):与 Replay Computation 的验证耗时相比,zk-STARK 生成 Proof 的时间复杂度近似于计算的复杂度 (O(n)),而 Verify Proof 的时间复杂度远小于计算的复杂度 (O(log))。 假设区块链网络中 Verifier 的 VerifyTime = Transcation 交易数量的对数的平方。当一个区块包含 10000 Transcation 时,验证者的 VerifyTime = (log₂ 10000)² ~ (13.2)² ~ 177 ms;当一个区块包含 100 0000 Transcation 时,验证者的 V...


Tornado 龙卷风混币原理
项目背景Tornado(https://tornado.cash/)是以太坊隐私赛道著名的混币项目,其混币技术主要使用了 zk-SNARK 零知识证明。 1、关于 zk-SNARK 零知识证明的原理可以参见 if(DAO) 之前的文章: https://mirror.xyz/0xd05cFA28Eaf8B4eaFD8Cd86d33c6CeD1a1875417/X3qSOjObTknXQ_iGhDBFYETibD0TVW0twz5QDIthjGI 2、混币的意思是混币者通过 Tornado 合约将混币者的以太坊地址和资金去向地址失去关联,从而达到隐匿资金去向的目的。项目原理1、Bob 是第 3 个将自己的代币 Deposit 存入龙卷风合约进行混币的客户。 2、Alice 是第 4 个将自己的代币 Deposit 存入龙卷风合约进行混币的客户。 3、Alice 在 Deposit 时(1)Alice random k4 ,r4。k4 和 r4 in { 0 ,1 } ^ 256。( k4 ,r4 ) 也是 Alice 未来的取款凭证 note4。 (2)Alice 计算得到 C4 ...
Erasure Coding 纠删码原理( Near Protocol )
Erasure Code 是什么1、Erasure Code是一种编码技术,它可以将 n 份原始数据,增加 m 份数据,并能通过n+m份中的任意 n 份数据,还原为原始数据。即如果有任意小于等于 m 份的数据失效,仍然能通过剩下的数据还原出来。 2、纠删码技术在分布式存储系统中的应用主要有三类 (1)RS( Reed-Solomon )里德-所罗门纠删码 (2)AC( Array Code: RAID5、RAID6等)阵列纠删码 (3)LDPC( LowDensity Parity Check Code )低密度奇偶校验纠删码( LDPC 目前主要用于通信、视频和音频编码等领域 )Erasure Code 的优势副本策略和纠删码是存储领域常见的两种数据冗余技术。相比于副本策略,纠删码具有更高的磁盘利用率:RS码原理Reed-Solomon(RS)码是存储系统较为常用的一种纠删码,它有两个参数n和m,记为RS(n ,m)。n 代表原始数据块个数。m代表校验块个数。以n=5,m=3为例 : 1、encoding 编码过程(D是原始数据块,得到的C为校验块,构建 Bi 有专门的数学方法...
零知识证明之zk-STARK
zk-STARK(零知识的可扩展的透明知识论证)zero knowledge - Scalable Transparent ARgument of Knowledge zero knowledge(零知识):Private input(秘密的输入)将会被隐藏,除了 Prover 以外的任何人都不知道 Private input 的内容,在知识论证的过程中也不能反推出 Private input。 Scalable(扩展性):与 Replay Computation 的验证耗时相比,zk-STARK 生成 Proof 的时间复杂度近似于计算的复杂度 (O(n)),而 Verify Proof 的时间复杂度远小于计算的复杂度 (O(log))。 假设区块链网络中 Verifier 的 VerifyTime = Transcation 交易数量的对数的平方。当一个区块包含 10000 Transcation 时,验证者的 VerifyTime = (log₂ 10000)² ~ (13.2)² ~ 177 ms;当一个区块包含 100 0000 Transcation 时,验证者的 V...
Share Dialog
Share Dialog

Subscribe to Ethan - if(DAO)

Subscribe to Ethan - if(DAO)
<100 subscribers
<100 subscribers
if(DAO) 试图用非技术语言和少量数学及密码学知识,向一般读者解释:
(1)zk-SNARK 是什么。
(2)zk-SNARK 有什么用。
(3)通过 zk-SNARK 的协议一步步进化过程弄清零知识的底层原理。
(4)通过 Proving Key 、Verifying Key 是什么;Proof 包含的内容有哪些;为什么通过 Proving Key 可以得到 Proof ;为什么通过 Verifying Key 可以验证 Proof 等问题,理解 zk-SNARK 是如何具体落地应用的 。
关于zk-SNARK的学术论文包含严谨的数学公式和论证过程;关于zk-SNARK的应用程序代码也有一些成熟的 lib 可供使用。但这些应该是数学家、密码学家和程序员感兴趣的内容,而不是一般读者。所以本文更侧重于如何将zk-SNARK的理论和实践 “联系” 起来。
文中如有错误之处还请指正。下面进入正文:
1、什么是零知识证明
零知识可以简单理解为:我有一个秘密,在不向我朋友阿文透露秘密的情况下,让阿文相信我知道这个秘密。我们将证明的过程/方法称做 “ 零知识证明 ”。
2、零知识证明和 zk-SNARK
零知识证明包含很多种不同算法的协议版本,其中的一个协议叫做:zk-SNARK 。
( 下文拆解的 zk-SNARK 是基于 Pinocchio 的协议版本 ,实际上 zk-SNARK 还包含很多变种版本,比如 Groth16 、Sonic 、Plonk 等 ;除了 zk-SNARK 之外的零知识证明比较普遍的还包括 zk-STARK、Bulletproofs、zkCNN 等等 )。
3、zk-SNARK
zk-SNARK = zero knowledge Succinct Non-interactive ARgument of Knowledge(非交互式简洁零知识论证)
(1)zero knowledge:零知识,即在证明的过程中不透露任何内情。
(2)Succinct:简洁的,主要是指验证过程不涉及大量数据传输以及验证算法简单。
(3)Non-interactive:无交互,即 Prover 和 Verifier 之间不需要进行交互即可验证命题的真伪。
(4)ARgument of Knowledge:知识论证,证明是严谨的,而论证是基于概率的,因而使用论证二字。
4、zk-SNARK中的几个基础概念
(1)零知识 = 我的秘密。
(2)Prover = 证明者,即上面例子中的 “ 我 ”。
(3)Verifier = 验证者,即上面例子中的 “ 阿文 ”。
(4)Proof = 我拿着这个证明(Proof)给阿文,阿文就可以验证我是否知道秘密。
(5)Proving Key = 我通过 Proving Key 才能生成 Proof。
(6)Verifying Key = 阿文通过 Verifying Key 可以验证我的 Proof 是否为真。
5、zk-SNARK 有什么用
zk-SNARK 目前在诸多应用场景上均有实现,在此举2个例子抛砖引玉:
数据隐私
我们正处在从 Web2.0 迈向 Web3.0 的时代,未来人们会对隐私有着更高的要求。
在 Web2.0 时代,如果我想向银行贷款,我需要出具各种资产证明(抵质押)或交易流水信息(信用),这些信息本来是属于我的个人数据资产,但却必须要赤裸裸的暴露在银行的审核部门之下。
在 Web3.0 时代,如果我想向银行贷款,zk-SNARK 可以提供更好的隐私解决方案:
零知识是我的资产情况,它可以转化为满足m个约束条件的n个多项式(下文会着重介绍如何对多项式进行零知识论证)。我在不暴露多项式的情况下,通过 Proving Key 生成一个 Proof ,该 Proof 向银行证明我具有贷款资质,而不用暴露我的实际资产信息;银行可以通过 Verifying Key 验证我的资产情况是否满足贷款资质,而无需知道我的实际资产情况。
数据计算和存储
绝大部分区块链网络共识的时候,所有节点做同样的计算才能达成共识,通过 zk-SNARK 可以改进为只需一方计算,其它节点进行简单验证即可,节省了大量的计算资源。
以 Mina Protocol 为例:Mina 网络将 Merkle Path(当前全网账户状态)转换为n个多项式,从而生成(递归的)zk-SNARK 证明,网络上的其他节点验证证明即可认为当前状态是正确的。当前全局账户状态中又包含上一个区块的账户状态(递归),当前正确,上一个就正确。往前以此类推。
对于 Mina 而言,一个递归生成的zk-SNARK证明只有 1kB 左右,即便加上当前区块的 Merkle Path(20kB),无论链体再长,其大小也不会超过 22 kB。这与其他区块链动辄几百G的体积相比几乎可以忽略不计。
协议中的零知识(即不能让他人知道的知识):多项式 P(x) = x^3−3x^2+2x。
协议中有2个角色:证明者和验证者。证明者自称知道 P(x),验证者不知道 P(x)。
证明的方式:证明者通过 Proving Key 生成 Proof 并发送给验证者,验证者通过 Verifying Key 验证 Proof 的正确性。整个过程中,验证者在不知道 P(x)的情况下验证了证明者是否知道 P(x)。
1、证明者
首先将 P(x) = x^3−3x^2+2x 因式分解为 P(x) =(x−0)(x−1)(x−2)。
2、证明者
随机选择因式中的一部分 t(x) = (x−1)(x−2),并 Public 出去。
3、证明者
证明者根据 P(x) = t(x) * h(x) 计算出 h(x) = P(x) /t(x) = (x-0) = x。
4、验证者
随机取值 s,然后代入多项式计算 t(s) ,并将 s 发送给证明者。
5、证明者
根据验证者发来的 s ,计算P(s) 和 h(s),并将计算结果发送给验证者。
6、验证者
使用第4步计算出的 t(s) 和第5步证明者发来的 P(s) 和 h(s),验证 t(s) = P(s) / h(s) 是否成立。
在这个最原始版本的协议中,我们回顾一下是如何实现证明的:证明者想向验证者证明他知道多项式 P(x),但又不想暴露知识 P(x)。于是证明者通过 Proving Key = s 计算得到 Proof = P(s) 和 h(s), 并发送给验证者。验证者通过 Verifying Key = t(s) 验证 Proof 的正确性,即验证者将证明者计算后发来的 t(s) 代入 P(x) = t(x) * h(x) ,如果等式成立则验证通过。
验证的整个过程中,验证者并不知道 P(x) 是什么,却通过 Verifying Key 验证出证明者“可能”知道 P(x)。当验证者随机取值 s0 …… s1 足够多的时候,验证者就可以大概率确认证明者知道 P(x)。
Proving Key 是什么?Verifying Key 是什么?Proof 包含的内容有哪些?为什么通过 Proving Key 可以得到 Proof ?为什么通过 Verifying Key 可以验证 Proof ?读到这里, if(DAO) 相信各位已经对传说中的 zk-SNARK 有一点感觉了。
在协议一的证明者第5步有一个明显的问题:验证者把 s 直接发给了证明者,由于 t(x) 是公开信息,所以证明者可以通过 s 计算出 t(s)的结果,比如 t(s)=5。进而证明者可以随意构造P(s) =15,h(s)=3 或者 P(s) =20,h(s)=4,将结果发送给验证者进行作弊。
为了解决这个问题,在协议二版本的 zk-SNARK 中,验证者不能把s直接发送给证明者,而是把经过隐藏之后的 s 发送给证明者。那么如何隐藏 s 呢,这需要用到一些密码学知识:同态加密。
同态加密的具体做法是:验证者生成一个只有他自己知道的G,并将随机数 s 构造成 E(s) = G^s (mod N)的形式,以达到隐藏s的目的。即验证者不希望证明者通过 s 计算P(s)和 h(s),而是让证明者通过 E(s) 计算 E(P(s)) 和 E(h(s)) 。这里有点绕,下面会解释具体的计算过程。
注:mod 计算的顺序是不影响计算结果的,也就是说先计算 mod 在进行加减乘除和先进行加减乘除再分别取 mod 的计算结果一样,所以下面的公式为了看起来简洁统一省略了 mod。
1、2、3、证明者
同上。
4、验证者
随机产生 s(不能直接发送给证明者),然后构造
E(s^3) = (G^(s^3))
E(s^2) = (G^(s^2))
E(s) = (G^s)
目的(1):隐藏s,不让证明者通过s计算t(s),所以就不能构造P(s) =15,h(s)=3 或者 P(s) =20,h(s)=4。
目的(2):隐藏了多项式的系数,因为知道了多项式的系数就等于知道了多项式,从而泄露了知识。
最后将构造的 E(s^3)、E(s^2) 、E(s) 发给证明者。
5、证明者
通过E(s^3) 、E(s^2) 、E(s) 计算 E(P(s)) ,计算过程如下:
E(P(s)) = G ^ P(s) = G ^ (s^3−3s^2+2s) = 图1最右侧 = 图1最左侧。

证明者把验证者发过来的 E(s^3) 、E(s^2) 、E(s) 代入最左边的方程即可求解出E(P(s)) 。
然后证明者通过 h(x) = P(x) / t(x) 计算得到 E(h(s))。
最后把 E(P(s)) 和 E(h(s)) 发送给验证者。
6、验证者
最后验证者通过 t(s) 验证:E(P(s)) 是否= E(h(s)) ^ t(s)。
这是因为协议一中验证者验证 “ P(s)是否 = h(s) * t(s) ” 等价于 “ G ^ P(s) 是否 = (G ^ h(s)) ^ t(s) ”,即E(P(s)) 是否= E(h(s)) ^ t(s) 。
在这个改进版的协议二,我们回顾一下是如何实现证明的:证明者通过同态加密后的 Proving Key = [ E(s^3)、E(s^2) 、E(s) ] 计算得到 Proof = [ E(P(s)) 、E(h(s)) ] , 并发送给验证者。验证者通过 Verifying Key = t(s) 验证 Proof 的正确性:E(P(s)) 是否= E(h(s)) ^ t(s)。
在协议二的证明者第6步存在一个证明者的问题:
验证者要验证的G ^ P(s) = (G ^ h(s)) ^ t(s) 等价于 G ^ P(S) = (G ^ t(s)) ^ h(s),证明者可以通过 E(s^3) = (G^(s^3))、E(s^2) = (G^(s^2))、E(s) =(G^s) 计算出来G^t(s) = E(t(s))。
这是因为t(x)是公开的,而E(s^3)、E(s^2) 、E(s)证明者又是知道的,所以证明者可以计算出 E(t(s)) = 图1最左侧 = 图1最右侧。这时证明者可以根据E(t(s)),指定一个h(s),并构造E(P(s)) ,从而满足E(P(s)) = E(t(s)) ^ h(s) 。
所以产生了问题:证明者给验证者的 E(P(x)) 不是用验证者提供的题干: (G^(s^3)), (G^(s^2)),(G^s)计算出来的,即证明者不知道 P(x) 却通过 h(s) 构造了一个满足 E(P(s)) = E(t(s)) ^ h(s)约束的假 E(P(s)),从而骗过了验证者。
解决思路:验证者再发送一个新的参数,该参数验证 E(P(x)) 必须用 (G^(s^3)), (G^(s^2)),(G^s) 计算出来。这个方法叫 KEA:Knowledge-of-Exponent Assumption 指数知识假设。
4、验证者
随机产生 s 并同态加密,然后随机产生 α(阿尔法不方便打字,请允许我用 a 代替偷懒)
然后构造 (G^(s^3)), (G^(s^2)),(G^s) ,构造 (G^((a * s^3)), (G^(a * s^2)),(G^(a * s))
最后将 G^P(s),G^P(a * s) 和 G^h(s) 发给证明者。
5、证明者
通过 [ (G^(s^3)), (G^(s^2)),(G^s) ],[ (G^((a * s^3)),(G^(a * s^2)),(G^(a * s)) ]计算得出 G^P(s),G^P(a * s),连同证明者第3步已知的 G^h(s) 一并发给验证者。
6、验证者
(1)最后验证:G^P(s) 是否= (G^h(s)) ^ t(s) (验证零知识:多项式 p(x) 有根 t(x));
(2)还需要验证 (G^P(s))^a 是否= G^(P(a * s)) (用来校验证明者是否使用了指定的多项式)。
这是因为随机数 a 是验证者产生的,证明者并不知道。验证者拿到证明者计算出来的 G^P(s)和 G^(P(a * s)),通过a进行验证即可。证明者如果通过构造 G^t(s),指定一个h(s),从而形成 (G^h(s)) ^ t(s) = (G^t(s)) ^ h(s) 的形式,然后计算出 G^P(s),是无法满足 G^P(s) 的a次幂刚好等于G^(P(a * s)) 的。
经过上面的分析,协议三的4、5、6步形如图2:

在这个改进版的协议三,我们回顾一下是如何实现证明的:证明者通过同态加密后的 Proving Key = [ E(s^3)、E(s^2) 、E(s) ] 和 [ E(a * s^3)*、E( a * s^2) *、E(a * s) ] ,计算得到 Proof = [ g^p = G^P(s) ,g^p’ = G^P(a*s) ,以及证明者第3步计算出的G^h(s) ] , 并发送给验证者。
(0)验证者的 Verifying Key = [ t(s) ,a ]。
(1)验证者通过 t(s) 验证 Proof 的正确性:E(P(s)) 是否= E(h(s)) ^ t(s)。
(2)验证者通过 a 验证 (G^P(s))^a 是否= G^(P(a * s)) ,即证明者提供的 g^P 是用验证者提供的 (G^(s^3)), (G^(s^2)),(G^s)计算出来的。
在协议三的第5步存在验证者的问题:
验证者有可能从证明者发来的信息中推导出多项式,从而导致零知识泄露。当多项式相对简单的时候,比如:b * x + c,验证者可以通过暴力破解的方式破解系数,然后验证者再按照证明者计算的内容将多项式计算出来。
所以证明者需要随机生成 δ 对返回值进行同态加密,将发送数据隐藏后再发送给验证者。证明者发送给验证者的值也进行同态加密,这个过程通常被称为:无成本的零知识。
5、证明者随机生成δ,并构造
(G^P(s))^δ、(G^h(s))^δ 和 (G^P(a * s))^δ 发送给验证者。
所以协议三的5、6步改进为图3:

在这个改进版的协议四,我们回顾一下是如何实现证明的:证明者通过同态加密后的 Proving Key = [ E(s^3)、E(s^2) 、E(s) ] 和 [ E(a * s^3)*、E( a * s^2) *、E(a * s) ] ,计算得到 Proof = [ g^p = G^P(s) ,g^p’ = G^P(a*s) ] , 将 Proof 再进行一次同态加密:Proof = [ (G^P(s))^δ、(G^P(a * s))^δ、以及证明者第3步计算出的G^h(x)的加密值:(G^h(s))^δ ],最后将 Proof 返回给验证者。
(0)验证者的 Verifying Key = [ t(s) ,a ]。
(1)验证者通过 t(s) 验证 Proof 的正确性:(G^P(s))^δ 是否= [ (G^h(s))^δ ]^ t(s)。
等价于 G^(P(s) * δ) 是否= G^( h(s) * δ * t(s) ) 等价于 G^(δ * p) 是否= G^(δ * h(s) * t(s) )。
(2)验证者通过 a 验证 [ (G^P(s))^δ ]^a 是否= [ (G^P(a * s))^δ ] 。
至此,我们一起把协议一升级到了协议四。细心的读者可能已经发现,协议四中仍然需要 “ 验证者先生成一些信息,把信息提供给证明者,证明者根据这些信息计算一些结果,再返回给验证者 ”,这分明就是在 “ 交互 ”,和我们的主题 zk-SNARK “ 非交互式 ” 零知识论证 不符。
虽然是 “ 交互式 ” ,但协议执行的过程中还是很完美的实现了我的既定目标:验证者在全程零知识的情况下验证了证明者的论述。那我们为什么还需要 “ 非交互式 ” 呢?
这是因为交互式零知识论证有着2个明显的硬伤:
1、因为需要交互,所以要求每个证明者和验证者必须同时在线。
2、每一个验证者都会随机选出 s 和 a,证明者需要计算n次,效率低下(n个节点)。
所以下面我们的主要工作将致力于将 “ 交互式 ” 改进为 “ 非交互式 ” 。
从交互式改为非交互式,即验证者和证明者之间不交互,我们面临的第一个严峻的问题是:
当 s 和 a 不能由验证者随机产生时,由于验证者和证明者之间不交互,证明者就不知道 t(s) 和 a,所以没法进行第6步的验证工作。
当 s 和 a 由第三方随机产生时,然后计算 t(s) 和 a,如果直接发送给验证者,且验证者和证明者串谋时,证明者依然可以通过 t(s) 指定一个h(s)从而构建P(s)。
当 s 和 a 由第三方随机产生时,然后计算 t(s) 和 a,加密后将 g^t(s) 和 g^a 发送给验证者,虽然解决了验证者和证明者串谋问题,但此时验证者得到的 Verifying Key = [ g^t(s),g^a ],是无法代入验证(G^P(s))^δ 是否= [ (G^h(s))^δ ]^ t(s)的(此时验证者知道g^t(s)却不知道t(s) )。
所以此时我们要引入一个新的工具来解决验证者不知道t(s)只知道g^t(s),无法代入验证(G^P(s))^δ 是否= [ (G^h(s))^δ ]^ t(s)的问题。这个新的工具就是椭圆曲线双线性映射。
配对操作(双线性映射)是一个数学结构,表示为函数 e(g,g),它给定一个数据集中的两个加密的输入(即 g^a, g^b ),可以将他们确定性地映射到另一组不同的输出数据集上的它们的乘积,即 e(g^a, g^b) = e(g, g)^(ab)。如图4所示:

已知椭圆曲线满足的性质:e(g^a, g^b) = g^(ab)。( 知道踩油门就可以开车,无需把发动机拆开才可以开车,Right?)
所以验证者验证 (G^P(s))^δ 是否= [ (G^h(s))^δ ]^ t(s) 的问题转化为:
验证者验证 e ( g^(P(s) * δ),g^1 ) 是否= e ( g^t(s) ,g^(h(s)*δ) )
这是因为:
左侧 = g^(P(s) * δ) * 1 = e ( g^(P(s) * δ),g^1 )
右侧 = g^( t(s) * h(s) * δ ) = e ( g^t(s), g^(h(s)*δ) )
同理 [ (g^p(s))^δ ]^a = [ (g^p(a*s))^δ ] 等价于 e ( (g^p(s))^δ,g^a ) = e ( (g^p(a*s))^δ,g^1 )
所以协议五最终的形式如图5所示:

第三方将 g^t(s) 和 g^a 发送给验证者,即Verification Key。
同时将 g^si 和 g^(a*si) 发送给证明者,即Prover Key。
证明者生成的 g^p(s)、g^h(s)、g^p'(s) = g^p(a*s),即Proof。
在这个改进版的协议五中,我们回顾一下是如何实现证明的:
4、第三方
随机生成 s 和 a,然后计算出 t(s),然后
(1)将 g^t(s) 和 g^a 发送给验证者,即Verifying Key。
(2)将 g^si 和 g^(a*si) 发送给证明者,即Prover Key。
5、证明者
根据g^si 和 g^(a*si) 计算出g^p(s)、g^p'(s) = g^p(a*s) *,*根据 P(x)=t(s) * h(s)计算出h(s)并构造出 g^h(s),然后再通过 δ 加密,即 Proof = [ (g^p(s))^δ、(g^p(a*s))^δ、(g^h(s))^δ ],最后把 Proof 发送给验证者。
6、验证者
(1)通过 g^t(s) 验证 Proof 的正确性:e ( g^(P(s) * δ) ,g^1 ) = e ( g^t(s) ,g^(h(s)*δ) )
(2)通过 g^a 验证 e ( (g^p(s))^δ ,g^a ) = e ( (g^p(a*s))^δ ,g^1 )。
协议五中有一个明显的问题:生成随机 t(s) 和 a 的第三方是未必可信的,就如同无数中心化机构舞弊的案例。
其中的一个解决方案是把全网所有的参与方拉下水,大家共同参与生成随机 t(s) 和 a 的过程。用到的工具是CRS(Common Reference String,公共参考串)。
多方参与生成CRS,如图6所示:

在协议六中多方参与生成CRS的问题:参与者生成 CRS 的过程并没有强制后一个参与者(图6中的 Bob 和 Carol)都要使用前面已经公开的 CRS。如果一个攻击者是链上的最后一个参与者,他可以像链上的第一个参与者一样忽略前面的 CRS 随便构造一个有效的 CRS,这样他就变成了唯一一个知道秘密 s 和 α 的人。
为了解决这个问题,我们可以额外再要求除了第一个(可信的)以外的每一个参与者去加密然后公开他的参数。
这样我们得到了一个完整的 zk-SNARKOP 协议,如图7所示:

1、证明者(不变)
首先将 P(x) = x^3−3x^2+2x 因式分解为 P(x) =(x−0)(x−1)(x−2)。
2、证明者(不变)
随机选择因式中的一部分 t(x) = (x−1)(x−2),并 Public 出去。
3、证明者(不变)
证明者根据 P(x) = t(x) * h(x) 计算出多项式 h(x) = P(x) /t(x) = (x-0) = x。
4、全网各方( 协议二、协议三、协议六 )
(1)网络多方参与生成一个相对可信的 CRS :生成随机数 s 和 a。也称 Setup 可信设置。
(2)构建加密值 g^si、g^a、g^(a*si)。
(3)生成 Proving Key = [ g^si、g^(a*si) ],并发送给证明者。
(4)生成 Verifying Key = [ g^a、g^t(s) ],并发送给验证者。
5、证明者(协议四)
(1)通过 g^si 计算得到 g^p(s) 和 g^h(s) (证明者知道 P(x) 和 h(x) = P(x) /t(x) )。
(2)通过 g^(a*si) 计算得到 g^(a*p(s)) 。
(3)随机生成 δ ,构造随机化 Proof = [ g^( δ * p(s))、g^( δ * h(s))、g^( δ * (a*p(s))) ],然后发送给验证者(Proof 中是不包含 P(x) 的知识的)。
6、验证者(协议五)
(1)Proof = [ g^( δ * p(s))、g^( δ * h(s))、g^( δ * (a*p(s))) ] 简写为 [ g^p、g^h、g^p’ ]。
(2)验证多项式约束(验证证明者是否使用 CRS 的参数来计算 g^p(s) 和 g^h(s)):
e ( (g^p(s))^δ ,g^a ) = e ( (g^p(a*s))^δ ,g^1 ) (协议五)简写为
e ( g^p ,g^a ) = e ( g^p’ ,g^1 )
(3)验证多项式系数(验证验证者是否知道多项式的各项系数 = 知道多项式):
e ( g^(P(s) * δ) ,g^1 ) = e ( g^t(s) ,g^(h(s)*δ) ) 简写为
e ( g^p ,g^1 ) = e ( g^t(s) ,g^h )
之前我们已经完成了对多项式 P(x) = h(x) * t(x) 的证明( 即协议七 zk-SNARKOP ),如果我们能够把计算机程序转化为 P(x) ,就可以完成对程序的零知识证明。
请注意:并不是程序直接转化为多项式(程序的灵活度和复杂度太高了,很难转为通用方法)。我们要转化为零知识的内容是:程序中的某个函数( Public )的输入参数、计算中间变量以及计算后的输出结果。我们可以将下文中的 l(x) 和 r(x) 理解为输入参数,o(x) 理解为计算后的输出结果。
任何计算的本质都是由以下形式的基本运算组成的:
左操作数 运算操作符 右操作数 = 输出结果
以乘法运算操作为例,上述运算可以转换为:l(x) * r(x) = o(x) ,即 l(x) * r(x) - o(x) = 0 。
令 P(x) = l(x) * r(x) - o(x) = 0 ,这代表 P(x) 在 x = 某个值 a 的时候与 x 轴有交点。
对于函数 f(a ,b) { return a * b },当传入参数是 :3 * 2 (= 6)的时候,可以构造多项式( Enforcing Operation )表示该函数: l(x) = 3x ,r(x) = 2x , o(x) = 6x ,约束条件为 x=1 。即 l(1) = 3 ,r(1) = 2 ,o(1) = 6。
那么 P(x) = l(x) * r(x) - o(x) = 3x * 2x - 6x = 6x^2 - 6x ,用图形表示出来就是:

从图形中可以清楚的看到,当 x = 1 的时候 P(x) = l(x) * r(x) - o(x) = 6x^2 - 6x = 0 ,这意味着 (x-1) 是 P(x) = l(x) * r(x) - o(x) 分解因式后的一个因子。这就是协议七 zk-SNARKOP 中的合适的 t(x) = x - 1 。
zk-SNARKOP 协议的零知识就转化为: P(x) = h(x) * t(x) = l(x) * r(x) - o(x)。所以协议可以更新为:

上面协议的本质就是将协议七 zk-SNARKOP 中的 P(x) 用 l(x) * r(x) - o(x) 代替。
协议八也存在比较明显的局限性:
1、乘法也不会仅仅是 a * b 的简单单一运算形式。( “ 继续协议八 ” 章节将解决此问题 )
2、程序计算不可能只包含乘法。( “ 继续协议九 ” 章节将解决此问题 )
我们首先解决复杂一点的乘法计算问题,比如 a * b * c( 例如 3 * 2 * 4 = 24 )。
a * b * c 可以转化为:
a * b = r1
r1 * c = r2
我们按照协议八的思路,将上述2个运算构造成 l(x) * r(x) = o(x) 的形式。将运算构造成多项式的方法统称插值法(用给定的一组点去构造一个能经过所有这些点的弯曲多项式),构造的具体方法有很多,包括:一组未知方程、牛顿多项式、内维尔算法、拉格朗日多项式、快速傅里叶变换。
为了方便计算,这里我们直接使用 “ 一组未知方程 ” 去构造 l(x) * r(x) = o(x) 的形式:
l(1) = a ,l(2) = r1
r(1) = b ,r(2) = c
o(1) = r1 ,o(2) = r2
当 x = 1 时,l(1) * r(1) = o(1)
当 x = 2 时,l(2) * r(2) = o(2)
( 实际情况是使用 x = Random 去构造 l(x) ,r(x) ,o(x) )
对于 l(1) = a ,l(2) = r1 就相当于点 (1 ,a) 和点 (2 ,r1) 确定一条直线,所以可以得到 l(x)。
对于 r(1) = b ,r(2) = c 就相当于点 (1 ,b) 和点 (2 ,c) 确定一条直线,所以可以得到 r(x)。
对于 o(1) = r1 ,o(2) = r2 就相当于点 (1 ,r1) 和点 (2 ,r2) 确定一条直线,所以可以得到 o(x)。
我们要计算的函数是 f(a ,b ,c) { return = a * b * c } ,函数是 Public 的,我们要构建 l(x) 、r(x) 、o(x)。
将 a * b * c 拆分成如下形式:
a * b = r1
r1 * c = r2
假设参数 a = 3 ,b = 2 ,c = 4,即:
3 * 2 = 6
6 * 4 = 24
使用 x = 1 和 x = 2 ( 实际情况是使用 x = Random )构建 l(x) 、r(x) 、o(x):
l(1) = 3 ,r(1) = 2 ,o(1) = 6
l(2) = 6 ,r(2) = 4 ,o(2) = 24
l(x) 经过了点(1 ,3)和点(2 ,6)得到 l(x) = 3x
因为2点确定一条直线 y = kx + b
1 * k + b = 3
2 * k + b = 6
k = 3 , b = 0
同理可以得到 r(x) 和 o(x)。
证明者通过函数的参数构造得到 l(x) ,r(x) 和 o(x) 后,即可执行协议八进行零知识证明。
协议八的问题是:如果是证明者构建 l(x) 、r(x) 、o(x),则他可以随意构造( a 、b 、c是零知识,只有证明者自己知道 ),并且满足验证者的校验。例如证明者把 b = 2 改为 b = 1 :
a * b * c 仍然拆分成 :
a * b = r1
r1 * c = r2
3 * 1 = 3
3 * 4 = 12
l(1) = 3 , r(1) = 1 ,o(1) = 3
l(2) = 3 ,r(2) = 4 ,o(2) = 12
证明者修改 b 的取值后构建的 l(x) 、r(x) 、o(x) 仍然满足 h(x) * t(x) = l(x) * r(x) - o(x) 。
因此构建 l(x) 、r(x) 、o(x) 的工作一定要交给 Setup ,所以我们需要想办法让证明者通过 Setup 生成的 l(x) 、r(x) 、o(x) 去构建 L(x) * R(x) = O(x),然后再执行协议八。
根据多项式的一个简单性质 :y = l(x) 等价于 a * y = a * l(x)
我们对于之前的简单例子 :3 * 2 = 6 进行重新构造,即证明者构造出 :
L(x) = 3 * l(x)
R(x) = 2 * r(x)
O(x) = 6 * o(x)
即可。这样的结果是:Setup 选取随机的 x 构造 l(x) 、r(x) 、o(x) 并发送给验证者,验证者根据 Setup发来的结果,将对应的系数 3、2、6 进行分配得到 L(x) 、R(x) 、O(x) ,然后再执行协议八。
对于再复杂一点的运算,上面简单分配系数的方法就行不通了,我们需要一个更加通用的方法,例如对于下面的复杂乘法计算 :
a * b = r1
a * c = r2
d * c = r3
我们仍然构造
l(1) = 1 ,l(2) = 1 ,那 l(3) = 什么呢,显然 l(3) 未必 = d 。这种情况下我们可以使用一个通用构造:
la(1) = 1 ,la(2) = 1 ,la(3) = 0
ld(1) = 0 ,ld(2) = 0 ,ld(3) = 1
将这个计算乘法运算的较为通用的构造多项式的 la(x) 和 ld(x) 更新至协议九 :

对于加法、减法和除法,都可以转化为 l(x) * r(x) = o(x) 的形式:
加法 :a + b = r 等价于 ( a + b ) * 1 = r ,即 l(x) = a + b ,r(x) = 1 ,o(x) = r。
比如 3 + 2 = 5
l(x) = 3 la(x) + 2 ld(x) ,r(x) = 1 ,o(x) = 5。
la(1) = 1 * la(2) = 0。
ld(1) = 0 * ld(2) = 1。
L(x) = a * la(x) + d * ld(x)。
减法 :a + b = r 等价于 ( a + (-b) ) * 1 = r ,即 l(x) = a + (-b) ,r(x) = 1 ,o(x) = r。
除法 :a / b = r 等价于 a * r = b ,即 l(x) = a ,r(x) = r ,o(x) = b。
至此,我们已经将程序包含的函数中的输入参数、计算过程和输出结果成功转化成为多项式,又将多项式成功进行了零知识证明。上述内容基本涵盖了 zk-SNARK 最最核心的思想和实现路径,已经满足了本文撰写的预期。
最终的 zk-SNARK 协议在此基础上对协议执行进行进一步的约束,然后对协议进行了优化,由于计算过程较为繁琐并难以表达,故在此不再展开讨论,仅展示如下 :

作为区块链技术中应用最广泛的 zk-SNARKs,已经发展出了诸多各具特点的协议算法 :
1、Groth16
Groth16 是目前最快、数据量最小的 zk-SNARK,最早被用于 Zcash ,也是区块链中的第一个 zk-SNARK 用例。Groth16 的 CRS 不是通用的,其设置需要绑定到一个特定的电路。
论文 :https://eprint.iacr.org/2016/260
2、Sonic
Sonic 是一种早期的通用 zk-SNARK 协议,支持通用、可升级的 CRS 。Sonic 的证明大小固定,但是验证成本高,理论上可以将多个证明分批验证以获得更好的性能。
论文 :https://eprint.iacr.org/2019/099
3、Fractal
Fractal 是一种允许递归的 zk-SNARK。通过对电路的预处理实现了透明设置。证明最大 250KB,这比其他构建生成的证明都要大的多。
论文 :https://eprint.iacr.org/2019/1076
4、Halo
Halo 支持递归证据组织,无需可信设置,与其他新的 zk-SNARK 构建不同,Halo 的验证时间是线性的。
论文 :https://eprint.iacr.org/2019/1021
5、SuperSonic
Sonic 的改进版,是第一个在验证时间和证明数据量方面实用化的透明 zk-SNARK。
论文 :https://eprint.iacr.org/2019/1229
6、Marlin
Sonic 的改进版,证明时间缩短 10 倍,验证时间缩短 4 倍。
论文 :https://eprint.iacr.org/2019/1047
7、Plonk
Sonic 的改进版,证明时间缩短 5 倍。
论文 :https://eprint.iacr.org/2019/953
密码学是区块链技术的基石,而其中的零知识证明可以看作是密码学中璀璨的明珠。密码学是一门可以追溯到 2000 多年前的古老学问,其发展历史主要可以划分为以下 3 个阶段 :
1、古典密码学
16 世纪由法国人发明的 “ 维吉尼亚密码 ” 是古典密码理论发展上的一个重要里程碑,它使用一个词组作为密钥,词组中每一个字母都将确定一个替换表。这一时期的密码学主要被应用于军事领域,如何有效的传递密文,又能防止被敌人截获后破解。
2、近代密码学
源于香农在 20 世纪 40 年代末发表的一系列论文,特别是 1949 年的 “ Communication Theory of Secrecy Systems (保密系统通信理论)”,把已有数千年历史的密码学推向了基于信息论的科学轨道,密码学终于从艺术转向科学。这一时期的重要突破是 DES 的出现,并在金融等商业领域得到了广泛的应用。
3、现代密码学
1977 年麻省理工学院的 Ron Rivest、Adi Shamir、Leonard Adleman 提出的非对称加密算法 RSA,有效的解决了密钥传送的问题,标志着密码学进入了百家争鸣的现代阶段。
(0)零知识证明
1989 年麻省理工学院研究人员 Goldwasser、Micali 及 Rackoff 提出了 “ 零知识证明 ” 的概念。实现零知识证明的协议有很多,其中比较有名的包括 :zk-SNARK 、zk-STARK 、Bulletproofs 、zkBoo 、zkCNN 等。
(1)zk-SNARK
2012 年加州大学伯克利分校的 Alessandro Chiesa 教授与人合著了一篇论文,为他们第一次构造的零知识证明创造了术语 zk-SNARK。zk-SNARK 依赖于椭圆曲线来保证安全。
(2)Bulletproofs
2017 年 Benedikt Bünz 、Jonathan Bootle 、Dan Boneh 、Andrew Poelstra 、Pieter Wuille 、Greg Maxwell 撰写了第一篇描述 Bulletproofs 的论文。Bulletproofs 依赖于离散对数假设并且不需要可信设置。
(3)zk-STARK
2018 年 Eli Ben-Sasson、Iddo Bentov、Yinon Horeshy 和 Michael Riabzev 撰写了第一篇描述 zk-STARK 的论文。STARKs 的基础技术依赖于散列函数。依赖散列函数提供了一些好处,例如具有量子抵抗性、不需要任何可信设置。
1、if(DAO) 对目前主流的零知识证明协议进行了对比:

(1)O = 时间复杂度
时间复杂度并不是表示一个程序解决问题需要花多少时间,而是当问题规模扩大后,程序需要的时间长度增长得有多快。也就是说对于高速处理数据的计算机来说,处理某一个特定数据的效率不能衡量一个程序的好坏,而应该看当这个数据的规模变大到数百倍后,程序运行时间是否还是一样,或者也跟着慢了数百倍,或者变慢了数万倍。不管数据有多大,程序处理花的时间始终是一定的,我们就说这个程序具有 O(1) 的时间复杂度,也称常数级复杂度。
(2)C = 多项式的运算过程 = 电路的门数( Gate ),即下图的 + - * / 二维运算的总数:

比如 SHA256 ( m ) = h,我们表达成电路 C 的形式 :C ( h ,m ) = 0。计算机要完成一次SHA256 哈希运算需要约 20 K Gates。也就是说,计算过程越复杂,电路越复杂,涉及的门越多,在同等计算资源的情况下零知识证明的过程越慢。以著名以太坊二层项目 zkSync 为例,在顶配服务器的情况下刷一次电路的时间是 10 分钟左右。( Poseidon Hash Gates 只有几百个 )。
2、其他机构的零知识证明对比:引用 Matter Labs(https://github.com/matter-labs/awesome-zero-knowledge-proofs) 和 Beanstalk(https://docs.google.com/presentation/d/1gfB6WZMvM9mmDKofFibIgsyYShdf0RV_Y8TLz3k1Ls0/edit#slide=id.g443ebc39b4_0_110) 的研究报告简单对比一下上述 3 种最普遍的零知识证明协议:


时至今日,零知识证明已经在区块链领域大放异彩 :
技术应用方面,第一个实现 zk-SNARK 的匿名加密货币 Zcash (ZEC)、Layer2 的主要解决方案 zk Rollup 、Mina Protocol 的智能合约 Snapp 等等。
资本市场方面,排名前20位的加密基金中有超过 60%的基金都长期持仓隐私加密资产(数据来源:Messari 2021 年度报告)。
在可预见的将来,零知识证明一定会迎来技术成熟期,并得到爆发式的应用和增长 !
https://arxiv.org/pdf/1906.07221.pdf (maksym@petkus.info)
https://secbit.io/blog/ (安比实验室)
================================
================================
欢迎大佬们的探讨指正~
您可以在这里找到作者 twitter:@ethan_ifdao
if(DAO) 试图用非技术语言和少量数学及密码学知识,向一般读者解释:
(1)zk-SNARK 是什么。
(2)zk-SNARK 有什么用。
(3)通过 zk-SNARK 的协议一步步进化过程弄清零知识的底层原理。
(4)通过 Proving Key 、Verifying Key 是什么;Proof 包含的内容有哪些;为什么通过 Proving Key 可以得到 Proof ;为什么通过 Verifying Key 可以验证 Proof 等问题,理解 zk-SNARK 是如何具体落地应用的 。
关于zk-SNARK的学术论文包含严谨的数学公式和论证过程;关于zk-SNARK的应用程序代码也有一些成熟的 lib 可供使用。但这些应该是数学家、密码学家和程序员感兴趣的内容,而不是一般读者。所以本文更侧重于如何将zk-SNARK的理论和实践 “联系” 起来。
文中如有错误之处还请指正。下面进入正文:
1、什么是零知识证明
零知识可以简单理解为:我有一个秘密,在不向我朋友阿文透露秘密的情况下,让阿文相信我知道这个秘密。我们将证明的过程/方法称做 “ 零知识证明 ”。
2、零知识证明和 zk-SNARK
零知识证明包含很多种不同算法的协议版本,其中的一个协议叫做:zk-SNARK 。
( 下文拆解的 zk-SNARK 是基于 Pinocchio 的协议版本 ,实际上 zk-SNARK 还包含很多变种版本,比如 Groth16 、Sonic 、Plonk 等 ;除了 zk-SNARK 之外的零知识证明比较普遍的还包括 zk-STARK、Bulletproofs、zkCNN 等等 )。
3、zk-SNARK
zk-SNARK = zero knowledge Succinct Non-interactive ARgument of Knowledge(非交互式简洁零知识论证)
(1)zero knowledge:零知识,即在证明的过程中不透露任何内情。
(2)Succinct:简洁的,主要是指验证过程不涉及大量数据传输以及验证算法简单。
(3)Non-interactive:无交互,即 Prover 和 Verifier 之间不需要进行交互即可验证命题的真伪。
(4)ARgument of Knowledge:知识论证,证明是严谨的,而论证是基于概率的,因而使用论证二字。
4、zk-SNARK中的几个基础概念
(1)零知识 = 我的秘密。
(2)Prover = 证明者,即上面例子中的 “ 我 ”。
(3)Verifier = 验证者,即上面例子中的 “ 阿文 ”。
(4)Proof = 我拿着这个证明(Proof)给阿文,阿文就可以验证我是否知道秘密。
(5)Proving Key = 我通过 Proving Key 才能生成 Proof。
(6)Verifying Key = 阿文通过 Verifying Key 可以验证我的 Proof 是否为真。
5、zk-SNARK 有什么用
zk-SNARK 目前在诸多应用场景上均有实现,在此举2个例子抛砖引玉:
数据隐私
我们正处在从 Web2.0 迈向 Web3.0 的时代,未来人们会对隐私有着更高的要求。
在 Web2.0 时代,如果我想向银行贷款,我需要出具各种资产证明(抵质押)或交易流水信息(信用),这些信息本来是属于我的个人数据资产,但却必须要赤裸裸的暴露在银行的审核部门之下。
在 Web3.0 时代,如果我想向银行贷款,zk-SNARK 可以提供更好的隐私解决方案:
零知识是我的资产情况,它可以转化为满足m个约束条件的n个多项式(下文会着重介绍如何对多项式进行零知识论证)。我在不暴露多项式的情况下,通过 Proving Key 生成一个 Proof ,该 Proof 向银行证明我具有贷款资质,而不用暴露我的实际资产信息;银行可以通过 Verifying Key 验证我的资产情况是否满足贷款资质,而无需知道我的实际资产情况。
数据计算和存储
绝大部分区块链网络共识的时候,所有节点做同样的计算才能达成共识,通过 zk-SNARK 可以改进为只需一方计算,其它节点进行简单验证即可,节省了大量的计算资源。
以 Mina Protocol 为例:Mina 网络将 Merkle Path(当前全网账户状态)转换为n个多项式,从而生成(递归的)zk-SNARK 证明,网络上的其他节点验证证明即可认为当前状态是正确的。当前全局账户状态中又包含上一个区块的账户状态(递归),当前正确,上一个就正确。往前以此类推。
对于 Mina 而言,一个递归生成的zk-SNARK证明只有 1kB 左右,即便加上当前区块的 Merkle Path(20kB),无论链体再长,其大小也不会超过 22 kB。这与其他区块链动辄几百G的体积相比几乎可以忽略不计。
协议中的零知识(即不能让他人知道的知识):多项式 P(x) = x^3−3x^2+2x。
协议中有2个角色:证明者和验证者。证明者自称知道 P(x),验证者不知道 P(x)。
证明的方式:证明者通过 Proving Key 生成 Proof 并发送给验证者,验证者通过 Verifying Key 验证 Proof 的正确性。整个过程中,验证者在不知道 P(x)的情况下验证了证明者是否知道 P(x)。
1、证明者
首先将 P(x) = x^3−3x^2+2x 因式分解为 P(x) =(x−0)(x−1)(x−2)。
2、证明者
随机选择因式中的一部分 t(x) = (x−1)(x−2),并 Public 出去。
3、证明者
证明者根据 P(x) = t(x) * h(x) 计算出 h(x) = P(x) /t(x) = (x-0) = x。
4、验证者
随机取值 s,然后代入多项式计算 t(s) ,并将 s 发送给证明者。
5、证明者
根据验证者发来的 s ,计算P(s) 和 h(s),并将计算结果发送给验证者。
6、验证者
使用第4步计算出的 t(s) 和第5步证明者发来的 P(s) 和 h(s),验证 t(s) = P(s) / h(s) 是否成立。
在这个最原始版本的协议中,我们回顾一下是如何实现证明的:证明者想向验证者证明他知道多项式 P(x),但又不想暴露知识 P(x)。于是证明者通过 Proving Key = s 计算得到 Proof = P(s) 和 h(s), 并发送给验证者。验证者通过 Verifying Key = t(s) 验证 Proof 的正确性,即验证者将证明者计算后发来的 t(s) 代入 P(x) = t(x) * h(x) ,如果等式成立则验证通过。
验证的整个过程中,验证者并不知道 P(x) 是什么,却通过 Verifying Key 验证出证明者“可能”知道 P(x)。当验证者随机取值 s0 …… s1 足够多的时候,验证者就可以大概率确认证明者知道 P(x)。
Proving Key 是什么?Verifying Key 是什么?Proof 包含的内容有哪些?为什么通过 Proving Key 可以得到 Proof ?为什么通过 Verifying Key 可以验证 Proof ?读到这里, if(DAO) 相信各位已经对传说中的 zk-SNARK 有一点感觉了。
在协议一的证明者第5步有一个明显的问题:验证者把 s 直接发给了证明者,由于 t(x) 是公开信息,所以证明者可以通过 s 计算出 t(s)的结果,比如 t(s)=5。进而证明者可以随意构造P(s) =15,h(s)=3 或者 P(s) =20,h(s)=4,将结果发送给验证者进行作弊。
为了解决这个问题,在协议二版本的 zk-SNARK 中,验证者不能把s直接发送给证明者,而是把经过隐藏之后的 s 发送给证明者。那么如何隐藏 s 呢,这需要用到一些密码学知识:同态加密。
同态加密的具体做法是:验证者生成一个只有他自己知道的G,并将随机数 s 构造成 E(s) = G^s (mod N)的形式,以达到隐藏s的目的。即验证者不希望证明者通过 s 计算P(s)和 h(s),而是让证明者通过 E(s) 计算 E(P(s)) 和 E(h(s)) 。这里有点绕,下面会解释具体的计算过程。
注:mod 计算的顺序是不影响计算结果的,也就是说先计算 mod 在进行加减乘除和先进行加减乘除再分别取 mod 的计算结果一样,所以下面的公式为了看起来简洁统一省略了 mod。
1、2、3、证明者
同上。
4、验证者
随机产生 s(不能直接发送给证明者),然后构造
E(s^3) = (G^(s^3))
E(s^2) = (G^(s^2))
E(s) = (G^s)
目的(1):隐藏s,不让证明者通过s计算t(s),所以就不能构造P(s) =15,h(s)=3 或者 P(s) =20,h(s)=4。
目的(2):隐藏了多项式的系数,因为知道了多项式的系数就等于知道了多项式,从而泄露了知识。
最后将构造的 E(s^3)、E(s^2) 、E(s) 发给证明者。
5、证明者
通过E(s^3) 、E(s^2) 、E(s) 计算 E(P(s)) ,计算过程如下:
E(P(s)) = G ^ P(s) = G ^ (s^3−3s^2+2s) = 图1最右侧 = 图1最左侧。

证明者把验证者发过来的 E(s^3) 、E(s^2) 、E(s) 代入最左边的方程即可求解出E(P(s)) 。
然后证明者通过 h(x) = P(x) / t(x) 计算得到 E(h(s))。
最后把 E(P(s)) 和 E(h(s)) 发送给验证者。
6、验证者
最后验证者通过 t(s) 验证:E(P(s)) 是否= E(h(s)) ^ t(s)。
这是因为协议一中验证者验证 “ P(s)是否 = h(s) * t(s) ” 等价于 “ G ^ P(s) 是否 = (G ^ h(s)) ^ t(s) ”,即E(P(s)) 是否= E(h(s)) ^ t(s) 。
在这个改进版的协议二,我们回顾一下是如何实现证明的:证明者通过同态加密后的 Proving Key = [ E(s^3)、E(s^2) 、E(s) ] 计算得到 Proof = [ E(P(s)) 、E(h(s)) ] , 并发送给验证者。验证者通过 Verifying Key = t(s) 验证 Proof 的正确性:E(P(s)) 是否= E(h(s)) ^ t(s)。
在协议二的证明者第6步存在一个证明者的问题:
验证者要验证的G ^ P(s) = (G ^ h(s)) ^ t(s) 等价于 G ^ P(S) = (G ^ t(s)) ^ h(s),证明者可以通过 E(s^3) = (G^(s^3))、E(s^2) = (G^(s^2))、E(s) =(G^s) 计算出来G^t(s) = E(t(s))。
这是因为t(x)是公开的,而E(s^3)、E(s^2) 、E(s)证明者又是知道的,所以证明者可以计算出 E(t(s)) = 图1最左侧 = 图1最右侧。这时证明者可以根据E(t(s)),指定一个h(s),并构造E(P(s)) ,从而满足E(P(s)) = E(t(s)) ^ h(s) 。
所以产生了问题:证明者给验证者的 E(P(x)) 不是用验证者提供的题干: (G^(s^3)), (G^(s^2)),(G^s)计算出来的,即证明者不知道 P(x) 却通过 h(s) 构造了一个满足 E(P(s)) = E(t(s)) ^ h(s)约束的假 E(P(s)),从而骗过了验证者。
解决思路:验证者再发送一个新的参数,该参数验证 E(P(x)) 必须用 (G^(s^3)), (G^(s^2)),(G^s) 计算出来。这个方法叫 KEA:Knowledge-of-Exponent Assumption 指数知识假设。
4、验证者
随机产生 s 并同态加密,然后随机产生 α(阿尔法不方便打字,请允许我用 a 代替偷懒)
然后构造 (G^(s^3)), (G^(s^2)),(G^s) ,构造 (G^((a * s^3)), (G^(a * s^2)),(G^(a * s))
最后将 G^P(s),G^P(a * s) 和 G^h(s) 发给证明者。
5、证明者
通过 [ (G^(s^3)), (G^(s^2)),(G^s) ],[ (G^((a * s^3)),(G^(a * s^2)),(G^(a * s)) ]计算得出 G^P(s),G^P(a * s),连同证明者第3步已知的 G^h(s) 一并发给验证者。
6、验证者
(1)最后验证:G^P(s) 是否= (G^h(s)) ^ t(s) (验证零知识:多项式 p(x) 有根 t(x));
(2)还需要验证 (G^P(s))^a 是否= G^(P(a * s)) (用来校验证明者是否使用了指定的多项式)。
这是因为随机数 a 是验证者产生的,证明者并不知道。验证者拿到证明者计算出来的 G^P(s)和 G^(P(a * s)),通过a进行验证即可。证明者如果通过构造 G^t(s),指定一个h(s),从而形成 (G^h(s)) ^ t(s) = (G^t(s)) ^ h(s) 的形式,然后计算出 G^P(s),是无法满足 G^P(s) 的a次幂刚好等于G^(P(a * s)) 的。
经过上面的分析,协议三的4、5、6步形如图2:

在这个改进版的协议三,我们回顾一下是如何实现证明的:证明者通过同态加密后的 Proving Key = [ E(s^3)、E(s^2) 、E(s) ] 和 [ E(a * s^3)*、E( a * s^2) *、E(a * s) ] ,计算得到 Proof = [ g^p = G^P(s) ,g^p’ = G^P(a*s) ,以及证明者第3步计算出的G^h(s) ] , 并发送给验证者。
(0)验证者的 Verifying Key = [ t(s) ,a ]。
(1)验证者通过 t(s) 验证 Proof 的正确性:E(P(s)) 是否= E(h(s)) ^ t(s)。
(2)验证者通过 a 验证 (G^P(s))^a 是否= G^(P(a * s)) ,即证明者提供的 g^P 是用验证者提供的 (G^(s^3)), (G^(s^2)),(G^s)计算出来的。
在协议三的第5步存在验证者的问题:
验证者有可能从证明者发来的信息中推导出多项式,从而导致零知识泄露。当多项式相对简单的时候,比如:b * x + c,验证者可以通过暴力破解的方式破解系数,然后验证者再按照证明者计算的内容将多项式计算出来。
所以证明者需要随机生成 δ 对返回值进行同态加密,将发送数据隐藏后再发送给验证者。证明者发送给验证者的值也进行同态加密,这个过程通常被称为:无成本的零知识。
5、证明者随机生成δ,并构造
(G^P(s))^δ、(G^h(s))^δ 和 (G^P(a * s))^δ 发送给验证者。
所以协议三的5、6步改进为图3:

在这个改进版的协议四,我们回顾一下是如何实现证明的:证明者通过同态加密后的 Proving Key = [ E(s^3)、E(s^2) 、E(s) ] 和 [ E(a * s^3)*、E( a * s^2) *、E(a * s) ] ,计算得到 Proof = [ g^p = G^P(s) ,g^p’ = G^P(a*s) ] , 将 Proof 再进行一次同态加密:Proof = [ (G^P(s))^δ、(G^P(a * s))^δ、以及证明者第3步计算出的G^h(x)的加密值:(G^h(s))^δ ],最后将 Proof 返回给验证者。
(0)验证者的 Verifying Key = [ t(s) ,a ]。
(1)验证者通过 t(s) 验证 Proof 的正确性:(G^P(s))^δ 是否= [ (G^h(s))^δ ]^ t(s)。
等价于 G^(P(s) * δ) 是否= G^( h(s) * δ * t(s) ) 等价于 G^(δ * p) 是否= G^(δ * h(s) * t(s) )。
(2)验证者通过 a 验证 [ (G^P(s))^δ ]^a 是否= [ (G^P(a * s))^δ ] 。
至此,我们一起把协议一升级到了协议四。细心的读者可能已经发现,协议四中仍然需要 “ 验证者先生成一些信息,把信息提供给证明者,证明者根据这些信息计算一些结果,再返回给验证者 ”,这分明就是在 “ 交互 ”,和我们的主题 zk-SNARK “ 非交互式 ” 零知识论证 不符。
虽然是 “ 交互式 ” ,但协议执行的过程中还是很完美的实现了我的既定目标:验证者在全程零知识的情况下验证了证明者的论述。那我们为什么还需要 “ 非交互式 ” 呢?
这是因为交互式零知识论证有着2个明显的硬伤:
1、因为需要交互,所以要求每个证明者和验证者必须同时在线。
2、每一个验证者都会随机选出 s 和 a,证明者需要计算n次,效率低下(n个节点)。
所以下面我们的主要工作将致力于将 “ 交互式 ” 改进为 “ 非交互式 ” 。
从交互式改为非交互式,即验证者和证明者之间不交互,我们面临的第一个严峻的问题是:
当 s 和 a 不能由验证者随机产生时,由于验证者和证明者之间不交互,证明者就不知道 t(s) 和 a,所以没法进行第6步的验证工作。
当 s 和 a 由第三方随机产生时,然后计算 t(s) 和 a,如果直接发送给验证者,且验证者和证明者串谋时,证明者依然可以通过 t(s) 指定一个h(s)从而构建P(s)。
当 s 和 a 由第三方随机产生时,然后计算 t(s) 和 a,加密后将 g^t(s) 和 g^a 发送给验证者,虽然解决了验证者和证明者串谋问题,但此时验证者得到的 Verifying Key = [ g^t(s),g^a ],是无法代入验证(G^P(s))^δ 是否= [ (G^h(s))^δ ]^ t(s)的(此时验证者知道g^t(s)却不知道t(s) )。
所以此时我们要引入一个新的工具来解决验证者不知道t(s)只知道g^t(s),无法代入验证(G^P(s))^δ 是否= [ (G^h(s))^δ ]^ t(s)的问题。这个新的工具就是椭圆曲线双线性映射。
配对操作(双线性映射)是一个数学结构,表示为函数 e(g,g),它给定一个数据集中的两个加密的输入(即 g^a, g^b ),可以将他们确定性地映射到另一组不同的输出数据集上的它们的乘积,即 e(g^a, g^b) = e(g, g)^(ab)。如图4所示:

已知椭圆曲线满足的性质:e(g^a, g^b) = g^(ab)。( 知道踩油门就可以开车,无需把发动机拆开才可以开车,Right?)
所以验证者验证 (G^P(s))^δ 是否= [ (G^h(s))^δ ]^ t(s) 的问题转化为:
验证者验证 e ( g^(P(s) * δ),g^1 ) 是否= e ( g^t(s) ,g^(h(s)*δ) )
这是因为:
左侧 = g^(P(s) * δ) * 1 = e ( g^(P(s) * δ),g^1 )
右侧 = g^( t(s) * h(s) * δ ) = e ( g^t(s), g^(h(s)*δ) )
同理 [ (g^p(s))^δ ]^a = [ (g^p(a*s))^δ ] 等价于 e ( (g^p(s))^δ,g^a ) = e ( (g^p(a*s))^δ,g^1 )
所以协议五最终的形式如图5所示:

第三方将 g^t(s) 和 g^a 发送给验证者,即Verification Key。
同时将 g^si 和 g^(a*si) 发送给证明者,即Prover Key。
证明者生成的 g^p(s)、g^h(s)、g^p'(s) = g^p(a*s),即Proof。
在这个改进版的协议五中,我们回顾一下是如何实现证明的:
4、第三方
随机生成 s 和 a,然后计算出 t(s),然后
(1)将 g^t(s) 和 g^a 发送给验证者,即Verifying Key。
(2)将 g^si 和 g^(a*si) 发送给证明者,即Prover Key。
5、证明者
根据g^si 和 g^(a*si) 计算出g^p(s)、g^p'(s) = g^p(a*s) *,*根据 P(x)=t(s) * h(s)计算出h(s)并构造出 g^h(s),然后再通过 δ 加密,即 Proof = [ (g^p(s))^δ、(g^p(a*s))^δ、(g^h(s))^δ ],最后把 Proof 发送给验证者。
6、验证者
(1)通过 g^t(s) 验证 Proof 的正确性:e ( g^(P(s) * δ) ,g^1 ) = e ( g^t(s) ,g^(h(s)*δ) )
(2)通过 g^a 验证 e ( (g^p(s))^δ ,g^a ) = e ( (g^p(a*s))^δ ,g^1 )。
协议五中有一个明显的问题:生成随机 t(s) 和 a 的第三方是未必可信的,就如同无数中心化机构舞弊的案例。
其中的一个解决方案是把全网所有的参与方拉下水,大家共同参与生成随机 t(s) 和 a 的过程。用到的工具是CRS(Common Reference String,公共参考串)。
多方参与生成CRS,如图6所示:

在协议六中多方参与生成CRS的问题:参与者生成 CRS 的过程并没有强制后一个参与者(图6中的 Bob 和 Carol)都要使用前面已经公开的 CRS。如果一个攻击者是链上的最后一个参与者,他可以像链上的第一个参与者一样忽略前面的 CRS 随便构造一个有效的 CRS,这样他就变成了唯一一个知道秘密 s 和 α 的人。
为了解决这个问题,我们可以额外再要求除了第一个(可信的)以外的每一个参与者去加密然后公开他的参数。
这样我们得到了一个完整的 zk-SNARKOP 协议,如图7所示:

1、证明者(不变)
首先将 P(x) = x^3−3x^2+2x 因式分解为 P(x) =(x−0)(x−1)(x−2)。
2、证明者(不变)
随机选择因式中的一部分 t(x) = (x−1)(x−2),并 Public 出去。
3、证明者(不变)
证明者根据 P(x) = t(x) * h(x) 计算出多项式 h(x) = P(x) /t(x) = (x-0) = x。
4、全网各方( 协议二、协议三、协议六 )
(1)网络多方参与生成一个相对可信的 CRS :生成随机数 s 和 a。也称 Setup 可信设置。
(2)构建加密值 g^si、g^a、g^(a*si)。
(3)生成 Proving Key = [ g^si、g^(a*si) ],并发送给证明者。
(4)生成 Verifying Key = [ g^a、g^t(s) ],并发送给验证者。
5、证明者(协议四)
(1)通过 g^si 计算得到 g^p(s) 和 g^h(s) (证明者知道 P(x) 和 h(x) = P(x) /t(x) )。
(2)通过 g^(a*si) 计算得到 g^(a*p(s)) 。
(3)随机生成 δ ,构造随机化 Proof = [ g^( δ * p(s))、g^( δ * h(s))、g^( δ * (a*p(s))) ],然后发送给验证者(Proof 中是不包含 P(x) 的知识的)。
6、验证者(协议五)
(1)Proof = [ g^( δ * p(s))、g^( δ * h(s))、g^( δ * (a*p(s))) ] 简写为 [ g^p、g^h、g^p’ ]。
(2)验证多项式约束(验证证明者是否使用 CRS 的参数来计算 g^p(s) 和 g^h(s)):
e ( (g^p(s))^δ ,g^a ) = e ( (g^p(a*s))^δ ,g^1 ) (协议五)简写为
e ( g^p ,g^a ) = e ( g^p’ ,g^1 )
(3)验证多项式系数(验证验证者是否知道多项式的各项系数 = 知道多项式):
e ( g^(P(s) * δ) ,g^1 ) = e ( g^t(s) ,g^(h(s)*δ) ) 简写为
e ( g^p ,g^1 ) = e ( g^t(s) ,g^h )
之前我们已经完成了对多项式 P(x) = h(x) * t(x) 的证明( 即协议七 zk-SNARKOP ),如果我们能够把计算机程序转化为 P(x) ,就可以完成对程序的零知识证明。
请注意:并不是程序直接转化为多项式(程序的灵活度和复杂度太高了,很难转为通用方法)。我们要转化为零知识的内容是:程序中的某个函数( Public )的输入参数、计算中间变量以及计算后的输出结果。我们可以将下文中的 l(x) 和 r(x) 理解为输入参数,o(x) 理解为计算后的输出结果。
任何计算的本质都是由以下形式的基本运算组成的:
左操作数 运算操作符 右操作数 = 输出结果
以乘法运算操作为例,上述运算可以转换为:l(x) * r(x) = o(x) ,即 l(x) * r(x) - o(x) = 0 。
令 P(x) = l(x) * r(x) - o(x) = 0 ,这代表 P(x) 在 x = 某个值 a 的时候与 x 轴有交点。
对于函数 f(a ,b) { return a * b },当传入参数是 :3 * 2 (= 6)的时候,可以构造多项式( Enforcing Operation )表示该函数: l(x) = 3x ,r(x) = 2x , o(x) = 6x ,约束条件为 x=1 。即 l(1) = 3 ,r(1) = 2 ,o(1) = 6。
那么 P(x) = l(x) * r(x) - o(x) = 3x * 2x - 6x = 6x^2 - 6x ,用图形表示出来就是:

从图形中可以清楚的看到,当 x = 1 的时候 P(x) = l(x) * r(x) - o(x) = 6x^2 - 6x = 0 ,这意味着 (x-1) 是 P(x) = l(x) * r(x) - o(x) 分解因式后的一个因子。这就是协议七 zk-SNARKOP 中的合适的 t(x) = x - 1 。
zk-SNARKOP 协议的零知识就转化为: P(x) = h(x) * t(x) = l(x) * r(x) - o(x)。所以协议可以更新为:

上面协议的本质就是将协议七 zk-SNARKOP 中的 P(x) 用 l(x) * r(x) - o(x) 代替。
协议八也存在比较明显的局限性:
1、乘法也不会仅仅是 a * b 的简单单一运算形式。( “ 继续协议八 ” 章节将解决此问题 )
2、程序计算不可能只包含乘法。( “ 继续协议九 ” 章节将解决此问题 )
我们首先解决复杂一点的乘法计算问题,比如 a * b * c( 例如 3 * 2 * 4 = 24 )。
a * b * c 可以转化为:
a * b = r1
r1 * c = r2
我们按照协议八的思路,将上述2个运算构造成 l(x) * r(x) = o(x) 的形式。将运算构造成多项式的方法统称插值法(用给定的一组点去构造一个能经过所有这些点的弯曲多项式),构造的具体方法有很多,包括:一组未知方程、牛顿多项式、内维尔算法、拉格朗日多项式、快速傅里叶变换。
为了方便计算,这里我们直接使用 “ 一组未知方程 ” 去构造 l(x) * r(x) = o(x) 的形式:
l(1) = a ,l(2) = r1
r(1) = b ,r(2) = c
o(1) = r1 ,o(2) = r2
当 x = 1 时,l(1) * r(1) = o(1)
当 x = 2 时,l(2) * r(2) = o(2)
( 实际情况是使用 x = Random 去构造 l(x) ,r(x) ,o(x) )
对于 l(1) = a ,l(2) = r1 就相当于点 (1 ,a) 和点 (2 ,r1) 确定一条直线,所以可以得到 l(x)。
对于 r(1) = b ,r(2) = c 就相当于点 (1 ,b) 和点 (2 ,c) 确定一条直线,所以可以得到 r(x)。
对于 o(1) = r1 ,o(2) = r2 就相当于点 (1 ,r1) 和点 (2 ,r2) 确定一条直线,所以可以得到 o(x)。
我们要计算的函数是 f(a ,b ,c) { return = a * b * c } ,函数是 Public 的,我们要构建 l(x) 、r(x) 、o(x)。
将 a * b * c 拆分成如下形式:
a * b = r1
r1 * c = r2
假设参数 a = 3 ,b = 2 ,c = 4,即:
3 * 2 = 6
6 * 4 = 24
使用 x = 1 和 x = 2 ( 实际情况是使用 x = Random )构建 l(x) 、r(x) 、o(x):
l(1) = 3 ,r(1) = 2 ,o(1) = 6
l(2) = 6 ,r(2) = 4 ,o(2) = 24
l(x) 经过了点(1 ,3)和点(2 ,6)得到 l(x) = 3x
因为2点确定一条直线 y = kx + b
1 * k + b = 3
2 * k + b = 6
k = 3 , b = 0
同理可以得到 r(x) 和 o(x)。
证明者通过函数的参数构造得到 l(x) ,r(x) 和 o(x) 后,即可执行协议八进行零知识证明。
协议八的问题是:如果是证明者构建 l(x) 、r(x) 、o(x),则他可以随意构造( a 、b 、c是零知识,只有证明者自己知道 ),并且满足验证者的校验。例如证明者把 b = 2 改为 b = 1 :
a * b * c 仍然拆分成 :
a * b = r1
r1 * c = r2
3 * 1 = 3
3 * 4 = 12
l(1) = 3 , r(1) = 1 ,o(1) = 3
l(2) = 3 ,r(2) = 4 ,o(2) = 12
证明者修改 b 的取值后构建的 l(x) 、r(x) 、o(x) 仍然满足 h(x) * t(x) = l(x) * r(x) - o(x) 。
因此构建 l(x) 、r(x) 、o(x) 的工作一定要交给 Setup ,所以我们需要想办法让证明者通过 Setup 生成的 l(x) 、r(x) 、o(x) 去构建 L(x) * R(x) = O(x),然后再执行协议八。
根据多项式的一个简单性质 :y = l(x) 等价于 a * y = a * l(x)
我们对于之前的简单例子 :3 * 2 = 6 进行重新构造,即证明者构造出 :
L(x) = 3 * l(x)
R(x) = 2 * r(x)
O(x) = 6 * o(x)
即可。这样的结果是:Setup 选取随机的 x 构造 l(x) 、r(x) 、o(x) 并发送给验证者,验证者根据 Setup发来的结果,将对应的系数 3、2、6 进行分配得到 L(x) 、R(x) 、O(x) ,然后再执行协议八。
对于再复杂一点的运算,上面简单分配系数的方法就行不通了,我们需要一个更加通用的方法,例如对于下面的复杂乘法计算 :
a * b = r1
a * c = r2
d * c = r3
我们仍然构造
l(1) = 1 ,l(2) = 1 ,那 l(3) = 什么呢,显然 l(3) 未必 = d 。这种情况下我们可以使用一个通用构造:
la(1) = 1 ,la(2) = 1 ,la(3) = 0
ld(1) = 0 ,ld(2) = 0 ,ld(3) = 1
将这个计算乘法运算的较为通用的构造多项式的 la(x) 和 ld(x) 更新至协议九 :

对于加法、减法和除法,都可以转化为 l(x) * r(x) = o(x) 的形式:
加法 :a + b = r 等价于 ( a + b ) * 1 = r ,即 l(x) = a + b ,r(x) = 1 ,o(x) = r。
比如 3 + 2 = 5
l(x) = 3 la(x) + 2 ld(x) ,r(x) = 1 ,o(x) = 5。
la(1) = 1 * la(2) = 0。
ld(1) = 0 * ld(2) = 1。
L(x) = a * la(x) + d * ld(x)。
减法 :a + b = r 等价于 ( a + (-b) ) * 1 = r ,即 l(x) = a + (-b) ,r(x) = 1 ,o(x) = r。
除法 :a / b = r 等价于 a * r = b ,即 l(x) = a ,r(x) = r ,o(x) = b。
至此,我们已经将程序包含的函数中的输入参数、计算过程和输出结果成功转化成为多项式,又将多项式成功进行了零知识证明。上述内容基本涵盖了 zk-SNARK 最最核心的思想和实现路径,已经满足了本文撰写的预期。
最终的 zk-SNARK 协议在此基础上对协议执行进行进一步的约束,然后对协议进行了优化,由于计算过程较为繁琐并难以表达,故在此不再展开讨论,仅展示如下 :

作为区块链技术中应用最广泛的 zk-SNARKs,已经发展出了诸多各具特点的协议算法 :
1、Groth16
Groth16 是目前最快、数据量最小的 zk-SNARK,最早被用于 Zcash ,也是区块链中的第一个 zk-SNARK 用例。Groth16 的 CRS 不是通用的,其设置需要绑定到一个特定的电路。
论文 :https://eprint.iacr.org/2016/260
2、Sonic
Sonic 是一种早期的通用 zk-SNARK 协议,支持通用、可升级的 CRS 。Sonic 的证明大小固定,但是验证成本高,理论上可以将多个证明分批验证以获得更好的性能。
论文 :https://eprint.iacr.org/2019/099
3、Fractal
Fractal 是一种允许递归的 zk-SNARK。通过对电路的预处理实现了透明设置。证明最大 250KB,这比其他构建生成的证明都要大的多。
论文 :https://eprint.iacr.org/2019/1076
4、Halo
Halo 支持递归证据组织,无需可信设置,与其他新的 zk-SNARK 构建不同,Halo 的验证时间是线性的。
论文 :https://eprint.iacr.org/2019/1021
5、SuperSonic
Sonic 的改进版,是第一个在验证时间和证明数据量方面实用化的透明 zk-SNARK。
论文 :https://eprint.iacr.org/2019/1229
6、Marlin
Sonic 的改进版,证明时间缩短 10 倍,验证时间缩短 4 倍。
论文 :https://eprint.iacr.org/2019/1047
7、Plonk
Sonic 的改进版,证明时间缩短 5 倍。
论文 :https://eprint.iacr.org/2019/953
密码学是区块链技术的基石,而其中的零知识证明可以看作是密码学中璀璨的明珠。密码学是一门可以追溯到 2000 多年前的古老学问,其发展历史主要可以划分为以下 3 个阶段 :
1、古典密码学
16 世纪由法国人发明的 “ 维吉尼亚密码 ” 是古典密码理论发展上的一个重要里程碑,它使用一个词组作为密钥,词组中每一个字母都将确定一个替换表。这一时期的密码学主要被应用于军事领域,如何有效的传递密文,又能防止被敌人截获后破解。
2、近代密码学
源于香农在 20 世纪 40 年代末发表的一系列论文,特别是 1949 年的 “ Communication Theory of Secrecy Systems (保密系统通信理论)”,把已有数千年历史的密码学推向了基于信息论的科学轨道,密码学终于从艺术转向科学。这一时期的重要突破是 DES 的出现,并在金融等商业领域得到了广泛的应用。
3、现代密码学
1977 年麻省理工学院的 Ron Rivest、Adi Shamir、Leonard Adleman 提出的非对称加密算法 RSA,有效的解决了密钥传送的问题,标志着密码学进入了百家争鸣的现代阶段。
(0)零知识证明
1989 年麻省理工学院研究人员 Goldwasser、Micali 及 Rackoff 提出了 “ 零知识证明 ” 的概念。实现零知识证明的协议有很多,其中比较有名的包括 :zk-SNARK 、zk-STARK 、Bulletproofs 、zkBoo 、zkCNN 等。
(1)zk-SNARK
2012 年加州大学伯克利分校的 Alessandro Chiesa 教授与人合著了一篇论文,为他们第一次构造的零知识证明创造了术语 zk-SNARK。zk-SNARK 依赖于椭圆曲线来保证安全。
(2)Bulletproofs
2017 年 Benedikt Bünz 、Jonathan Bootle 、Dan Boneh 、Andrew Poelstra 、Pieter Wuille 、Greg Maxwell 撰写了第一篇描述 Bulletproofs 的论文。Bulletproofs 依赖于离散对数假设并且不需要可信设置。
(3)zk-STARK
2018 年 Eli Ben-Sasson、Iddo Bentov、Yinon Horeshy 和 Michael Riabzev 撰写了第一篇描述 zk-STARK 的论文。STARKs 的基础技术依赖于散列函数。依赖散列函数提供了一些好处,例如具有量子抵抗性、不需要任何可信设置。
1、if(DAO) 对目前主流的零知识证明协议进行了对比:

(1)O = 时间复杂度
时间复杂度并不是表示一个程序解决问题需要花多少时间,而是当问题规模扩大后,程序需要的时间长度增长得有多快。也就是说对于高速处理数据的计算机来说,处理某一个特定数据的效率不能衡量一个程序的好坏,而应该看当这个数据的规模变大到数百倍后,程序运行时间是否还是一样,或者也跟着慢了数百倍,或者变慢了数万倍。不管数据有多大,程序处理花的时间始终是一定的,我们就说这个程序具有 O(1) 的时间复杂度,也称常数级复杂度。
(2)C = 多项式的运算过程 = 电路的门数( Gate ),即下图的 + - * / 二维运算的总数:

比如 SHA256 ( m ) = h,我们表达成电路 C 的形式 :C ( h ,m ) = 0。计算机要完成一次SHA256 哈希运算需要约 20 K Gates。也就是说,计算过程越复杂,电路越复杂,涉及的门越多,在同等计算资源的情况下零知识证明的过程越慢。以著名以太坊二层项目 zkSync 为例,在顶配服务器的情况下刷一次电路的时间是 10 分钟左右。( Poseidon Hash Gates 只有几百个 )。
2、其他机构的零知识证明对比:引用 Matter Labs(https://github.com/matter-labs/awesome-zero-knowledge-proofs) 和 Beanstalk(https://docs.google.com/presentation/d/1gfB6WZMvM9mmDKofFibIgsyYShdf0RV_Y8TLz3k1Ls0/edit#slide=id.g443ebc39b4_0_110) 的研究报告简单对比一下上述 3 种最普遍的零知识证明协议:


时至今日,零知识证明已经在区块链领域大放异彩 :
技术应用方面,第一个实现 zk-SNARK 的匿名加密货币 Zcash (ZEC)、Layer2 的主要解决方案 zk Rollup 、Mina Protocol 的智能合约 Snapp 等等。
资本市场方面,排名前20位的加密基金中有超过 60%的基金都长期持仓隐私加密资产(数据来源:Messari 2021 年度报告)。
在可预见的将来,零知识证明一定会迎来技术成熟期,并得到爆发式的应用和增长 !
https://arxiv.org/pdf/1906.07221.pdf (maksym@petkus.info)
https://secbit.io/blog/ (安比实验室)
================================
================================
欢迎大佬们的探讨指正~
您可以在这里找到作者 twitter:@ethan_ifdao
No activity yet